HTML5 Canvas Set Fill Tutorial
To fill a shape with Konva, we can set the fill property when we instantiate a shape, or we can use the fill() method.
Konva supports colors, patterns, linear gradients, and radial gradients.
Instructions: Mouseover each pentagon to change its fill. You can also drag and drop the shapes.
- Vanilla
- React
- Vue
import Konva from 'konva';
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for (var src in sources) {
numImages++;
}
for (var src in sources) {
images[src] = new Image();
images[src].onload = function () {
if (++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
function draw(images) {
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
var colorPentagon = new Konva.RegularPolygon({
x: 80,
y: stage.height() / 2,
sides: 5,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
var patternPentagon = new Konva.RegularPolygon({
x: 220,
y: stage.height() / 2,
sides: 5,
radius: 70,
fillPatternImage: images.darthVader,
fillPatternOffset: { x: -220, y: 70 },
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
var linearGradPentagon = new Konva.RegularPolygon({
x: 360,
y: stage.height() / 2,
sides: 5,
radius: 70,
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
var radialGradPentagon = new Konva.RegularPolygon({
x: 500,
y: stage.height() / 2,
sides: 5,
radius: 70,
fillRadialGradientStartPoint: { x: 0, y: 0 },
fillRadialGradientStartRadius: 0,
fillRadialGradientEndPoint: { x: 0, y: 0 },
fillRadialGradientEndRadius: 70,
fillRadialGradientColorStops: [0, 'red', 0.5, 'yellow', 1, 'blue'],
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
/*
* bind listeners
*/
colorPentagon.on('mouseover touchstart', function () {
this.fill('blue');
});
colorPentagon.on('mouseout touchend', function () {
this.fill('red');
});
patternPentagon.on('mouseover touchstart', function () {
this.fillPatternImage(images.yoda);
this.fillPatternOffset({ x: -100, y: 70 });
});
patternPentagon.on('mouseout touchend', function () {
this.fillPatternImage(images.darthVader);
this.fillPatternOffset({ x: -220, y: 70 });
});
linearGradPentagon.on('mouseover touchstart', function () {
this.fillLinearGradientStartPoint({ x: -50 });
this.fillLinearGradientEndPoint({ x: 50 });
this.fillLinearGradientColorStops([0, 'green', 1, 'yellow']);
});
linearGradPentagon.on('mouseout touchend', function () {
// set multiple properties at once with setAttrs
this.setAttrs({
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
});
});
radialGradPentagon.on('mouseover touchstart', function () {
this.fillRadialGradientColorStops([
0,
'red',
0.5,
'yellow',
1,
'green',
]);
});
radialGradPentagon.on('mouseout touchend', function () {
// set multiple properties at once with setAttrs
this.setAttrs({
fillRadialGradientStartPoint: 0,
fillRadialGradientStartRadius: 0,
fillRadialGradientEndPoint: 0,
fillRadialGradientEndRadius: 70,
fillRadialGradientColorStops: [0, 'red', 0.5, 'yellow', 1, 'blue'],
});
});
layer.add(colorPentagon);
layer.add(patternPentagon);
layer.add(linearGradPentagon);
layer.add(radialGradPentagon);
stage.add(layer);
}
var sources = {
darthVader: 'https://konvajs.org/assets/darth-vader.jpg',
yoda: 'https://konvajs.org/assets/yoda.jpg',
};
loadImages(sources, function (images) {
draw(images);
});
import React from 'react';
import { Stage, Layer, RegularPolygon } from 'react-konva';
import useImage from 'use-image';
const commonProps = {
sides: 5,
radius: 70,
stroke: 'black',
strokeWidth: 4,
draggable: true,
};
const ColorPolygon = () => {
const [fill, setFill] = React.useState('red');
const [position, setPosition] = React.useState({
x: 80,
y: window.innerHeight / 2,
});
const handleEnter = (e) => {
setFill('blue');
e.target.getStage().container().style.cursor = 'pointer';
};
const handleLeave = (e) => {
setFill('red');
e.target.getStage().container().style.cursor = 'default';
};
return (
<RegularPolygon
{...commonProps}
x={position.x}
y={position.y}
fill={fill}
onMouseEnter={handleEnter}
onTouchStart={handleEnter}
onMouseLeave={handleLeave}
onTouchEnd={handleLeave}
onDragMove={(e) => {
setPosition({ x: e.target.x(), y: e.target.y() });
}}
/>
);
};
const PatternPolygon = () => {
const [darthVader] = useImage('https://konvajs.org/assets/darth-vader.jpg');
const [yoda] = useImage('https://konvajs.org/assets/yoda.jpg');
const [image, setImage] = React.useState(null);
const [offset, setOffset] = React.useState({ x: -220, y: 70 });
const [position, setPosition] = React.useState({
x: 220,
y: window.innerHeight / 2,
});
React.useEffect(() => {
if (darthVader) {
setImage(darthVader);
}
}, [darthVader]);
const handleEnter = (e) => {
setImage(yoda);
setOffset({ x: -100, y: 70 });
e.target.getStage().container().style.cursor = 'pointer';
};
const handleLeave = (e) => {
setImage(darthVader);
setOffset({ x: -220, y: 70 });
e.target.getStage().container().style.cursor = 'default';
};
return (
<RegularPolygon
{...commonProps}
x={position.x}
y={position.y}
fillPatternImage={image}
fillPatternOffset={offset}
onMouseEnter={handleEnter}
onTouchStart={handleEnter}
onMouseLeave={handleLeave}
onTouchEnd={handleLeave}
onDragMove={(e) => {
setPosition({ x: e.target.x(), y: e.target.y() });
}}
/>
);
};
const LinearGradientPolygon = () => {
const [colorStops, setColorStops] = React.useState([0, 'red', 1, 'yellow']);
const [position, setPosition] = React.useState({
x: 360,
y: window.innerHeight / 2,
});
const handleEnter = (e) => {
setColorStops([0, 'green', 1, 'yellow']);
e.target.getStage().container().style.cursor = 'pointer';
};
const handleLeave = (e) => {
setColorStops([0, 'red', 1, 'yellow']);
e.target.getStage().container().style.cursor = 'default';
};
return (
<RegularPolygon
{...commonProps}
x={position.x}
y={position.y}
fillLinearGradientStartPoint={{ x: -50, y: -50 }}
fillLinearGradientEndPoint={{ x: 50, y: 50 }}
fillLinearGradientColorStops={colorStops}
onMouseEnter={handleEnter}
onTouchStart={handleEnter}
onMouseLeave={handleLeave}
onTouchEnd={handleLeave}
onDragMove={(e) => {
setPosition({ x: e.target.x(), y: e.target.y() });
}}
/>
);
};
const RadialGradientPolygon = () => {
const [colorStops, setColorStops] = React.useState([0, 'red', 0.5, 'yellow', 1, 'blue']);
const [position, setPosition] = React.useState({
x: 500,
y: window.innerHeight / 2,
});
const handleEnter = (e) => {
setColorStops([0, 'red', 0.5, 'yellow', 1, 'green']);
e.target.getStage().container().style.cursor = 'pointer';
};
const handleLeave = (e) => {
setColorStops([0, 'red', 0.5, 'yellow', 1, 'blue']);
e.target.getStage().container().style.cursor = 'default';
};
return (
<RegularPolygon
{...commonProps}
x={position.x}
y={position.y}
fillRadialGradientStartPoint={{ x: 0, y: 0 }}
fillRadialGradientStartRadius={0}
fillRadialGradientEndPoint={{ x: 0, y: 0 }}
fillRadialGradientEndRadius={70}
fillRadialGradientColorStops={colorStops}
onMouseEnter={handleEnter}
onTouchStart={handleEnter}
onMouseLeave={handleLeave}
onTouchEnd={handleLeave}
onDragMove={(e) => {
setPosition({ x: e.target.x(), y: e.target.y() });
}}
/>
);
};
const App = () => {
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<ColorPolygon />
<PatternPolygon />
<LinearGradientPolygon />
<RadialGradientPolygon />
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize">
<v-layer>
<v-regular-polygon
:config="{
x: 80,
y: stageSize.height / 2,
sides: 5,
radius: 70,
fill: colorFill,
stroke: 'black',
strokeWidth: 4,
draggable: true,
}"
@mouseenter="onColorEnter"
@touchstart="onColorEnter"
@mouseleave="onColorLeave"
@touchend="onColorLeave"
/>
<v-regular-polygon
:config="{
x: 220,
y: stageSize.height / 2,
sides: 5,
radius: 70,
fillPatternImage: patternImage,
fillPatternOffset: patternOffset,
stroke: 'black',
strokeWidth: 4,
draggable: true,
}"
@mouseenter="onPatternEnter"
@touchstart="onPatternEnter"
@mouseleave="onPatternLeave"
@touchend="onPatternLeave"
/>
<v-regular-polygon
:config="{
x: 360,
y: stageSize.height / 2,
sides: 5,
radius: 70,
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: linearGradientStops,
stroke: 'black',
strokeWidth: 4,
draggable: true,
}"
@mouseenter="onLinearGradientEnter"
@touchstart="onLinearGradientEnter"
@mouseleave="onLinearGradientLeave"
@touchend="onLinearGradientLeave"
/>
<v-regular-polygon
:config="{
x: 500,
y: stageSize.height / 2,
sides: 5,
radius: 70,
fillRadialGradientStartPoint: { x: 0, y: 0 },
fillRadialGradientStartRadius: 0,
fillRadialGradientEndPoint: { x: 0, y: 0 },
fillRadialGradientEndRadius: 70,
fillRadialGradientColorStops: radialGradientStops,
stroke: 'black',
strokeWidth: 4,
draggable: true,
}"
@mouseenter="onRadialGradientEnter"
@touchstart="onRadialGradientEnter"
@mouseleave="onRadialGradientLeave"
@touchend="onRadialGradientLeave"
/>
</v-layer>
</v-stage>
</template>
<script>
export default {
data() {
return {
stageSize: {
width: window.innerWidth,
height: window.innerHeight,
},
colorFill: 'red',
patternImage: null,
patternOffset: { x: -220, y: 70 },
linearGradientStops: [0, 'red', 1, 'yellow'],
radialGradientStops: [0, 'red', 0.5, 'yellow', 1, 'blue'],
images: {},
};
},
mounted() {
this.loadImages();
},
methods: {
loadImages() {
const sources = {
darthVader: 'https://konvajs.org/assets/darth-vader.jpg',
yoda: 'https://konvajs.org/assets/yoda.jpg',
};
Object.keys(sources).forEach((name) => {
const image = new Image();
image.onload = () => {
this.images[name] = image;
if (name === 'darthVader') {
this.patternImage = image;
}
};
image.src = sources[name];
});
},
onColorEnter(e) {
this.colorFill = 'blue';
e.target.getStage().container().style.cursor = 'pointer';
},
onColorLeave(e) {
this.colorFill = 'red';
e.target.getStage().container().style.cursor = 'default';
},
onPatternEnter(e) {
this.patternImage = this.images.yoda;
this.patternOffset = { x: -100, y: 70 };
e.target.getStage().container().style.cursor = 'pointer';
},
onPatternLeave(e) {
this.patternImage = this.images.darthVader;
this.patternOffset = { x: -220, y: 70 };
e.target.getStage().container().style.cursor = 'default';
},
onLinearGradientEnter(e) {
this.linearGradientStops = [0, 'green', 1, 'yellow'];
e.target.getStage().container().style.cursor = 'pointer';
},
onLinearGradientLeave(e) {
this.linearGradientStops = [0, 'red', 1, 'yellow'];
e.target.getStage().container().style.cursor = 'default';
},
onRadialGradientEnter(e) {
this.radialGradientStops = [0, 'red', 0.5, 'yellow', 1, 'green'];
e.target.getStage().container().style.cursor = 'pointer';
},
onRadialGradientLeave(e) {
this.radialGradientStops = [0, 'red', 0.5, 'yellow', 1, 'blue'];
e.target.getStage().container().style.cursor = 'default';
},
},
};
</script>