width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const rect1 = new Konva.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4
});
layer.add(rect1);
const rect2 = new Konva.Rect({
x: 150,
y: 40,
width: 100,
height: 50,
fill: 'red',
shadowBlur: 10,
cornerRadius: 10
});
layer.add(rect2);
const rect3 = new Konva.Rect({
x: 50,
y: 120,
width: 100,
height: 100,
fill: 'blue',
cornerRadius: [0, 10, 20, 30]
});
layer.add(rect3);
stage.add(layer);
```
```js
import { Stage, Layer, Rect } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
**Next steps:**
- [Add events to shapes →](/docs/events/Binding_Events.html)
- [Make shapes draggable →](/docs/drag_and_drop/Drag_and_Drop.html)
- [Add resize and rotate handles →](/docs/select_and_transform/Basic_demo.html)
- [Animate shape properties →](/docs/animations/Create_an_Animation.html)
- [Konva.Rect API Reference →](/api/Konva.Rect.html)
---
# HTML5 Canvas Shape Events
> Learn how to bind event listeners to HTML5 Canvas shapes with Konva.js. Handle click, dblclick, mouseover, mouseout, mousemove, and more.
Source: https://konvajs.org/docs/events/Binding_Events.html
To detect shape events with Konva, we can use the `on()` method to bind event handlers to a node.
The `on()` method requires an event type and a function to be executed when the event occurs.
Mouse events: `mouseover`, `mouseout`, `mouseenter`, `mouseleave`, `mousemove`, `mousedown`, `mouseup`, `wheel`, `click`, `dblclick`.
Touch events: `touchstart`, `touchmove`, `touchend`, `tap`, `dbltap`.
Pointer events: `pointerdown`, `pointermove`, `pointereup`, `pointercancel`, `pointerover`, `pointerenter`, `pointerout`,`pointerleave`, `pointerclick`, `pointerdblclick`.
Drag events: `dragstart`, `dragmove`, and `dragend`.
Transform events: `transformstart`, `transform`, `transformend`.
**Instructions: Mouseover and mouseout of the triangle, and mouseover, mouseout, mousedown, and mouseup over the circle.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
const triangle = new Konva.RegularPolygon({
x: 80,
y: 120,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
});
const circle = new Konva.Circle({
x: 230,
y: 100,
radius: 60,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
function writeMessage(message) {
text.text(message);
}
triangle.on('mouseout', () => {
writeMessage('Mouseout triangle');
});
triangle.on('mousemove', () => {
const mousePos = stage.getPointerPosition();
writeMessage('x: ' + mousePos.x + ', y: ' + mousePos.y);
});
circle.on('mouseover', () => {
writeMessage('Mouseover circle');
});
circle.on('mouseout', () => {
writeMessage('Mouseout circle');
});
circle.on('mousedown', () => {
writeMessage('Mousedown circle');
});
circle.on('mouseup', () => {
writeMessage('Mouseup circle');
});
layer.add(triangle);
layer.add(circle);
layer.add(text);
stage.add(layer);
````
```jsx
import { Stage, Layer, RegularPolygon, Circle, Text } from 'react-konva';
import { useRef, useState } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const stageRef = useRef();
const writeMessage = (text) => {
setMessage(text);
};
return (
writeMessage('Mouseout triangle')}
onMousemove={() => {
const mousePos = stageRef.current.getPointerPosition();
writeMessage('x: ' + mousePos.x + ', y: ' + mousePos.y);
}}
/>
writeMessage('Mouseover circle')}
onMouseout={() => writeMessage('Mouseout circle')}
onMousedown={() => writeMessage('Mousedown circle')}
onMouseup={() => writeMessage('Mouseup circle')}
/>
);
};
export default App;
````
```html
```
---
# HTML5 Canvas Drag and Drop Tutorial
> Learn how to add drag and drop to HTML5 Canvas shapes with Konva.js. Make any shape draggable with a single property and handle drag events.
Source: https://konvajs.org/docs/drag_and_drop/Drag_and_Drop.html
To drag and drop shapes with Konva, we can set the `draggable` property
to true when we instantiate a shape, or we can use the `draggable()` method.
The `draggable()` method enables drag and drop for both desktop and mobile
applications automatically.
To detect drag and drop events with Konva, we can use the `on()` method to
bind `dragstart`, `dragmove`, or `dragend` events to a node.
The `on()` method requires an event type and a function to be executed when the event occurs.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
// add cursor styling
circle.on('mouseover', function () {
document.body.style.cursor = 'pointer';
});
circle.on('mouseout', function () {
document.body.style.cursor = 'default';
});
layer.add(circle);
```
```jsx
import { Stage, Layer, Circle } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [position, setPosition] = useState({ x: window.innerWidth / 2, y: window.innerHeight / 2 });
return (
{
document.body.style.cursor = 'pointer';
}}
onMouseLeave={(e) => {
document.body.style.cursor = 'default';
}}
onDragEnd={(e) => {
setPosition({
x: e.target.x(),
y: e.target.y()
});
}}
/>
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Konva Animation Tutorial
> Learn how to create animations on HTML5 Canvas with Konva.js. Use Konva.Animation for frame-based animations and Konva.Tween for property transitions.
Source: https://konvajs.org/docs/animations/Create_an_Animation.html
To create custom animations with Konva, we can use the `Konva.Animation`
constructor which takes two arguments, the required update function and
an optional layer, or array of layers, that will be updated with each animation frame.
The animation function is passed a `frame` object which contains a `time` property which is the number
of milliseconds that the animation has been running, a `timeDiff` property which
is the number of milliseconds that have passed since the last frame,
and a `frameRate` property which is the current frame rate in frames per second.
The update function should never redraw the stage or a layer because the animation
engine will intelligently handle that for us.
The update function should only contain logic that updates Node properties,
such as `position`, `rotation`, `scale`, `width`, `height`, `radius`, `colors`, etc.
Once the animation has been created, we can start it at anytime with the `start()` method.
For a full list of attributes and methods, check out the [Konva.Animation documentation](/api/Konva.Animation.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 50,
height: 50,
fill: 'green',
});
layer.add(rect);
const anim = new Konva.Animation(function(frame) {
const time = frame.time;
const timeDiff = frame.timeDiff;
const frameRate = frame.frameRate;
// Example: move rectangle in a circle
const radius = 50;
const x = radius * Math.cos(frame.time * 2 * Math.PI / 2000) + 100;
const y = radius * Math.sin(frame.time * 2 * Math.PI / 2000) + 100;
rect.position({ x, y });
}, layer);
anim.start();
````
```js
import { Stage, Layer, Rect } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const rectRef = useRef(null);
useEffect(() => {
const anim = new Konva.Animation((frame) => {
const time = frame.time;
const timeDiff = frame.timeDiff;
const frameRate = frame.frameRate;
// Example: move rectangle in a circle
const radius = 50;
const x = radius * Math.cos(frame.time * 2 * Math.PI / 2000) + 100;
const y = radius * Math.sin(frame.time * 2 * Math.PI / 2000) + 100;
rectRef.current.position({ x, y });
}, rectRef.current.getLayer());
anim.start();
return () => {
anim.stop();
};
}, []);
return (
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Blur Image Filter Tutorial
> Learn how to apply a blur filter to images on HTML5 Canvas using Konva.js with adjustable blurRadius property.
Source: https://konvajs.org/docs/filters/Blur.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To blur an image with Konva, we can use the `Konva.Filters.Blur` filter
and set the blur amount with the `blurRadius` property.
**Instructions**: Slide the control to adjust the blur radius.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Blur]);
image.blurRadius(10);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '0';
slider.max = '40';
slider.value = image.blurRadius();
slider.style.position = 'absolute';
slider.style.top = '20px';
slider.style.left = '20px';
slider.addEventListener('input', (e) => {
const value = parseInt(e.target.value);
image.blurRadius(value);
});
document.body.appendChild(slider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [blurRadius, setBlurRadius] = useState(10);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Blur]}
blurRadius={blurRadius}
/>
setBlurRadius(parseInt(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas All Konva performance tips list
> All Konva.js performance optimization tips: layer management, shape caching, listening false, batch draw, and more. Render thousands of shapes efficiently on HTML5 Canvas.
Source: https://konvajs.org/docs/performance/All_Performance_Tips.html
Don't want to spend your time with performance issues? Request a [performance review](https://lavrton.com/consulting/).
### Why this is important
The HTML5 canvas is efficient at what it does and internally `Konva` has many features that aim to provide great performance. However, when your project starts to grow in complexity, or when you just have a lot of shapes on the stage, there must inevitably be some negative performance impact.
### Optimization targets
The optimizations here focus on two general rules:
* Compute as little as possible: all computation takes time to complete. Each individual computation may run in a tiny fraction of a second, but the thousands or millions of computations caused by your code, Konva, JavaScript, and the layers below that, will add up to something more observable by the human eye if that super-slick animation or effect is, in fact, jerky.
* **Draw as little as possible**: this is important because all drawing has a performance cost. There are two categories of cost - firstly the computation of the drawing which we covered in the point above, and then the movement of the drawing from memory to the screen. Depending on the case, there may also be intermediate off-screen compositing or per-pixel processing. The rule is therefore do as little drawing as possible.
### The Stage
1. Optimise stage size - following the rule of 'draw as little as possible', try to avoid creating a large stage because moving all those bytes from memory to screen is going to have a negative impact. There are some tips [here](/docs/sandbox/Canvas_Scrolling.html) that offer alternative approaches to the mega-stage problem!
2. Set a viewport on mobile - Scaling images is a significant performance hit, so for mobile applications set viewport: `
` which will avoid unnecessary scaling of your Konva output.
3. Use `Konva.pixelRatio = 1` on retina devices - Konva automatically handles pixel ratio adjustments in order to render crisp drawings on all devices. But, just in case you have bad performance on retina devices, set `Konva.pixelRatio = 1` to reduce the scaling work Konva has to do. This setting might affect the output in some cases, so make sure that quality of the result is ok for you.
### Layers
1. [Layer Management](/docs/performance/Layer_Management.html) - under the hood, each Konva layer is a separate HTML5 canvas element which gives some useful capabilities, including the ability to refresh only a layer that changed and so avoid the performance cost of refreshing the entire stage. But with great power great responsibility comes, and each layer has an incremental performance overhead so we should keep the number of layers to a minimum.
2. Use `layer.listening(false)` - Konva gives us mouse and touch event listeners on all the shapes we draw. But there is a performance cost for each one, and for a layer with many shapes Konva has to expend many cycles checking which listeners might be triggered. If you have a layer on which none of the shapes need to react to events, take this burden away by setting `layer.listening(false)`. See [Demo](/docs/sandbox/Animation_Stress_Test.html). There is a similar point in the shapes section.
3. Optimise dragging costs - while you drag a shape across a layer that layer must be redrawn per cycle of the move event listener. To avoid this performance cost, move the shape to a dedicated layer while dragging, then move it back to original layer at drag end. See [Demo](/docs/sandbox/Drag_and_Drop_Stress_Test.html)
### Shapes
1. [Shape Caching](/docs/performance/Shape_Caching.html) - internally Konva makes an image of your shape and uses that when the shape has to be drawn. Drawing images avoids the overhead of composing the shape from its drawing instructions, and can increase performance impressively for complex shapes and groups.
2. Keep the shapes tidy - each shape in your stage has a cost just to exist. To optimise performance, hide or remove from the layer any objects that become invisible / opacity = 0, or objects that go out of view.
3. Use `shape.listening(false)` - as with layers (see point 7 above), Konva looks out for when events should be triggered for shapes, which has a performance cost. Telling a shape to stop listening for events reduces this cost, as explained at [Listening false](/docs/performance/Listening_False.html).
4. Switch off perfect drawing - In some cases the result of drawing with the HTML5 canvas is not what you might have expected - see the demo for an example [Disable Perfect Drawing](/docs/performance/Disable_Perfect_Draw.html). Konva does extra work via its perfect drawing feature to put that right, but this comes with a performance cost. By setting `shape.perfectDrawEnabled(false)` this cost can be avoided, with no reduction in output quality, when a shape has fill, stroke and opacity.
5. [Optimize Stroke Drawing](/docs/performance/Optimize_Strokes.html) - To achieve drawing results that look as expected, Konva makes an extra internal drawing when a shape has both stroke and shadow. Avoid this performance burden by switching off the shadow that Konva adds for the stroke.
### Animations
1. [Optimize Animation](/docs/performance/Optimize_Animation.html) - Avoiding unnecessary redraw costs for animation steps that fall between visual changes.
### Memory
1. [Avoid Memory Leaks](/docs/performance/Avoid_Memory_Leaks.html) - Konva looks after a lot of cases where you might make memory leaks, but bringing shapes and tweens into the world and managing their exit is an area where you can help.
2. **Know what a layer costs.** Every layer allocates two canvases: a scene canvas at the device pixel ratio, and a hit canvas always at ratio 1. On a 1920 × 1080 stage on a retina screen that is roughly 33 MB plus 8 MB, so about 41 MB per layer before you draw anything. This is why Konva warns above five layers — at that point you are near 200 MB of canvas memory on nothing but empty layers.
3. **Mobile Safari enforces a hard ceiling.** Past it you get `Total canvas memory use exceeds the maximum limit`, reported as 256 MB on some devices and 384 MB on others, and the canvas goes blank rather than degrading. The levers, in order of effect: fewer layers, a smaller stage, and `Konva.pixelRatio = 1` — which quarters the scene canvas on a 2× screen. Caches count towards the same budget, so release them with `node.clearCache()` when a node is no longer visible.
### Very large scenes
1. **Cull what is off screen.** Konva draws every node on a layer whether or not it lands inside the stage. For a scene much larger than the viewport, hide the nodes outside it — `visible(false)` skips both drawing and hit testing, and is far cheaper than removing and re-adding nodes.
```js
function cull() {
const view = {
x: -stage.x() / stage.scaleX(),
y: -stage.y() / stage.scaleY(),
width: stage.width() / stage.scaleX(),
height: stage.height() / stage.scaleY(),
};
layer.children.forEach((node) => {
node.visible(Konva.Util.haveIntersection(view, node.getClientRect()));
});
}
```
Call it when the view changes — after a pan or a zoom — not on every frame. `getClientRect()` is not free, so for tens of thousands of nodes keep your own index of positions and test against that instead of asking each node.
2. **Skip hit detection during a drag.** While a node is being dragged Konva does not run hit detection, which keeps dragging cheap. If you need to know what is under the pointer mid-drag — highlighting a drop target, for example — turn it back on and accept the cost:
```js
Konva.hitOnDragEnabled = true; // default is false
```
3. **Reach for one shape instead of many.** A thousand nodes each with their own attributes, transform and hit region cost far more than one custom shape that draws a thousand things in a single `sceneFunc`. You lose per-item events and dragging, so this is a trade, not a free win — see [Custom Shape](/docs/shapes/Custom.html).
Below is a demo showing some of these performance tips in action:
```js
import Konva from 'konva';
// Create stage with good performance settings
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
// Create layers with performance optimizations
const backgroundLayer = new Konva.Layer({ listening: false });
const mainLayer = new Konva.Layer();
const dragLayer = new Konva.Layer();
stage.add(backgroundLayer);
stage.add(mainLayer);
stage.add(dragLayer);
// Create a shape with caching
const star = new Konva.Star({
x: 200,
y: 200,
numPoints: 6,
innerRadius: 40,
outerRadius: 70,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
draggable: true,
perfectDrawEnabled: false, // performance optimization
});
// Cache the shape for better performance
star.cache();
// Optimize dragging performance
star.on('dragstart', () => {
star.moveTo(dragLayer);
});
star.on('dragend', () => {
star.moveTo(mainLayer);
});
// Create background with listening disabled
const rect = new Konva.Rect({
x: 0,
y: 0,
width: stage.width(),
height: stage.height(),
fill: 'lightgray',
listening: false,
});
backgroundLayer.add(rect);
mainLayer.add(star);
```
```js
import { Stage, Layer, Star, Rect } from 'react-konva';
import { Portal } from 'react-konva-utils';
import { useState, useRef, useEffect } from 'react';
const App = () => {
const [isDragging, setIsDragging] = useState(false);
const [position, setPosition] = useState({ x: 200, y: 200 });
const starRef = useRef(null);
useEffect(() => {
// Cache the shape for better performance
if (starRef.current) {
starRef.current.cache();
}
}, []);
const handleDragStart = () => {
setIsDragging(true);
};
const handleDragEnd = (event) => {
setPosition(event.target.position());
setIsDragging(false);
};
return (
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Stage Serialization Tutorial
> Learn how to serialize and save HTML5 Canvas state as JSON with Konva.js. Use stage.toJSON() to export and Konva.Node.create() to restore canvas content.
Source: https://konvajs.org/docs/data_and_serialization/Serialize_a_Stage.html
To serialize a stage with Konva, we can use the `toJSON()` method.
The `toJSON()` method will return a JSON string that contains all of the node's attributes.
Note that event handlers and images are not serializable.
```js
import Konva from 'konva';
// Create wrapper with relative positioning
const stage = new Konva.Stage({
container: 'container',
width: 400,
height: 400
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 3
});
layer.add(circle);
// Add button on top of stage
const button = document.createElement('button');
button.textContent = 'Serialize Stage';
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
document.body.appendChild(button);
button.addEventListener('click', () => {
const json = stage.toJSON();
console.log(json);
alert('Stage serialized! Check the console for the JSON string.');
});
```
**Note:** While directly serializing the stage works in React, it's generally considered an anti-pattern. In React applications, you should manage your application state separately and serialize that state instead of the stage.
```js
import { Stage, Layer, Circle } from 'react-konva';
import { useRef, useState } from 'react';
const App = () => {
const stageRef = useRef(null);
const [circle, setCircle] = useState({
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 3
});
const handleSerialize = () => {
// In a real app, prefer saving app state, not stage JSON
const json = JSON.stringify({ shapes: [circle] });
console.log('Serialized state:', json);
alert('State serialized! Check the console for the JSON string.');
};
return (
Serialize
{
setCircle({
...circle,
x: e.target.x(),
y: e.target.y()
});
}}
/>
);
};
export default App;
```
**Note:** While directly serializing the stage works in Vue, it's generally considered an anti-pattern. In Vue applications, you should manage your application state with reactive data and serialize that state instead of the stage.
```js
Serialize
```
---
# HTML5 Canvas Shape select, resize and rotate
> Learn how to select, resize, and rotate shapes on HTML5 Canvas with Konva.js Transformer. Add interactive handles for resizing and rotating any shape.
Source: https://konvajs.org/docs/select_and_transform/Basic_demo.html
`Transformer` is a special kind of `Konva.Group`. It allows you easily resize and rotate any node or set of nodes.
To enable it you need to:
1. Create new instance with `new Konva.Transformer()`
2. Add it to layer
3. attach to node with `transformer.nodes([shape]);`
_Note:_ Transforming tool is not changing `width` and `height` properties of nodes when you resize them. Instead it changes `scaleX` and `scaleY` properties.
**Instructions: Try to resize and rotate shapes. Click on empty area to remove selection. Use SHIFT or CTRL to add/remove shapes into/from selection. Try to select area on a canvas.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
// create rectangle
const rect1 = new Konva.Rect({
x: 60,
y: 60,
width: 100,
height: 90,
fill: 'red',
name: 'rect',
draggable: true,
});
layer.add(rect1);
const rect2 = new Konva.Rect({
x: 250,
y: 100,
width: 150,
height: 90,
fill: 'green',
name: 'rect',
draggable: true,
});
layer.add(rect2);
// create transformer
const tr = new Konva.Transformer();
layer.add(tr);
// add a new feature, lets add ability to draw selection rectangle
let selectionRectangle = new Konva.Rect({
fill: 'rgba(0,0,255,0.5)',
visible: false,
});
layer.add(selectionRectangle);
let x1, y1, x2, y2;
stage.on('mousedown touchstart', (e) => {
// do nothing if we mousedown on any shape
if (e.target !== stage) {
return;
}
x1 = stage.getPointerPosition().x;
y1 = stage.getPointerPosition().y;
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;
selectionRectangle.setAttrs({
x: x1,
y: y1,
width: 0,
height: 0,
visible: true,
});
});
stage.on('mousemove touchmove', () => {
// do nothing if we didn't start selection
if (!selectionRectangle.visible()) {
return;
}
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;
selectionRectangle.setAttrs({
x: Math.min(x1, x2),
y: Math.min(y1, y2),
width: Math.abs(x2 - x1),
height: Math.abs(y2 - y1),
});
});
stage.on('mouseup touchend', () => {
// do nothing if we didn't start selection
if (!selectionRectangle.visible()) {
return;
}
// update visibility in timeout, so we can check it in click event
setTimeout(() => {
selectionRectangle.visible(false);
});
var shapes = stage.find('.rect');
var box = selectionRectangle.getClientRect();
var selected = shapes.filter((shape) =>
Konva.Util.haveIntersection(box, shape.getClientRect())
);
tr.nodes(selected);
});
// clicks should select/deselect shapes
stage.on('click tap', function (e) {
// if we are selecting with rect, do nothing
if (selectionRectangle.visible() && selectionRectangle.width() > 0 && selectionRectangle.height() > 0) {
return;
}
// if click on empty area - remove all selections
if (e.target === stage) {
tr.nodes([]);
return;
}
// do nothing if clicked NOT on our rectangles
if (!e.target.hasName('rect')) {
return;
}
// do we pressed shift or ctrl?
const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
const isSelected = tr.nodes().indexOf(e.target) >= 0;
if (!metaPressed && !isSelected) {
// if no key pressed and the node is not selected
// select just one
tr.nodes([e.target]);
} else if (metaPressed && isSelected) {
// if we pressed keys and node was selected
// we need to remove it from selection:
const nodes = tr.nodes().slice(); // use slice to have new copy of array
// remove node from array
nodes.splice(nodes.indexOf(e.target), 1);
tr.nodes(nodes);
} else if (metaPressed && !isSelected) {
// add the node into selection
const nodes = tr.nodes().concat([e.target]);
tr.nodes(nodes);
}
});
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
const initialRectangles = [
{
x: 60,
y: 60,
width: 100,
height: 90,
fill: 'red',
id: 'rect1',
name: 'rect',
rotation: 0,
},
{
x: 250,
y: 100,
width: 150,
height: 90,
fill: 'green',
id: 'rect2',
name: 'rect',
rotation: 0,
},
];
// Helper functions for calculating bounding boxes of rotated rectangles
const degToRad = (angle) => (angle / 180) * Math.PI;
const getCorner = (pivotX, pivotY, diffX, diffY, angle) => {
const distance = Math.sqrt(diffX * diffX + diffY * diffY);
angle += Math.atan2(diffY, diffX);
const x = pivotX + distance * Math.cos(angle);
const y = pivotY + distance * Math.sin(angle);
return { x, y };
};
const getClientRect = (element) => {
const { x, y, width, height, rotation = 0 } = element;
const rad = degToRad(rotation);
const p1 = getCorner(x, y, 0, 0, rad);
const p2 = getCorner(x, y, width, 0, rad);
const p3 = getCorner(x, y, width, height, rad);
const p4 = getCorner(x, y, 0, height, rad);
const minX = Math.min(p1.x, p2.x, p3.x, p4.x);
const minY = Math.min(p1.y, p2.y, p3.y, p4.y);
const maxX = Math.max(p1.x, p2.x, p3.x, p4.x);
const maxY = Math.max(p1.y, p2.y, p3.y, p4.y);
return {
x: minX,
y: minY,
width: maxX - minX,
height: maxY - minY,
};
};
const App = () => {
const [rectangles, setRectangles] = useState(initialRectangles);
const [selectedIds, setSelectedIds] = useState([]);
const [selectionRectangle, setSelectionRectangle] = useState({
visible: false,
x1: 0,
y1: 0,
x2: 0,
y2: 0,
});
const isSelecting = useRef(false);
const transformerRef = useRef();
const rectRefs = useRef(new Map());
// Update transformer when selection changes
useEffect(() => {
if (selectedIds.length && transformerRef.current) {
// Get the nodes from the refs Map
const nodes = selectedIds
.map(id => rectRefs.current.get(id))
.filter(node => node);
transformerRef.current.nodes(nodes);
} else if (transformerRef.current) {
// Clear selection
transformerRef.current.nodes([]);
}
}, [selectedIds]);
// Click handler for stage
const handleStageClick = (e) => {
// If we are selecting with rect, do nothing
// But allow point clicks through (when width/height are 0)
const selWidth = Math.abs(selectionRectangle.x2 - selectionRectangle.x1);
const selHeight = Math.abs(selectionRectangle.y2 - selectionRectangle.y1);
if (selectionRectangle.visible && selWidth > 0 && selHeight > 0) {
return;
}
// If click on empty area - remove all selections
if (e.target === e.target.getStage()) {
setSelectedIds([]);
return;
}
// Do nothing if clicked NOT on our rectangles
if (!e.target.hasName('rect')) {
return;
}
const clickedId = e.target.id();
// Do we pressed shift or ctrl?
const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
const isSelected = selectedIds.includes(clickedId);
if (!metaPressed && !isSelected) {
// If no key pressed and the node is not selected
// select just one
setSelectedIds([clickedId]);
} else if (metaPressed && isSelected) {
// If we pressed keys and node was selected
// we need to remove it from selection
setSelectedIds(selectedIds.filter(id => id !== clickedId));
} else if (metaPressed && !isSelected) {
// Add the node into selection
setSelectedIds([...selectedIds, clickedId]);
}
};
const handleMouseDown = (e) => {
// Do nothing if we mousedown on any shape
if (e.target !== e.target.getStage()) {
return;
}
// Start selection rectangle
isSelecting.current = true;
const pos = e.target.getStage().getPointerPosition();
setSelectionRectangle({
visible: true,
x1: pos.x,
y1: pos.y,
x2: pos.x,
y2: pos.y,
});
};
const handleMouseMove = (e) => {
// Do nothing if we didn't start selection
if (!isSelecting.current) {
return;
}
const pos = e.target.getStage().getPointerPosition();
setSelectionRectangle({
...selectionRectangle,
x2: pos.x,
y2: pos.y,
});
};
const handleMouseUp = () => {
// Do nothing if we didn't start selection
if (!isSelecting.current) {
return;
}
isSelecting.current = false;
// Update visibility in timeout, so we can check it in click event
setTimeout(() => {
setSelectionRectangle({
...selectionRectangle,
visible: false,
});
});
const selBox = {
x: Math.min(selectionRectangle.x1, selectionRectangle.x2),
y: Math.min(selectionRectangle.y1, selectionRectangle.y2),
width: Math.abs(selectionRectangle.x2 - selectionRectangle.x1),
height: Math.abs(selectionRectangle.y2 - selectionRectangle.y1),
};
// Only select shapes if selection box has actual size (not just a point click)
if (selBox.width > 0 && selBox.height > 0) {
const selected = rectangles.filter(rect => {
// Check if rectangle intersects with selection box
return Konva.Util.haveIntersection(selBox, getClientRect(rect));
});
setSelectedIds(selected.map(rect => rect.id));
}
};
const handleDragEnd = (e) => {
const id = e.target.id();
setRectangles(prevRects => {
const newRects = [...prevRects];
const index = newRects.findIndex(r => r.id === id);
if (index !== -1) {
newRects[index] = {
...newRects[index],
x: e.target.x(),
y: e.target.y()
};
}
return newRects;
});
};
const handleTransformEnd = (e) => {
// Find which rectangle(s) were transformed
const id = e.target.id();
const node = e.target;
setRectangles(prevRects => {
const newRects = [...prevRects];
// Update each transformed node
const index = newRects.findIndex(r => r.id === id);
if (index !== -1) {
const scaleX = node.scaleX();
const scaleY = node.scaleY();
// Reset scale
node.scaleX(1);
node.scaleY(1);
// Update the state with new values
newRects[index] = {
...newRects[index],
x: node.x(),
y: node.y(),
width: Math.max(5, node.width() * scaleX),
height: Math.max(5, node.height() * scaleY),
rotation: node.rotation(),
};
}
return newRects;
});
};
return (
{/* Render rectangles directly */}
{rectangles.map(rect => (
{
if (node) {
rectRefs.current.set(rect.id, node);
}
}}
onDragEnd={handleDragEnd}
onTransformEnd={handleTransformEnd}
/>
))}
{/* Single transformer for all selected shapes */}
{
// Limit resize
if (newBox.width < 5 || newBox.height < 5) {
return oldBox;
}
return newBox;
}}
/>
{/* Selection rectangle */}
{selectionRectangle.visible && (
)}
);
};
export default App;
```
```js
handleDragEnd(e, i)"
@transformend="(e) => handleTransformEnd(e, i)"
ref="rectRefs"
/>
```
## What Transformer does not do
`Transformer` draws the handles and applies the scale. Snapping to other
objects, alignment guides, a shared bounding box for a multi-selection, and
per-shape aspect rules are all yours to build — see
[objects snapping](/docs/sandbox/Objects_Snapping.html) for one approach.
A production editor also needs text editing, templates, and export around the
Transformer. If you would rather not build those, [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=docs&utm_content=transformer-basic) is a commercial
design editor SDK built on Konva by the Konva maintainers that ships them.
---
# Node.js Setup
> Set up Konva for server-side rendering in Node.js with canvas or skia-canvas backends for image generation and batch processing.
Source: https://konvajs.org/docs/nodejs/nodejs-setup/index.html
Konva can be used in Node.js environments for server-side rendering, image processing, and canvas operations. This guide will help you set up Konva in your Node.js project.
## Konva Version 10+
Konva v10+ dropped default support for Node.js environment. You now need to explicitly import a canvas backend.
### Installation
Konva v10+ offers two backend options for Node.js:
**node-canvas Backend**:
```bash
npm install konva canvas
```
**Skia Backend (Better Performance)**:
```bash
npm install konva skia-canvas
```
### Usage
Import Konva and your chosen backend:
**Canvas Backend**:
```js
import Konva from 'konva';
import 'konva/canvas-backend';
```
**Skia Backend**:
```js
import Konva from 'konva';
import 'konva/skia-backend';
```
**Complete Example**:
```js
import Konva from 'konva';
import 'konva/canvas-backend'; // or 'konva/skia-backend'
// Create a stage
const stage = new Konva.Stage({
container: 'container', // This will be ignored in Node.js
width: 800,
height: 600
});
// ... the rest of your konva code
// Export as data URL
const dataURL = stage.toDataURL();
```
## Konva Version ≤ 9 (Legacy)
For older versions of Konva, the setup was simpler:
### Installation
```bash
npm install konva
```
### Setup
```js
const Konva = require('konva');
// Create a stage
const stage = new Konva.Stage({
container: 'container', // This will be ignored in Node.js
width: 800,
height: 600
});
// ... the rest of your konva code
// Export as data URL
const dataURL = stage.toDataURL();
```
## Server-Side Rendering Considerations
When using Konva in Node.js, keep in mind:
1. **No DOM**: Konva doesn't require a DOM, making it perfect for server-side rendering
2. **Canvas Export**: Use `stage.toDataURL()` to export your canvas as an image
3. **Memory Management**: Be mindful of memory usage when processing multiple canvases
4. **Performance**: Konva performs well in Node.js environments for batch operations
5. **SSR Frameworks**: For Next.js and other SSR frameworks, consider using client-side only rendering for canvas content
## Common Use Cases
- **Image Generation**: Create dynamic images for emails, reports, or social media
- **Chart Generation**: Generate charts and graphs server-side
- **Document Processing**: Add graphics to PDFs or other documents
- **Batch Processing**: Process multiple images or graphics in parallel
---
# AI Tools for Konva.js Development
> Use AI to build Konva.js apps faster. Chat with an AI bot trained on Konva docs, or connect the Konva MCP server to Cursor, Claude Desktop, Windsurf, and other AI coding tools.
Source: https://konvajs.org/docs/ai_tools.html
## Coding with Konva and AI
We have several AI tools to help you build Konva apps faster. All of them are powered by [CrawlChat](https://www.crawlchat.com/).
The AI agent uses Konva docs extensively to answer your questions. Please remember it is an LLM and as any modern LLM it may give wrong answers.
## AI Chat Bot
Click the "Ask AI" button on any page to ask a question about Konva.
You can also join the [Konva Discord community](https://discord.gg/8FqZwVT) and ask `@AiBot-CrawlChat` there.
## MCP (Model Context Protocol)
MCP is a standard protocol that connects AI coding tools to external documentation. With the Konva MCP server, tools like Cursor, Claude Desktop, and Windsurf can access Konva documentation directly when helping you write code.
### Cursor
Add the following to your Cursor MCP settings:
**Important: Cursor uses MCP only in "Agent" mode. "Ask" and other modes will not use it.**
```json
"konva-documentation": {
"command": "npx",
"args": [
"crawl-chat-mcp",
"--id=67d221efb4b9de65095a2579",
"--name=konva_documentation"
]
}
```
### Claude Desktop
Add the following to your Claude Desktop config file (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"konva-documentation": {
"command": "npx",
"args": [
"crawl-chat-mcp",
"--id=67d221efb4b9de65095a2579",
"--name=konva_documentation"
]
}
}
}
```
On macOS, the config file is at `~/Library/Application Support/Claude/claude_desktop_config.json`. On Windows, it's at `%APPDATA%\Claude\claude_desktop_config.json`.
### Windsurf
Add the following to your Windsurf MCP configuration:
```json
"konva-documentation": {
"command": "npx",
"args": [
"crawl-chat-mcp",
"--id=67d221efb4b9de65095a2579",
"--name=konva_documentation"
]
}
```
### Generic MCP Command
For any MCP-compatible tool, use:
```
npx crawl-chat-mcp --id=67d221efb4b9de65095a2579 --name=konva_documentation
```
## LLM-Readable Documentation
Konva provides machine-readable documentation files for AI tools:
- [`/llms.txt`](/llms.txt) — Concise summary of Konva with key documentation links (follows the [llmstxt.org](https://llmstxt.org/) standard)
- [`/llms-full.txt`](pathname:///llms-full.txt) — The same index followed by the full text of every documentation page, for tools that ingest one file
- [`/llms-small.txt`](pathname:///llms-small.txt) — The same without the sandbox demos, small enough for a single model context
- [`
.md`](pathname:///docs/overview.md) — Every documentation page is also served as plain markdown at the same path with a `.md` extension
These files help AI assistants give accurate answers about Konva.
If you are building a design editor with [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=docs&utm_content=ai-tools), the commercial design editor SDK built on Konva by the Konva maintainers, its [build with AI](https://polotno.com/docs/build-with-ai?utm_source=konvajs&utm_medium=docs&utm_content=ai-tools) page lists the equivalent files and MCP setup.
## Tips for Using AI with Konva
When asking AI tools about Konva, you'll get better results if you:
- Mention "Konva" or "react-konva" explicitly in your prompt
- Reference specific Konva features (e.g., "Transformer", "Layer", "toDataURL")
- Ask about one task at a time rather than combining multiple questions
- Verify AI-generated code against the [Konva docs](https://konvajs.org/docs/overview.html) and [API reference](https://konvajs.org/api/Konva.html)
---
# Need help with Konva library?
> Get help with Konva via StackOverflow, GitHub Issues, Discord chat, consulting, and other community resources.
Source: https://konvajs.org/docs/support.html
## Looking for a help with Konva framework?
Here is what you should do:
1. First try to find solutions online. Try to search your question. You can use google, or built-in search on top of that page.
2. The best place to ask questions is [StackOverflow](https://stackoverflow.com/questions/tagged/konvajs). You will have more chances to have a good answer if you create a high quality question with online demo, code samples, correct tags, etc.
3. If you found a bug or you want to request a feature go to [Issues Page](https://github.com/konvajs/konva/issues).
4. If you just want to discuss Konva you can join [discord Chat](https://discord.gg/8FqZwVT)
5. If you have something interesting to share use Twitter `#konvajs` hashtag
6. Visit [Changelog](https://github.com/konvajs/konva/blob/master/CHANGELOG.md) to see what is changing.
7. Need a consulting or strategy review? Go to the [consulting page](https://lavrton.com/consulting/).
8. Building a full design editor rather than a custom canvas? [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=docs&utm_content=support) is a commercial design editor SDK built on Konva by the Konva maintainers, with its own documentation and support.
---
# Angular Konva Cache Tutorial
> Learn how to cache Konva shapes in Angular using ng2-konva to improve canvas rendering performance.
Source: https://konvajs.org/docs/angular/Cache.html
Caching rasterizes a node into an internal image, which can improve performance and is required for some effects such as filters.
This example accesses the underlying Konva node after render and calls `cache()` manually.
**Instructions**: The demo shows a rectangle with shadow styling that is cached after the view initializes.
For more details, see the [Node API Reference](/api/Konva.Node.html) and [`cache()` documentation](/api/Konva.Node.html#cache).
## Cache Example
```js
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { RectConfig } from 'konva/lib/shapes/Rect';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App implements AfterViewInit {
@ViewChild('rect') rect!: any;
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configRect: RectConfig = {
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'red',
shadowBlur: 10,
shadowColor: 'black',
shadowOffsetX: 5,
shadowOffsetY: 5
};
ngAfterViewInit() {
if (this.rect) {
this.rect.getNode().cache();
}
}
}
```
---
# Angular Konva Custom Shape Tutorial
> Learn how to draw custom shapes on canvas in Angular using ng2-konva with the sceneFunc drawing function.
Source: https://konvajs.org/docs/angular/Custom_Shape.html
To create a custom shape with `ng2-konva`, use the `ko-shape` component and provide a `sceneFunc` drawing function.
Inside `sceneFunc`, you can use the native canvas context together with Konva helpers such as `context.fillStrokeShape(shape)` to apply fill, stroke, and shadow styles correctly.
**Instructions**: The demo draws a custom triangle-like shape with canvas path commands.
For more details, see the [Shape API Reference](/api/Konva.Shape.html).
## Custom Shape Example
```js
import { Component } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { ShapeConfig } from 'konva/lib/shapes/Shape';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App {
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configShape: ShapeConfig = {
x: 100,
y: 100,
fill: 'red',
stroke: 'black',
strokeWidth: 2,
sceneFunc: (context: any, shape: any) => {
context.beginPath();
context.moveTo(0, 0);
context.lineTo(100, 0);
context.lineTo(50, 100);
context.closePath();
context.fillStrokeShape(shape);
}
};
}
```
---
# Angular Konva Drag and Drop Tutorial
> Learn how to implement drag and drop for canvas shapes in Angular using ng2-konva with event handlers.
Source: https://konvajs.org/docs/angular/Drag_And_Drop.html
To make a shape draggable, set `draggable: true` in its config and listen to drag events on the node.
This example moves the dragged circle to the top of its layer when dragging starts.
**Instructions**: Drag the circle around the stage and notice that it is brought to the top when you start dragging.
For more details, see the [Node API Reference](/api/Konva.Node.html) and the [Circle API Reference](/api/Konva.Circle.html).
## Drag and Drop Example
```js
import { Component } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { CircleConfig } from 'konva/lib/shapes/Circle';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App {
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configCircle: CircleConfig = {
x: 100,
y: 100,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true,
};
public handleDragstart(event: any): void {
event.target.moveToTop();
}
}
```
---
# Angular Konva Events Tutorial
> Learn how to handle mouse, touch, and pointer events on canvas shapes in Angular using ng2-konva event bindings.
Source: https://konvajs.org/docs/angular/Events.html
To handle pointer events in Angular, attach listeners such as `(mousemove)` and `(mouseout)` directly to `ng2-konva` components.
This demo updates a text label from stage pointer coordinates while the mouse moves over the triangle.
**Instructions**: Move your mouse over the triangle to update the coordinate label, then move out to reset the text.
For more details, see the [Node API Reference](/api/Konva.Node.html) and the [Stage API Reference](/api/Konva.Stage.html).
## Events Example
```js
import { Component } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { RegularPolygonConfig } from 'konva/lib/shapes/RegularPolygon';
import { TextConfig } from 'konva/lib/shapes/Text';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App {
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configTriangle: RegularPolygonConfig = {
x: 80,
y: 120,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4
};
public configText: TextConfig = {
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: 'hello',
fill: 'black'
};
public handleMouseMove(event: any): void {
const mousePos = event.target.getStage().getPointerPosition();
const x = mousePos.x - 190;
const y = mousePos.y - 40;
this.configText = { ...this.configText, text: 'x: ' + x + ', y: ' + y };
}
public handleMouseOut(): void {
this.configText = { ...this.configText, text: 'Mouseout triangle' };
}
}
```
---
# Angular Konva Filters Tutorial
> Learn how to apply visual filters like blur to canvas shapes in Angular using ng2-konva with caching.
Source: https://konvajs.org/docs/angular/Filters.html
To apply filters in Konva, set the `filters` property and cache the node after it is mounted.
This demo uses a blur filter on a circle and calls `cache()` once the underlying Konva node is available.
**Instructions**: The demo renders a blurred red circle. Remove `cache()` in the editor to see why caching is required for filters.
For more details, see the [Filters API Reference](/api/Konva.Filters.html) and [`cache()` documentation](/api/Konva.Node.html#cache).
## Filters Example
```js
import { Component, ViewChild } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { CircleConfig } from 'konva/lib/shapes/Circle';
import Konva from 'konva';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App {
@ViewChild('circle') circle!: any;
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configCircle: CircleConfig = {
x: 150,
y: 150,
radius: 50,
fill: 'red',
filters: [Konva.Filters.Blur],
blurRadius: 15
};
ngAfterViewInit() {
this.circle.getNode().cache({ offset: 10 });
}
}
```
---
# Angular Konva Images Tutorial
> Learn how to load and display images on an HTML5 canvas in Angular using ng2-konva Image component.
Source: https://konvajs.org/docs/angular/Images.html
To display images with `Konva` in Angular, use `ko-image` and assign a loaded `HTMLImageElement` to the `image` property.
For Angular 21 apps, signals are the simplest way to update image config from async callbacks such as `Image.onload`.
**Instructions**: The demo loads an external image and renders it after the browser finishes loading it.
For more details, see the [Image API Reference](/api/Konva.Image.html).
## Images Example
```js
import { Component, OnInit, signal } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { ImageConfig } from 'konva/lib/shapes/Image';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App implements OnInit {
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configImage = signal({
x: 50,
y: 50,
image: null,
width: 100,
height: 100
});
ngOnInit() {
const imageObj = new Image();
imageObj.onload = () => {
this.configImage.update((config) => ({
...config,
image: imageObj
}));
};
imageObj.src = 'https://konvajs.org/assets/yoda.jpg';
}
}
```
---
# Angular Konva Shapes Tutorial
> Learn how to draw rectangles, circles, lines, and text on canvas in Angular using ng2-konva shape components.
Source: https://konvajs.org/docs/angular/Shapes.html
All `ng2-konva` shape components map to Konva shapes with the `ko-` prefix, so you can pass any regular Konva shape settings through the `config` object.
This example shows several common shapes with different styling options such as shadows, gradients, and line tension.
**Instructions**: The demo renders text, a rectangle, a circle, and a closed line with a gradient fill.
For more details, see the [Konva API Reference](/api/Konva.html) and the [Line API Reference](/api/Konva.Line.html).
## Shapes Example
```js
import { Component } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { TextConfig } from 'konva/lib/shapes/Text';
import { RectConfig } from 'konva/lib/shapes/Rect';
import { CircleConfig } from 'konva/lib/shapes/Circle';
import { LineConfig } from 'konva/lib/shapes/Line';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App {
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configText: TextConfig = {
text: 'Some text on canvas',
fontSize: 15
};
public configRect: RectConfig = {
x: 20,
y: 50,
width: 100,
height: 100,
fill: 'red',
shadowBlur: 10
};
public configCircle: CircleConfig = {
x: 200,
y: 100,
radius: 50,
fill: 'green'
};
public configLine: LineConfig = {
x: 20,
y: 200,
points: [0, 0, 100, 0, 100, 100],
tension: 0.5,
closed: true,
stroke: 'black',
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow']
};
}
```
---
# Angular Konva Simple Animations Tutorial
> Learn how to create simple canvas animations in Angular using ng2-konva with the Konva.Animation class.
Source: https://konvajs.org/docs/angular/Simple_Animations.html
Konva provides two main animation tools: `node.to()` for simple transitions and `Konva.Animation` for frame-by-frame updates.
This example uses `Konva.Animation` directly to move a circle in a sine wave after the view initializes.
**Instructions**: The demo continuously animates the red circle left and right.
For more details, see the [Animation docs](/docs/animations/Rotation.html) and the [Node API Reference](/api/Konva.Node.html).
## Simple Animation Example
```js
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { CircleConfig } from 'konva/lib/shapes/Circle';
import Konva from 'konva';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App implements OnInit, OnDestroy {
@ViewChild('circle') circle!: any;
private animation: any = null;
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configCircle: CircleConfig = {
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4
};
ngAfterViewInit() {
const circle = this.circle.getNode();
this.animation = new Konva.Animation((frame: any) => {
const time = frame.time;
const x = 100 + Math.sin(time / 1000) * 100;
circle.x(x);
}, circle.getLayer());
this.animation.start();
}
ngOnDestroy() {
if (this.animation) {
this.animation.stop();
}
}
}
```
---
# Angular Konva Transformer Tutorial
> Learn how to resize and rotate canvas shapes in Angular using ng2-konva Transformer for interactive selection.
Source: https://konvajs.org/docs/angular/Transformer.html
The Transformer tool is attached to nodes imperatively. In Angular, that means creating a `ko-transformer` and connecting it to the selected shape after the view is ready.
**Instructions**: Click the rectangle to select it, drag it to move it, and click on the empty stage to clear the selection.
For more details, see the [Transformer API Reference](/api/Konva.Transformer.html).
## Transformer Example
```js
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { RectConfig } from 'konva/lib/shapes/Rect';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App implements AfterViewInit {
@ViewChild('rect') rect!: any;
@ViewChild('transformer') transformer!: any;
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configRect: RectConfig = {
x: 100,
y: 100,
width: 100,
height: 100,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
};
ngAfterViewInit() {
this.transformer.getNode().nodes([this.rect.getNode()]);
}
public handleStageClick(event: any): void {
if (event?.target === event.target.getStage()) {
this.transformer.getNode().nodes([]);
}
}
public handleShapeClick(): void {
this.transformer.getNode().nodes([this.rect.getNode()]);
}
}
```
---
# Angular Konva Undo-Redo Tutorial
> Learn how to implement undo and redo for canvas interactions in Angular using ng2-konva with state history tracking.
Source: https://konvajs.org/docs/angular/Undo-Redo.html
Undo and redo work best when canvas state is stored in plain data objects instead of reading values back from the canvas on every render.
This example stores rectangle positions in a small history stack and restores previous snapshots when you click Undo or Redo.
**Instructions**: Drag the rectangle to create history entries, then click Undo and Redo to move through the saved states.
For more details, see the [Rect API Reference](/api/Konva.Rect.html).
## Undo-Redo Example
```js
import { Component } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { RectConfig } from 'konva/lib/shapes/Rect';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
Undo
Redo
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App {
private history: RectConfig[] = [];
private currentIndex: number = -1;
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configRect: RectConfig = {
x: 100,
y: 100,
width: 100,
height: 100,
fill: 'red',
draggable: true
};
constructor() {
this.saveState();
}
private saveState(): void {
// Remove any states after current index
this.history = this.history.slice(0, this.currentIndex + 1);
// Add current state
this.history.push({ ...this.configRect });
this.currentIndex++;
}
public handleDragEnd(event: any): void {
this.configRect = {
...this.configRect,
x: event.target.x(),
y: event.target.y()
};
this.saveState();
}
public undo(): void {
if (this.canUndo()) {
this.currentIndex--;
this.configRect = { ...this.history[this.currentIndex] };
}
}
public redo(): void {
if (this.canRedo()) {
this.currentIndex++;
this.configRect = { ...this.history[this.currentIndex] };
}
}
public canUndo(): boolean {
return this.currentIndex > 0;
}
public canRedo(): boolean {
return this.currentIndex < this.history.length - 1;
}
}
```
## Where a hand-built history stops
The history above records one value per step. A production editor has to record
grouped operations, so that a multi-select drag undoes as a single step, plus
transforms and images that finish loading after the action. That state machine
usually grows larger than the drawing code, so plan the history around document
operations rather than around raw node state.
---
# Angular Konva Z-Index Tutorial
> Learn how to control shape stacking order and z-index in Angular using ng2-konva by managing the data array.
Source: https://konvajs.org/docs/angular/zIndex.html
To control shape stacking order in Angular, update the order of the data array that renders your shapes.
The demo shows how to:
1. Create an array of circle shapes with random positions and colors
2. Handle drag events to update the visual order of shapes
3. Maintain the correct stacking order by manipulating the array order
4. Keep rendering driven by Angular state instead of imperative `zIndex()` calls
**Instructions**: Try to drag a circle. When you start dragging, it will automatically move to the top of the stack. This is achieved by manipulating the array of circles in our data, not by manually changing zIndex.
```js
import { Component, OnInit } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { CircleConfig } from 'konva/lib/shapes/Circle';
import {
CoreShapeComponent,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
standalone: true,
template: `
@for (item of items; track trackById($index, item)) {
}
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App implements OnInit {
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public items: CircleConfig[] = [];
private dragItemId: string | null = null;
ngOnInit() {
this.generateItems();
}
private generateItems(): void {
const newItems: CircleConfig[] = [];
for (let i = 0; i < 10; i++) {
newItems.push({
x: Math.random() * this.configStage.width!,
y: Math.random() * this.configStage.height!,
radius: 50,
id: "node-" + i,
fill: this.getRandomColor(),
draggable: true
});
}
this.items = newItems;
}
private getRandomColor(): string {
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'];
return colors[Math.floor(Math.random() * colors.length)];
}
public handleDragstart(event: any): void {
this.dragItemId = event.target.id();
const item = this.items.find(i => i.id === this.dragItemId);
if (item) {
this.items = [
...this.items.filter((i) => i.id !== this.dragItemId),
item
];
}
}
public handleDragend(): void {
this.dragItemId = null;
}
public trackById(index: number, item: CircleConfig): string {
return item.id as string;
}
}
```
---
# Animate Position Tutorial
> Learn how to animate shape position on HTML5 Canvas with Konva.js. Move shapes smoothly using Konva.Animation for frame-based updates.
Source: https://konvajs.org/docs/animations/Moving.html
To animate a shape's position with Konva, we can create a new animation with `Konva.Animation`
which modifies the shape's position with each animation frame.
For a full list of attributes and methods, check out the [Konva.Animation documentation](/api/Konva.Animation.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: 50,
y: window.innerHeight / 2,
radius: 30,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
layer.add(circle);
const amplitude = 100;
const period = 2000; // in milliseconds
const anim = new Konva.Animation(function(frame) {
circle.x(
amplitude * Math.sin((frame.time * 2 * Math.PI) / period) +
window.innerWidth / 2
);
}, layer);
anim.start();
````
```js
import { Stage, Layer, Circle } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const circleRef = useRef(null);
useEffect(() => {
const amplitude = 100;
const period = 2000; // in milliseconds
const anim = new Konva.Animation((frame) => {
circleRef.current.x(
amplitude * Math.sin((frame.time * 2 * Math.PI) / period) +
window.innerWidth / 2
);
}, circleRef.current.getLayer());
anim.start();
return () => {
anim.stop();
};
}, []);
return (
);
};
export default App;
````
```js
```
---
# Rotation Animation tutorial
> Learn how to animate shape rotation on HTML5 Canvas with Konva.js. Create smooth rotation animations using Konva.Animation.
Source: https://konvajs.org/docs/animations/Rotation.html
To animate a shape's rotation with Konva, we can create a new animation with
`Konva.Animation`, and define a function which modifies the shape's rotation with each animation frame.
In this tutorial, we'll rotate a blue rectangle about the top left corner,
a yellow rectangle about its center, and a red rectangle about an outside point.
For a full list of attributes and methods, check out the [Konva.Animation documentation](/api/Konva.Animation.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// blue rectangle - rotate around top-left corner
const blueRect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
offset: {
x: 0,
y: 0,
},
});
// yellow rectangle - rotate around center
const yellowRect = new Konva.Rect({
x: 200,
y: 50,
width: 100,
height: 50,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
offset: {
x: 50,
y: 25,
},
});
// red rectangle - rotate around point outside shape
const redRect = new Konva.Rect({
x: 350,
y: 50,
width: 100,
height: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
offset: {
x: -50,
y: 25,
},
});
layer.add(blueRect);
layer.add(yellowRect);
layer.add(redRect);
const angularSpeed = 90;
const anim = new Konva.Animation(function(frame) {
const angleDiff = (frame.timeDiff * angularSpeed) / 1000;
blueRect.rotate(angleDiff);
yellowRect.rotate(angleDiff);
redRect.rotate(angleDiff);
}, layer);
anim.start();
````
```js
import { Stage, Layer, Rect } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const blueRectRef = useRef(null);
const yellowRectRef = useRef(null);
const redRectRef = useRef(null);
useEffect(() => {
const angularSpeed = 90;
const anim = new Konva.Animation((frame) => {
const angleDiff = (frame.timeDiff * angularSpeed) / 1000;
blueRectRef.current.rotate(angleDiff);
yellowRectRef.current.rotate(angleDiff);
redRectRef.current.rotate(angleDiff);
}, blueRectRef.current.getLayer());
anim.start();
return () => {
anim.stop();
};
}, []);
return (
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Konva Scale Animation Tutorial
> Learn how to animate shape scaling on the HTML5 canvas using Konva.Animation with scaleX and scaleY properties.
Source: https://konvajs.org/docs/animations/Scaling.html
To animate a shape's scale with Konva, we can create a new animation with
`Konva.Animation`, and define a function which modifies the shape's scale with each animation frame.
In this tutorial, we'll scale the x and y component of a blue hexagon, the y component
of a yellow hexagon, and the x component of a red hexagon about an axis positioned on the right side of the shape.
**Instructions:** drag and drop the hexagons as they animate
For a full list of attributes and methods, check out the [Konva.Animation documentation](/api/Konva.Animation.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// blue hexagon - scale x and y
const blueHex = new Konva.RegularPolygon({
x: 50,
y: 50,
sides: 6,
radius: 20,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
// yellow hexagon - scale y only
const yellowHex = new Konva.RegularPolygon({
x: 150,
y: 50,
sides: 6,
radius: 20,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
// red hexagon - scale x only
const redHex = new Konva.RegularPolygon({
x: 250,
y: 50,
sides: 6,
radius: 20,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
layer.add(blueHex);
layer.add(yellowHex);
layer.add(redHex);
const period = 2000;
const anim = new Konva.Animation(function(frame) {
const scale = Math.sin(frame.time * 2 * Math.PI / period) + 2;
// blue hex - scale x and y
blueHex.scale({ x: scale, y: scale });
// yellow hex - scale y only
yellowHex.scaleY(scale);
// red hex - scale x only
redHex.scaleX(scale);
}, layer);
anim.start();
````
```js
import { Stage, Layer, RegularPolygon } from 'react-konva';
import { useEffect, useRef, useState } from 'react';
const App = () => {
const blueHexRef = useRef(null);
const yellowHexRef = useRef(null);
const redHexRef = useRef(null);
const [positions, setPositions] = useState({
blue: { x: 50, y: 50 },
yellow: { x: 150, y: 50 },
red: { x: 250, y: 50 }
});
useEffect(() => {
const period = 2000;
const anim = new Konva.Animation((frame) => {
const scale = Math.sin(frame.time * 2 * Math.PI / period) + 2;
// blue hex - scale x and y
blueHexRef.current.scale({ x: scale, y: scale });
// yellow hex - scale y only
yellowHexRef.current.scaleY(scale);
// red hex - scale x only
redHexRef.current.scaleX(scale);
}, blueHexRef.current.getLayer());
anim.start();
return () => {
anim.stop();
};
}, []);
const handleDragEnd = (e, color) => {
setPositions(prev => ({
...prev,
[color]: { x: e.target.x(), y: e.target.y() }
}));
};
return (
handleDragEnd(e, 'blue')}
/>
handleDragEnd(e, 'yellow')}
/>
handleDragEnd(e, 'red')}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Konva Stop Animation Tutorial
> Learn how to start and stop canvas animations in Konva using the start() and stop() methods on Konva.Animation.
Source: https://konvajs.org/docs/animations/Stop_Animation.html
To stop an animation with Konva, we can use the `stop()` method.
To restart the animation, we can again call the `start()`.
**Instructions:** Click on "Start" to start the animation and "Stop" to stop the animation.
For a full list of attributes and methods, check out the [Konva.Animation documentation](/api/Konva.Animation.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 30,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
layer.add(circle);
// add buttons
const container = document.createElement('div');
document.body.appendChild(container);
container.style.position = 'absolute';
container.style.top = '0px';
container.style.left = '0px';
const startBtn = document.createElement('button');
startBtn.textContent = 'Start Animation';
container.appendChild(startBtn);
const stopBtn = document.createElement('button');
stopBtn.textContent = 'Stop Animation';
container.appendChild(stopBtn);
const anim = new Konva.Animation(function(frame) {
circle.x(
amplitude * Math.sin((frame.time * 2 * Math.PI) / period) +
stage.width() / 2
);
}, layer);
const amplitude = 100;
const period = 2000;
startBtn.addEventListener('click', () => anim.start());
stopBtn.addEventListener('click', () => anim.stop());
````
```js
import { Stage, Layer, Circle } from 'react-konva';
import { useEffect, useRef, useState } from 'react';
const App = () => {
const circleRef = useRef(null);
const [isAnimating, setIsAnimating] = useState(false);
const animRef = useRef(null);
useEffect(() => {
const amplitude = 100;
const period = 2000;
animRef.current = new Konva.Animation((frame) => {
circleRef.current.x(
amplitude * Math.sin((frame.time * 2 * Math.PI) / period) +
window.innerWidth / 2
);
}, circleRef.current.getLayer());
return () => {
if (animRef.current) {
animRef.current.stop();
}
};
}, []);
const handleStart = () => {
animRef.current.start();
setIsAnimating(true);
};
const handleStop = () => {
animRef.current.stop();
setIsAnimating(false);
};
return (
Start Animation
Stop Animation
);
};
export default App;
````
```js
Start Animation
Stop Animation
```
---
# Text Animations Tutorial
> Learn how to create character-by-character text animations in Konva using the charRenderFunc property for per-character rendering effects.
Source: https://konvajs.org/docs/animations/Text_Animations.html
**Note: this feature is only available from Konva v10.0.0.**
Konva provides powerful text animation capabilities through the `charRenderFunc` property. This function allows you to customize how each character is rendered, enabling character-by-character animations and effects.
```js
var text = new Konva.Text({
x: 10,
y: 10,
text: 'AB',
fontSize: 20,
charRenderFunc: function ({ context, index }) {
if (index === 1) {
// shift only the second character
context.translate(0, 10);
}
},
});
```
The `charRenderFunc` receives a context object with the following parameters:
- **`char`** - The actual character string being rendered
- **`index`** - Zero-based index of the character in the entire text
- **`x`** - X position where the character will be rendered
- **`y`** - Y position where the character will be rendered
- **`lineIndex`** - Zero-based index of the line containing this character
- **`column`** - Zero-based column position within the current line
- **`isLastInLine`** - Boolean indicating if this is the last character in its line
- **`width`** - Width of the character
- **`context`** - Canvas 2D rendering context for applying transformations, opacity, colors, etc.
This allows you to apply transformations, opacity changes, or other effects to individual characters based on their position and properties.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// we will store the opacity of each character in an array
const charOpacities = [];
const textNode = new Konva.Text({
x: window.innerWidth / 2 - 100,
y: window.innerHeight / 2 - 20,
text: 'ANIMATION',
fontSize: 40,
fontFamily: 'Arial',
fill: '#333',
charRenderFunc: function ({ context, index }) {
context.globalAlpha = charOpacities[index];
},
});
layer.add(textNode);
const anim = new Konva.Animation(function(frame) {
const time = frame.time;
const cycleDuration = 4000; // 4 seconds total cycle
const fadeInDuration = 1500; // 1.5 seconds to fade in all
const holdDuration = 1000; // 1 second hold
const fadeOutDuration = 1500; // 1.5 seconds to fade out all
const cycleTime = time % cycleDuration;
for (let i = 0; i < textNode.text().length; i++) {
const charDelay = i * 150; // 150ms delay between characters
if (cycleTime < fadeInDuration) {
// Fade in phase
const charStartTime = charDelay;
const charFadeTime = Math.max(0, cycleTime - charStartTime);
charOpacities[i] = Math.min(1, charFadeTime / 300);
} else if (cycleTime < fadeInDuration + holdDuration) {
// Hold phase - all characters visible
charOpacities[i] = 1;
} else {
// Fade out phase
const fadeOutStart = fadeInDuration + holdDuration;
const charFadeOutDelay = i * 150; // Same order as fade in
const charFadeOutTime = Math.max(0, cycleTime - fadeOutStart - charFadeOutDelay);
charOpacities[i] = Math.max(0, 1 - charFadeOutTime / 300);
}
}
}, layer);
anim.start();
```
```js
import { Stage, Layer, Text } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const textRef = useRef(null);
const layerRef = useRef(null);
const charOpacitiesRef = useRef([]);
useEffect(() => {
const anim = new Konva.Animation((frame) => {
const time = frame.time;
const cycleDuration = 4000; // 4 seconds total cycle
const fadeInDuration = 1500; // 1.5 seconds to fade in all
const holdDuration = 1000; // 1 second hold
const fadeOutDuration = 1500; // 1.5 seconds to fade out all
const cycleTime = time % cycleDuration;
for (let i = 0; i < textRef.current.text().length; i++) {
const charDelay = i * 150; // 150ms delay between characters
if (cycleTime < fadeInDuration) {
// Fade in phase
const charStartTime = charDelay;
const charFadeTime = Math.max(0, cycleTime - charStartTime);
charOpacitiesRef.current[i] = Math.min(1, charFadeTime / 300);
} else if (cycleTime < fadeInDuration + holdDuration) {
// Hold phase - all characters visible
charOpacitiesRef.current[i] = 1;
} else {
// Fade out phase
const fadeOutStart = fadeInDuration + holdDuration;
const charFadeOutDelay = i * 150; // Same order as fade in
const charFadeOutTime = Math.max(0, cycleTime - fadeOutStart - charFadeOutDelay);
charOpacitiesRef.current[i] = Math.max(0, 1 - charFadeOutTime / 300);
}
}
}, layerRef.current);
anim.start();
return () => {
anim.stop();
};
}, []);
return (
{
context.globalAlpha = charOpacitiesRef.current[index] || 0;
}}
/>
);
};
export default App;
```
```js
```
---
# Clipping Functions Tutorial
> Learn how to use custom clipping functions with clipFunc to create complex clipping regions for groups and layers in Konva.
Source: https://konvajs.org/docs/clipping/Clipping_Function.html
## How to clip nodes in the layer?
To draw things inside of complex clipping regions with Konva, we can set the `clipFunc`
property of a group, a layer.
In this tutorial, we'll draw blobs inside of a two circles clipping region applied to a group.
```js
import Konva from 'konva';
// First we need to create stage
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
// Then create layer
const layer = new Konva.Layer();
const group = new Konva.Group({
clipFunc: function (ctx) {
ctx.beginPath();
ctx.arc(200, 120, 50, 0, Math.PI * 2, false);
ctx.arc(280, 120, 50, 0, Math.PI * 2, false);
},
});
for (let i = 0; i < 20; i++) {
const blob = new Konva.Circle({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
radius: Math.random() * 50,
fill: 'green',
opacity: 0.8,
});
group.add(blob);
}
// add the shape to the layer
layer.add(group);
// add the layer to the stage
stage.add(layer);
```
```js
import { Stage, Layer, Group, Circle } from 'react-konva';
const App = () => {
const blobs = Array.from({ length: 20 }, (_, i) => ({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
radius: Math.random() * 50,
}));
return (
{
ctx.beginPath();
ctx.arc(200, 120, 50, 0, Math.PI * 2, false);
ctx.arc(280, 120, 50, 0, Math.PI * 2, false);
}}
>
{blobs.map((blob, i) => (
))}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Simple Clipping tutorial
> Learn how to apply simple rectangular clipping regions to groups and layers in Konva using the clip property.
Source: https://konvajs.org/docs/clipping/Clipping_Regions.html
To draw things inside of clipping regions with Konva, we can set the `clip`
property of a group or a layer.
Clipping regions are defined by an `x`, `y`, `width`, and `height`. In this tutorial,
we'll draw blobs inside of a rectangular clipping region applied to a group.
For more complex cases take a look into clipping function. [Clipping Function](/docs/clipping/Clipping_Function.html)
```js
import Konva from 'konva';
// First we need to create stage
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
// Then create layer
const layer = new Konva.Layer();
const group = new Konva.Group({
clip: {
x: 100,
y: 20,
width: 200,
height: 200,
},
});
for (let i = 0; i < 20; i++) {
const blob = new Konva.Circle({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
radius: Math.random() * 50,
fill: 'green',
opacity: 0.8,
});
group.add(blob);
}
// add the shape to the layer
layer.add(group);
// add the layer to the stage
stage.add(layer);
```
```js
import { Stage, Layer, Group, Circle } from 'react-konva';
const App = () => {
const blobs = Array.from({ length: 20 }, (_, i) => ({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
radius: Math.random() * 50,
}));
return (
{blobs.map((blob, i) => (
))}
);
};
export default App;
```
```js
```
---
# Save and Load HTML5 Canvas Stage Best Practices
> Best practices for saving and loading HTML5 Canvas state with Konva.js. Tips for serialization, data management, and state persistence.
Source: https://konvajs.org/docs/data_and_serialization/Best_Practices.html
## What is the best way to save/load full stage content and how to implement undo/redo?
If you want to save/load simple canvas content you can use the built-in `Konva` methods: `node.toJSON()` and `Node.create(json)`.
See [simple](/docs/data_and_serialization/Simple_Load.html) and [complex](/docs/data_and_serialization/Complex_Load.html) demos.
But those methods are useful only in very small apps. In bigger apps it is VERY hard to use those methods. Why? Because the tree structure is usually very complex in larger apps, you may have a lot of event listeners, images, filters, etc. That data is not serializable into JSON (or it is very hard to do that).
Also it is very common that nodes in a tree have a lot information that is not directly related to the state of your app, but just used to describe visual view of your app.
For instance, let's think we have a game, that draws several balls in canvas. The balls are not just circles, but the complex visual groups of objects with shadows and texts inside them (like "Made in China"). Now let's think you want to serialize state of your app and use it somewhere else. Like send to another computer or implement undo/redo. Almost all the visual information (shadows, texts, sizes) is not critical and may be you don't need to save it. Because all balls have the same shadows, sizes, etc. But what is critical? In that case it is just a number of balls and their coordinates. You need to save/load only that information. It will be just a simple array:
```javascript
var state = [{x: 10, y: 10}, { x: 160, y: 1041}]
```
Now when you have that information, you need to have a function, that can create the whole canvas structure.
If you want to update your canvas, for instance, you want to create a new ball, you don't need to create a new canvas node directly (like creating new instance of `Konva.Circle`), you just need to push a new object into a state and update (or recreate) canvas.
In that case you don't need to care about image loading, filters, event listeners, etc in saving/loading phases. Because you do all these actions in your `create` or `update` functions.
You would better understand what I am talking about if you know how many modern frameworks work (like `React`, `Vue`, `Angular` and many other).
Also take a look into these demos to have a better idea:
1. [Undo/redo with react](/docs/react/Undo-Redo.html)
1. [Save/load with Vue](/docs/vue/Save-Load.html)
How to implement that `create` and `update` functions? It depends. From my point of view it will be easier to use frameworks that can do that job for you, like [react-konva](/docs/react/index.html).
If you don't want to use such frameworks you need to think in terms of your own app. Here I will try to make a small demo to give you an idea.
The super naive method is to implement just one function `create(state)` that will do all the complex job of loading.
If you have some changes in your app you just need to destroy the canvas and create a new one. But the drawback of such approach is possibly a bad performance.
A bit smarter implementation is to create two functions `create(state)` and `update(state)`. `create` will make instances of all required objects, attach events and load images. `update` will update properties of nodes. If number of objects is changed - destroy all and create from scratch. If only some properties changed - call `update`.
**Instructions:** In that demo we will have a bunch of images with filters, and you can add more, move them, apply a new filter by clicking on images and use undo/redo.
```js
import Konva from 'konva';
// Initial state
let state = {
images: [
{ x: 50, y: 50, filter: 'none' },
{ x: 150, y: 50, filter: 'blur' }
]
};
// History for undo/redo
const history = [JSON.stringify(state)];
let historyStep = 0;
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// Create container
const container = document.createElement('div');
container.style.position = 'relative';
document.body.appendChild(container);
// Create button container
const buttonContainer = document.createElement('div');
buttonContainer.style.position = 'absolute';
buttonContainer.style.top = '10px';
buttonContainer.style.left = '10px';
buttonContainer.style.zIndex = '10';
container.appendChild(buttonContainer);
// Create UI buttons
const addButton = document.createElement('button');
addButton.textContent = 'Add Image';
addButton.style.margin = '0 5px';
buttonContainer.appendChild(addButton);
const undoButton = document.createElement('button');
undoButton.textContent = 'Undo';
undoButton.style.margin = '0 5px';
buttonContainer.appendChild(undoButton);
const redoButton = document.createElement('button');
redoButton.textContent = 'Redo';
redoButton.style.margin = '0 5px';
buttonContainer.appendChild(redoButton);
// Move stage container into our container
const stageContainer = document.getElementById('container');
container.appendChild(stageContainer);
stageContainer.style.position = 'absolute';
stageContainer.style.top = '0';
stageContainer.style.left = '0';
// Load image
const imageObj = new Image();
imageObj.src = 'https://konvajs.org/assets/lion.png';
function createImage(imageConfig) {
const image = new Konva.Image({
image: imageObj,
x: imageConfig.x,
y: imageConfig.y,
width: 100,
height: 100,
draggable: true
});
if (imageConfig.filter === 'blur') {
image.cache();
image.filters([Konva.Filters.Blur]);
image.blurRadius(10);
}
return image;
}
function create(state) {
layer.destroyChildren();
state.images.forEach(imgConfig => {
const image = createImage(imgConfig);
image.on('dragend', () => {
const pos = image.position();
const index = layer.children.indexOf(image);
state.images[index] = {
...state.images[index],
x: pos.x,
y: pos.y
};
saveHistory();
});
image.on('click', () => {
const index = layer.children.indexOf(image);
state.images[index] = {
...state.images[index],
filter: state.images[index].filter === 'none' ? 'blur' : 'none'
};
saveHistory();
create(state);
});
layer.add(image);
});
}
function saveHistory() {
historyStep++;
history.length = historyStep;
history.push(JSON.stringify(state));
}
// Add event listeners
addButton.addEventListener('click', () => {
state.images.push({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
filter: 'none'
});
saveHistory();
create(state);
});
undoButton.addEventListener('click', () => {
if (historyStep === 0) return;
historyStep--;
state = JSON.parse(history[historyStep]);
create(state);
});
redoButton.addEventListener('click', () => {
if (historyStep === history.length - 1) return;
historyStep++;
state = JSON.parse(history[historyStep]);
create(state);
});
imageObj.onload = () => {
create(state);
};
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const FilteredImage = ({ filter, ...props }) => {
const imageRef = useRef();
useEffect(() => {
imageRef.current?.cache();
}, [props.image]);
return (
);
};
const App = () => {
const [images, setImages] = useState([
{ x: 50, y: 50, filter: 'none' },
{ x: 150, y: 50, filter: 'blur' }
]);
const [history, setHistory] = useState([]);
const [historyStep, setHistoryStep] = useState(0);
const [lionImage] = useImage('https://konvajs.org/assets/lion.png', 'anonymous');
useEffect(() => {
if (lionImage) {
setHistory([JSON.stringify(images)]);
}
}, [lionImage]);
const handleDragEnd = (index, e) => {
const newImages = [...images];
newImages[index] = {
...newImages[index],
x: e.target.x(),
y: e.target.y()
};
setImages(newImages);
saveHistory(newImages);
};
const handleClick = (index) => {
const newImages = [...images];
newImages[index] = {
...newImages[index],
filter: newImages[index].filter === 'none' ? 'blur' : 'none'
};
setImages(newImages);
saveHistory(newImages);
};
const saveHistory = (newImages) => {
const newHistory = history.slice(0, historyStep + 1);
newHistory.push(JSON.stringify(newImages));
setHistory(newHistory);
setHistoryStep(newHistory.length - 1);
};
const handleAdd = () => {
const newImages = [...images, {
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
filter: 'none'
}];
setImages(newImages);
saveHistory(newImages);
};
const handleUndo = () => {
if (historyStep === 0) return;
const newStep = historyStep - 1;
setHistoryStep(newStep);
setImages(JSON.parse(history[newStep]));
};
const handleRedo = () => {
if (historyStep === history.length - 1) return;
const newStep = historyStep + 1;
setHistoryStep(newStep);
setImages(JSON.parse(history[newStep]));
};
return (
Add Image
Undo
Redo
{lionImage && images.map((img, i) => (
handleDragEnd(i, e)}
onClick={() => handleClick(i)}
/>
))}
);
};
export default App;
```
```js
```
---
# Load HTML5 Canvas Stage from JSON Tutorial
> Load a Konva stage from JSON with images and event bindings using Konva.Node.create and find selectors.
Source: https://konvajs.org/docs/data_and_serialization/Complex_Load.html
To load a complex stage that originally contained images and event bindings using Konva,
we need to create a stage node using `Konva.Node.create()`, and then set the
images and event handlers with the help of selectors using the `find()` method.
Images and event handlers must be manually set because they aren't serializable.
**That methods works for small apps. For more complex cases take a look into [Best Practices](/docs/data_and_serialization/Best_Practices.html)**
```js
import Konva from 'konva';
// JSON string from a previous save
const json = '{"attrs":{"width":578,"height":200},"className":"Stage","children":[{"attrs":{},"className":"Layer","children":[{"attrs":{"x":100,"y":100,"sides":6,"radius":70,"fill":"red","stroke":"black","strokeWidth":4},"className":"RegularPolygon"}]}]}';
// create node using json string
const stage = Konva.Node.create(json, 'container');
// get reference to the hexagon
const hexagon = stage.findOne('RegularPolygon');
// bind events
hexagon.on('click', () => {
hexagon.fill(Konva.Util.getRandomColor());
});
```
**Note:** Using `Konva.Node.create()` directly in React is an anti-pattern. In React applications, we should manage state separately from the view. Instead of deserializing entire node structures, we should load the data that defines our shapes and let React components handle the rendering. The example below demonstrates how to load shape data as state in React:
```js
import { Stage, Layer, RegularPolygon } from 'react-konva';
import { useState, useEffect } from 'react';
import Konva from 'konva';
const App = () => {
const [shapeData, setShapeData] = useState(null);
useEffect(() => {
// Simulating loading JSON data from storage or API
const loadData = () => {
// This would typically come from localStorage, API, etc.
const jsonString = '{"hexagon":{"x":100,"y":100,"sides":6,"radius":70,"fill":"red","stroke":"black","strokeWidth":4}}';
try {
// Parse the JSON into a JavaScript object
const data = JSON.parse(jsonString);
setShapeData(data);
} catch (error) {
console.error('Error parsing JSON:', error);
}
};
loadData();
}, []);
const handleClick = () => {
if (shapeData) {
setShapeData({
...shapeData,
hexagon: {
...shapeData.hexagon,
fill: Konva.Util.getRandomColor()
}
});
}
};
// Don't render until we have data
if (!shapeData) return Loading...
;
return (
);
};
export default App;
```
**Note:** Using `Konva.Node.create()` directly in Vue is an anti-pattern. In Vue applications, we should manage state with reactive data separately from the view. Instead of deserializing entire node structures, we should load the data that defines our shapes and let Vue components handle the rendering. The example below demonstrates how to load shape data as reactive state in Vue:
```js
```
---
# HTML5 Canvas Export to High Quality Image Tutorial
> Learn how to export HTML5 Canvas to high-quality PNG or JPEG images with Konva.js. Use stage.toDataURL() with pixelRatio for retina-quality exports.
Source: https://konvajs.org/docs/data_and_serialization/High-Quality-Export.html
If you need to export a stage as an image or as base64 then you can use the `stage.toDataURL()` or `stage.toImage()` methods.
By default in `Konva`, exported images have the `pixelRatio` attribute set to `1`. This means that if you export a stage with a size of `500x500`, then the exported image will have the same size of `500x500`.
In some cases you may want to export an image that is more suited to higher (or even smaller) resolutions. For instance, you may wish to export something as an image and then use that image on a canvas on HDPI devices (with a high pixel ratio, like a retina display). Another scenario may be that you need to export a user's drawing onto a computer running a high resolution.
If you were to do this with the default settings, then you would see a blurred image. You can read more about the global `pixelRatio` attribute here [MDN - devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio).
For both of these use cases, you can use:
```javascript
stage.toDataURL({
pixelRatio: 2 // or other value you need
})
```
Now, a stage with a size of `500x500` would be exported as an image with a size of `1000x1000`. Almost all nodes in `Konva` are stored as vector data, apart from bitmap images and cached nodes. This results in a high quality exported image.
**Instructions:** try to save stage as an image. You will see that it has a high resolution.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: 400,
height: 400
});
const layer = new Konva.Layer();
stage.add(layer);
// create some shapes
const circle = new Konva.Circle({
x: 200,
y: 200,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
const text = new Konva.Text({
x: 150,
y: 190,
text: 'High Quality Export',
fontSize: 20,
fill: 'white'
});
layer.add(circle);
layer.add(text);
// add button
const button = document.createElement('button');
button.textContent = 'Save as High Quality Image';
document.body.appendChild(button);
button.addEventListener('click', () => {
// save stage as a high quality image
const dataURL = stage.toDataURL({
pixelRatio: 2 // double resolution
});
// create link to download
const link = document.createElement('a');
link.download = 'stage.png';
link.href = dataURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
```
```js
import { Stage, Layer, Circle, Text } from 'react-konva';
import { useRef } from 'react';
const App = () => {
const stageRef = useRef(null);
const handleExport = () => {
const dataURL = stageRef.current.toDataURL({
pixelRatio: 2 // double resolution
});
const link = document.createElement('a');
link.download = 'stage.png';
link.href = dataURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
return (
Save as High Quality Image
);
};
export default App;
```
```js
Save as High Quality Image
```
## The browser size limit
Every browser caps canvas width, height, and total area, and the cap is lower on
mobile. Past that point a large `pixelRatio` fails quietly: the call returns a
blank or truncated image instead of raising an error. Large-format output has to
be tiled, or rendered outside the browser.
---
# Load Simple HTML5 Canvas Stage from JSON Tutorial
> Learn how to load an HTML5 Canvas stage from JSON with Konva.js. Restore saved canvas state using Konva.Node.create().
Source: https://konvajs.org/docs/data_and_serialization/Simple_Load.html
To load a simple stage from JSON with Konva, we can use the `Konva.Node.create()` method.
The `create()` method accepts a JSON string and container id as arguments.
```js
import Konva from 'konva';
// JSON string from a previous save
const json = '{"attrs":{"width":400,"height":400},"className":"Stage","children":[{"attrs":{},"className":"Layer","children":[{"attrs":{"x":100,"y":100,"radius":50,"fill":"red","stroke":"black","strokeWidth":3},"className":"Circle"}]}]}';
// create node using json string
const stage = Konva.Node.create(json, 'container');
// you can keep adding events, etc
const circle = stage.findOne('Circle');
circle.on('click', () => {
circle.fill(Konva.Util.getRandomColor());
});
```
**Note:** Using `Konva.Node.create()` directly in React or Vue is an anti-pattern. In these frameworks, we should manage state (data) separately from the view (components). Instead of serializing and loading entire node structures, we should save and load the data that defines our shapes, then let the framework components handle rendering. This approach is more aligned with React and Vue's declarative, state-driven patterns and provides better control over component lifecycle and events.
```js
import { Stage, Layer, Circle } from 'react-konva';
import { useState, useEffect } from 'react';
import Konva from 'konva';
const App = () => {
// In React, we store shape data as state instead of using Konva.Node.create()
const [shapeData, setShapeData] = useState(null);
useEffect(() => {
// Simulating loading JSON data from storage or API
const savedShapeData = {
circle: {
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 3
},
// We could have more shapes here
};
// In a real app, this might be:
// fetch('/api/shapes').then(response => response.json()).then(setShapeData)
setShapeData(savedShapeData);
}, []);
const handleCircleClick = () => {
setShapeData({
...shapeData,
circle: {
...shapeData.circle,
fill: Konva.Util.getRandomColor()
}
});
};
// Don't render until we have data
if (!shapeData) return Loading...
;
return (
);
};
export default App;
```
**Note:** Using `Konva.Node.create()` directly in React or Vue is an anti-pattern. In these frameworks, we should manage state (data) separately from the view (components). Instead of serializing and loading entire node structures, we should save and load the data that defines our shapes, then let the framework components handle rendering. This approach is more aligned with React and Vue's declarative, state-driven patterns and provides better control over component lifecycle and events.
```js
Loading...
```
---
# Canvas Screenshot — Export HTML5 Canvas as Image with JavaScript
> Take a screenshot of your HTML5 Canvas and export it as PNG or JPEG with JavaScript. Use Konva.js toDataURL() to capture canvas content as a base64 image or downloadable file.
Source: https://konvajs.org/docs/data_and_serialization/Stage_Data_URL.html
To take a screenshot of your canvas and export it as an image with `Konva`, use the `toDataURL()`
method. It returns the data URL directly for every node type, including `Stage`.
You can pass in a mime type such as image/jpeg and a quality value that ranges between 0 and 1.
You can also capture screenshots of specific nodes, including layers, groups, and shapes.
*Note: The `toDataURL()` method requires that any images drawn onto the canvas
are hosted on a web server with the same domain as the code executing it.
If this condition is not met, a SECURITY_ERR exception is thrown.*
**Instructions:** Drag and drop the rectangle and then click on the save button to get the composite data url and open the resulting image in a new window.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: 400,
height: 400
});
const layer = new Konva.Layer();
stage.add(layer);
// create draggable rectangle
const rect = new Konva.Rect({
x: 100,
y: 100,
width: 100,
height: 100,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
layer.add(rect);
// add button
const button = document.createElement('button');
button.textContent = 'Save as Image';
document.body.appendChild(button);
button.addEventListener('click', () => {
// get data URL with default settings
const dataURL = stage.toDataURL();
// open in new window
const win = window.open();
win.document.write(` `);
// you can also save with different settings
const jpegURL = stage.toDataURL({
mimeType: 'image/jpeg',
quality: 0.8
});
console.log('JPEG URL:', jpegURL);
});
```
```js
import { Stage, Layer, Rect } from 'react-konva';
import { useRef, useState } from 'react';
const App = () => {
const stageRef = useRef(null);
const [position, setPosition] = useState({ x: 100, y: 100 });
const handleExport = () => {
// get data URL with default settings
const dataURL = stageRef.current.toDataURL();
// open in new window
const win = window.open();
win.document.write(` `);
// you can also save with different settings
const jpegURL = stageRef.current.toDataURL({
mimeType: 'image/jpeg',
quality: 0.8
});
console.log('JPEG URL:', jpegURL);
};
const handleDragEnd = (e) => {
setPosition({
x: e.target.x(),
y: e.target.y()
});
};
return (
Save as Image
);
};
export default App;
```
```js
Save as Image
```
---
# How to support and donate to Konva project?
> Support Konva development through Patreon, Open Collective, or GitHub Sponsors to help maintain the framework.
Source: https://konvajs.org/docs/donate.html
Hello, my name is Anton. I am core maintainer of `Konva` framework.
**If you want to support development of `Konva` and all its ecosystem tools like `react-konva` and `vue-konva` you can use:**
- [Patreon](https://www.patreon.com/lavrton)
- [Open Collective](https://opencollective.com/konva)
- [GitHub Sponsor](https://github.com/sponsors/lavrton)
I am spending a large amount of time to support `Konva` users and develop new versions with bugs fixes and new features.
If you are making money, by using `Konva` in your project, it makes sense to support `Konva` development. By doing this you will make sure that you have a good quality and maintained framework.
Even if you are not making money from your project but `Konva` saved you a lot of time, it will be very kind to support it.
### Your company is using Konva?
It may be hard for many developers to make a financial donation. But if you are using `konva` as part of your work in the company, talk to your managers to support the project. Well-supported project is a good value for the company.
---
# HTML5 Canvas Complex Drag and Drop Bounds
> Learn how to constrain drag and drop movement to custom boundaries and regions in Konva using the dragmove event.
Source: https://konvajs.org/docs/drag_and_drop/Complex_Drag_and_Drop.html
To bound the movement of nodes being dragged and dropped inside regions with
Konva, we can use the `dragmove` event to define boundaries that the node cannot cross.
_Tip: you can use `shape.absolutePosition()` method to get/set absolute position of a node, instead of relative `x` and `y`._
**Instructions:** Drag and drop the light blue rectangle and observe that it
is bound below an imaginary boundary at y = 50. Drag and drop the yellow
rectangle and observe that it is bound inside of an imaginary circle.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const blueGroup = new Konva.Group({
x: 30,
y: 70,
draggable: true,
});
// bound below y=50
blueGroup.on('dragmove', () => {
blueGroup.y(Math.max(blueGroup.y(), 50));
});
// bound inside a circle
const yellowGroup = new Konva.Group({
x: stage.width() / 2,
y: 70,
draggable: true,
});
yellowGroup.on('dragmove', () => {
const x = stage.width() / 2;
const y = 70;
const radius = 50;
const pos = yellowGroup.absolutePosition();
const scale = radius / Math.sqrt(Math.pow(pos.x - x, 2) + Math.pow(pos.y - y, 2));
if (scale < 1) {
yellowGroup.x(Math.round((pos.x - x) * scale + x));
yellowGroup.y(Math.round((pos.y - y) * scale + y));
}
});
const blueText = new Konva.Text({
fontSize: 26,
fontFamily: 'Calibri',
text: 'bound below',
fill: 'black',
padding: 10,
width: 150,
align: 'center',
});
const blueRect = new Konva.Rect({
width: 150,
height: 72,
fill: '#aaf',
stroke: 'black',
strokeWidth: 4,
});
const yellowText = new Konva.Text({
fontSize: 26,
fontFamily: 'Calibri',
text: 'bound in circle',
fill: 'black',
padding: 10,
width: 150,
align: 'center',
});
const yellowRect = new Konva.Rect({
width: 150,
height:72,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
});
blueGroup.add(blueRect).add(blueText);
yellowGroup.add(yellowRect).add(yellowText);
layer.add(blueGroup);
layer.add(yellowGroup);
stage.add(layer);
````
```jsx
import { Stage, Layer, Group, Rect, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [bluePosition, setBluePosition] = useState({ x: 30, y: 70 });
const [yellowPosition, setYellowPosition] = useState({
x: window.innerWidth / 2,
y: 70,
});
const handleBlueDragMove = (e) => {
setBluePosition({
x: e.target.x(),
y: Math.max(e.target.y(), 50),
});
};
const handleYellowDragMove = (e) => {
const x = window.innerWidth / 2;
const y = 70;
const radius = 50;
const position = e.target.absolutePosition();
const scale =
radius /
Math.sqrt(
Math.pow(position.x - x, 2) + Math.pow(position.y - y, 2)
);
if (scale < 1) {
setYellowPosition({
x: Math.round((position.x - x) * scale + x),
y: Math.round((position.y - y) * scale + y),
});
} else {
setYellowPosition(position);
}
};
return (
);
};
export default App;
````
```vue
```
---
# HTML5 Canvas Drag and Drop Events
> Learn how to handle drag events on HTML5 Canvas with Konva.js. Use dragstart, dragmove, and dragend events to respond to shape dragging.
Source: https://konvajs.org/docs/drag_and_drop/Drag_Events.html
To detect drag and drop events with Konva, we can use the `on()` method to
bind `dragstart`, `dragmove`, or `dragend` events to a node.
The `on()` method requires an event type and a function to be executed when the event occurs.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 40,
y: 40,
text: 'Draggable Text',
fontSize: 20,
draggable: true,
width: 200,
});
layer.add(text);
const status = new Konva.Text({
x: 40,
y: 100,
text: '',
fontSize: 16,
width: 200,
});
layer.add(status);
text.on('dragstart', () => {
status.text('drag started');
});
text.on('dragend', () => {
status.text('drag ended');
});
text.on('dragmove', () => {
status.text('dragging');
});
```
```jsx
import { Stage, Layer, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [position, setPosition] = useState({ x: 40, y: 40 });
const [status, setStatus] = useState('');
const handleDrag = (e, nextStatus) => {
setPosition({ x: e.target.x(), y: e.target.y() });
setStatus(nextStatus);
};
return (
setStatus('drag started')}
onDragEnd={(e) => handleDrag(e, 'drag ended')}
onDragMove={(e) => handleDrag(e, 'dragging')}
/>
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Drag and Drop a Group Tutorial
> Learn how to drag and drop a group of shapes together on the HTML5 canvas using Konva's draggable property.
Source: https://konvajs.org/docs/drag_and_drop/Drag_a_Group.html
To drag and drop groups with Konva, we can set the `draggable` property
of the config object to `true` when the group is instantiated, or we can use the `draggable()` method.
Note: remember, dragging a group, do not change the `x` and `y` properties of any of the children nodes. Instead properties of group itself are changed.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const group = new Konva.Group({
draggable: true,
});
layer.add(group);
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'];
for (let i = 0; i < 6; i++) {
const box = new Konva.Rect({
x: i * 30 + 10,
y: i * 18 + 40,
width: 100,
height: 50,
name: colors[i],
fill: colors[i],
stroke: 'black',
strokeWidth: 4,
});
group.add(box);
}
group.on('mouseover', function () {
document.body.style.cursor = 'move';
});
group.on('mouseout', function () {
document.body.style.cursor = 'default';
});
```
```jsx
import { Stage, Layer, Group, Rect, Text } from 'react-konva';
const App = () => {
const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple'];
const handleMouseOver = () => {
document.body.style.cursor = 'move';
};
const handleMouseOut = () => {
document.body.style.cursor = 'default';
};
return (
{colors.map((color, i) => (
))}
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Drag and Drop a Line
> Learn how to drag and drop a line on the HTML5 canvas with Konva by setting the draggable property to true.
Source: https://konvajs.org/docs/drag_and_drop/Drag_a_Line.html
To drag and drop a line with Konva, we can set the `draggable` property
of the config object to `true` when the line is instantiated, or we can use the `draggable()` method.
**Note: (!) dragging a line will NOT change the `points` property. Instead `x` and `y` properties of the line are changed.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const redLine = new Konva.Line({
x: 50,
y: 50,
points: [0, 0, 150, 0],
stroke: 'red',
strokeWidth: 15,
lineCap: 'round',
lineJoin: 'round',
draggable: true,
});
// add cursor styling
redLine.on('mouseover', function () {
document.body.style.cursor = 'pointer';
});
redLine.on('mouseout', function () {
document.body.style.cursor = 'default';
});
layer.add(redLine);
stage.add(layer);
```
```jsx
import { Stage, Layer, Line } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
return (
{
setPosition({
x: e.target.x(),
y: e.target.y(),
});
}}
onMouseEnter={(e) => {
document.body.style.cursor = 'pointer';
}}
onMouseLeave={(e) => {
document.body.style.cursor = 'default';
}}
/>
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Drag and Drop the Stage
> Learn how to make the entire Konva stage draggable, allowing users to pan the canvas by dragging any area.
Source: https://konvajs.org/docs/drag_and_drop/Drag_a_Stage.html
To drag and drop a stage with Konva, we can set the `draggable` property
of the config object to `true` when the group is instantiated, or we can use the `draggable()` method.
Unlike drag and drop for other nodes, such as shapes, groups, and layers,
we can drag the entire stage by dragging any portion of the stage.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
draggable: true
});
const layer = new Konva.Layer();
stage.add(layer);
// create circle
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
// create text
const text = new Konva.Text({
x: 10,
y: 10,
text: 'Drag the stage anywhere',
fontSize: 20,
fontFamily: 'Calibri',
fill: 'black'
});
layer.add(circle);
layer.add(text);
```
```jsx
import { Stage, Layer, Circle, Text } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Drag and Drop an Image
> Learn how to drag and drop an image on the HTML5 canvas with Konva by setting the draggable property to true.
Source: https://konvajs.org/docs/drag_and_drop/Drag_an_Image.html
To drag and drop an image with Konva, we can set the `draggable` property
to true when we instantiate a shape, or we can use the `draggable()` method.
The `draggable()` method enables drag and drop for both desktop and mobile
applications automatically.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const imageObj = new Image();
imageObj.onload = () => {
const yoda = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
width: 106,
height: 118,
draggable: true,
});
// add cursor styling
yoda.on('mouseover', function () {
document.body.style.cursor = 'pointer';
});
yoda.on('mouseout', function () {
document.body.style.cursor = 'default';
});
layer.add(yoda);
};
imageObj.src = 'https://konvajs.org/assets/yoda.jpg';
stage.add(layer);
```
```jsx
import { Stage, Layer, Image } from 'react-konva';
import { useState } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [yodaImage] = useImage('https://konvajs.org/assets/yoda.jpg');
return (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
onMouseEnter={(e) => {
document.body.style.cursor = 'pointer';
}}
onMouseLeave={(e) => {
document.body.style.cursor = 'default';
}}
/>
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Drop Events
> Learn how to implement drop events on HTML5 Canvas with Konva.js. Detect when a dragged shape is dropped onto a target zone.
Source: https://konvajs.org/docs/drag_and_drop/Drop_Events.html
Konva does not support drop events. But you can write your own drop events detections.
To detect drop target shape you have to move dragging object into another layer.
In this example you can see implementation of `drop`, `dragenter`, `dragleave`, `dragover` events.
**Instructions:** drag one shape over another. Or drag and drop one shape into another.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const tempLayer = new Konva.Layer();
stage.add(layer);
stage.add(tempLayer);
const text = new Konva.Text({
fill: 'black',
});
layer.add(text);
let previousShape;
// create multiple stars
for (let i = 0; i < 10; i++) {
const star = new Konva.Star({
x: stage.width() * Math.random(),
y: stage.height() * Math.random(),
fill: 'blue',
numPoints: 10,
innerRadius: 20,
outerRadius: 25,
draggable: true,
name: 'star ' + i,
shadowOffsetX: 5,
shadowOffsetY: 5,
});
star.on('dragstart', () => {
star.moveTo(tempLayer);
text.text('Moving ' + star.name());
});
star.on('dragmove', (e) => {
const pos = stage.getPointerPosition();
const shape = layer.getIntersection(pos);
if (previousShape && shape) {
if (previousShape !== shape) {
// leave from old target
previousShape.fire('dragleave', { evt: e.evt }, true);
// enter new target
shape.fire('dragenter', { evt: e.evt }, true);
previousShape = shape;
} else {
previousShape.fire('dragover', { evt: e.evt }, true);
}
} else if (!previousShape && shape) {
previousShape = shape;
shape.fire('dragenter', { evt: e.evt }, true);
} else if (previousShape && !shape) {
previousShape.fire('dragleave', { evt: e.evt }, true);
previousShape = undefined;
}
});
star.on('dragend', (e) => {
const pos = stage.getPointerPosition();
const shape = layer.getIntersection(pos);
if (previousShape && previousShape !== shape) {
previousShape.fire('dragleave', { evt: e.evt }, true);
}
if (shape) {
shape.fire('drop', { evt: e.evt }, true);
}
previousShape = undefined;
star.moveTo(layer);
});
star.on('dragenter', () => {
star.fill('green');
text.text('dragenter ' + star.name());
});
star.on('dragleave', () => {
star.fill('blue');
text.text('dragleave ' + star.name());
});
star.on('dragover', () => {
text.text('dragover ' + star.name());
});
star.on('drop', () => {
star.fill('red');
text.text('drop ' + star.name());
});
layer.add(star);
}
```
```jsx
import { Stage, Layer, Text, Star } from 'react-konva';
import { useState, useRef } from 'react';
const App = () => {
const [stars, setStars] = useState(() =>
Array.from({ length: 10 }, (_, i) => ({
id: i,
x: window.innerWidth * Math.random(),
y: window.innerHeight * Math.random(),
fill: 'blue',
name: `star ${i}`,
}))
);
const [message, setMessage] = useState('');
const previousShapeRef = useRef(null);
const mainLayerRef = useRef(null);
const tempLayerRef = useRef(null);
const updateStar = (id, attrs) => {
setStars((currentStars) =>
currentStars.map((star) =>
star.id === id ? { ...star, ...attrs } : star
)
);
};
const handleDragStart = (id, e) => {
const shape = e.target;
updateStar(id, { x: shape.x(), y: shape.y() });
shape.moveTo(tempLayerRef.current);
setMessage('Moving ' + shape.name());
};
const handleDragMove = (id, e) => {
const stage = e.target.getStage();
const pos = stage.getPointerPosition();
const shape = mainLayerRef.current.getIntersection(pos);
updateStar(id, { x: e.target.x(), y: e.target.y() });
if (previousShapeRef.current && shape) {
if (previousShapeRef.current !== shape) {
// leave from old target
previousShapeRef.current.fire('dragleave', { evt: e.evt }, true);
// enter new target
shape.fire('dragenter', { evt: e.evt }, true);
previousShapeRef.current = shape;
} else {
previousShapeRef.current.fire('dragover', { evt: e.evt }, true);
}
} else if (!previousShapeRef.current && shape) {
previousShapeRef.current = shape;
shape.fire('dragenter', { evt: e.evt }, true);
} else if (previousShapeRef.current && !shape) {
previousShapeRef.current.fire('dragleave', { evt: e.evt }, true);
previousShapeRef.current = undefined;
}
};
const handleDragEnd = (id, e) => {
const shape = e.target;
const stage = e.target.getStage();
const pos = stage.getPointerPosition();
const dropShape = mainLayerRef.current.getIntersection(pos);
if (
previousShapeRef.current &&
previousShapeRef.current !== dropShape
) {
previousShapeRef.current.fire('dragleave', { evt: e.evt }, true);
}
if (dropShape) {
dropShape.fire('drop', { evt: e.evt }, true);
}
updateStar(id, { x: shape.x(), y: shape.y() });
shape.moveTo(mainLayerRef.current);
previousShapeRef.current = undefined;
};
const handleDragEnter = (id, e) => {
updateStar(id, { fill: 'green' });
setMessage('dragenter ' + e.target.name());
};
const handleDragLeave = (id, e) => {
updateStar(id, { fill: 'blue' });
setMessage('dragleave ' + e.target.name());
};
const handleDragOver = (e) => {
setMessage('dragover ' + e.target.name());
};
const handleDrop = (id, e) => {
updateStar(id, { fill: 'red' });
setMessage('drop ' + e.target.name());
};
return (
{stars.map((star) => (
handleDragStart(star.id, e)}
onDragMove={(e) => handleDragMove(star.id, e)}
onDragEnd={(e) => handleDragEnd(star.id, e)}
onDragEnter={(e) => handleDragEnter(star.id, e)}
onDragLeave={(e) => handleDragLeave(star.id, e)}
onDragOver={handleDragOver}
onDrop={(e) => handleDrop(star.id, e)}
/>
))}
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Simple Drag Bounds Tutorial
> Learn how to constrain drag movement on HTML5 Canvas with Konva.js. Set drag boundaries to limit where shapes can be dragged.
Source: https://konvajs.org/docs/drag_and_drop/Simple_Drag_Bounds.html
To restrict the movement of shapes being dragged and dropped with Konva,
we can use the `dragmove` event and overrides the drag and drop position inside of it.
This event can be used to constrain the drag and drop movement in all kinds of ways, such as constraining the motion horizontally, vertically, diagonally, or radially, or even constrain the node
to stay inside of a box, circle, or any other path.
```js
shape.on('dragmove', () => {
// lock position of the shape on x axis
// keep y position as is
shape.x(0);
});
```
_Tip: you can use `shape.absolutePosition()` method to get/set absolute position of a node, instead of relative `x` and `y`._
**Instructions:** Drag and drop the the horizontal text and observe that it can only
move horizontally. Drag and drop the vertical text and observe that it can only move vertically.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const horizontalText = new Konva.Text({
x: 50,
y: 50,
text: 'Drag me horizontally',
fontSize: 16,
draggable: true,
fill: 'black',
});
horizontalText.on('dragmove', function () {
// horizontal only
this.y(50);
});
const verticalText = new Konva.Text({
x: 200,
y: 50,
text: 'Drag me vertically',
fontSize: 16,
draggable: true,
fill: 'black',
});
verticalText.on('dragmove', function () {
// vertical only
this.x(200);
});
layer.add(horizontalText);
layer.add(verticalText);
```
```jsx
import { Stage, Layer, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [horizontalPosition, setHorizontalPosition] = useState({ x: 50, y: 50 });
const [verticalPosition, setVerticalPosition] = useState({ x: 200, y: 50 });
const handleHorizontalDragMove = (e) => {
setHorizontalPosition({ x: e.target.x(), y: 50 });
};
const handleVerticalDragMove = (e) => {
setVerticalPosition({ x: 200, y: e.target.y() });
};
return (
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Cancel Event Bubble Propagation with Konva
> Learn how to cancel event bubble propagation in Konva by setting cancelBubble to true on the event object.
Source: https://konvajs.org/docs/events/Cancel_Propagation.html
To cancel event bubble propagation with Konva, we can set the `cancelBubble`
property of the Event object to true.
**Instructions: Click on the circle to observe that only the circle event binding
is handled because the event propagation was canceled when the circle event was triggered,
therefore preventing the event object from bubbling upwards.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
circle.on('click', function (evt) {
alert('You clicked on the circle');
// stop event bubble
evt.cancelBubble = true;
});
layer.on('click', function () {
alert('You clicked on the layer');
});
layer.add(circle);
```
```jsx
import { Stage, Layer, Circle } from 'react-konva';
const App = () => {
const handleCircleClick = (e) => {
alert('You clicked on the circle');
// stop event bubble
e.cancelBubble = true;
};
const handleLayerClick = () => {
alert('You clicked on the layer');
};
return (
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Custom Hit Detection Function Tutorial
> Learn how to define custom hit detection regions for HTML5 Canvas shapes with Konva.js. Override default hit areas with hitFunc for precise click detection.
Source: https://konvajs.org/docs/events/Custom_Hit_Region.html
There are two ways to change hit region of the shape: `hitFunc` and `hitStrokeWidth` properties.
## 1. What is `hitFunc`?
To create a custom hit draw function for a shape with Konva, we can set
the `hitFunc` property. A hit draw function is the function that Konva
will use to draw a region used for hit detection. Using a custom draw hit
function can have several benefits, such as making the hit region larger
so that it's easier for users to interact with a shape, making some portions
of a shape detectable and others not, or simplifying the hit draw function
in order to improve rendering performance.
Also take a look into some [best practices](/docs/shapes/Custom.html) of writing custom `sceneFunc` that can be used for `hitFunc` too.
`hitFunc` is a function with two arguments: [Konva.Context](/api/Konva.Context.html) renderer and a shape instance.
## 2. What is `hitStrokeWidth`?
For some shapes, like `Konva.Line` it is too hard to overwrite `hitFunc`. In some cases you just want to make it thicker for events. In this case it is better to use `hitStrokeWidth` property with a large value.
**Instructions: Mouseover, mouseout, mousedown, and mouseup over the star and
observe that the hit region is an over sized circle encompassing the shape. Also try the same for a line.
Also you can toggle hit canvas to see how it looks. It may be useful for debugging.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 10,
y: 10,
text: '',
fontSize: 24,
});
layer.add(text);
const star = new Konva.Star({
x: stage.width() / 4,
y: stage.height() / 2,
numPoints: 5,
innerRadius: 40,
outerRadius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
// custom hit function
star.hitFunc(function (context) {
context.beginPath();
context.arc(0, 0, 70, 0, Math.PI * 2, true);
context.closePath();
context.fillStrokeShape(this);
});
const line = new Konva.Line({
x: stage.width() * 0.6,
y: stage.height() / 2,
points: [-50, -50, 50, 50],
stroke: 'black',
strokeWidth: 2,
hitStrokeWidth: 20,
});
const button = document.createElement('button');
button.innerHTML = 'Toggle hit canvas';
document.body.appendChild(button);
let showHit = false;
button.addEventListener('click', () => {
showHit = !showHit;
if (showHit) {
stage.container().style.border = '2px solid black';
stage.container().style.height = stage.height() + 'px';
stage.container().appendChild(layer.hitCanvas._canvas);
layer.hitCanvas._canvas.style.position = 'absolute';
layer.hitCanvas._canvas.style.top = 0;
layer.hitCanvas._canvas.style.left = 0;
} else {
layer.hitCanvas._canvas.remove();
}
});
function writeMessage(message) {
text.text(message);
}
star.on('mouseover mouseout mousedown mouseup', function (evt) {
writeMessage(evt.type + ' star');
});
line.on('mouseover mouseout mousedown mouseup', function (evt) {
writeMessage(evt.type + ' line');
});
layer.add(star);
layer.add(line);
```
```jsx
import { Stage, Layer, Star, Line, Text } from 'react-konva';
import { useState, useEffect } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const [showHit, setShowHit] = useState(false);
const handleStarEvent = (evt) => {
setMessage(evt.type + ' star');
};
const handleLineEvent = (evt) => {
setMessage(evt.type + ' line');
};
useEffect(() => {
const stage = document.querySelector('.konvajs-content');
if (showHit) {
const hitCanvas = stage.querySelector('canvas:last-child');
stage.style.border = '2px solid black';
hitCanvas.style.position = 'absolute';
hitCanvas.style.top = '0';
hitCanvas.style.left = '0';
}
}, [showHit]);
return (
<>
setShowHit(!showHit)}>Toggle hit canvas
{
context.beginPath();
context.arc(0, 0, 70, 0, Math.PI * 2, true);
context.closePath();
context.fillStrokeShape(shape);
}}
onMouseover={handleStarEvent}
onMouseout={handleStarEvent}
onMousedown={handleStarEvent}
onMouseup={handleStarEvent}
/>
>
);
};
export default App;
```
```html
Toggle hit canvas
```
---
# HTML5 Canvas Desktop and Mobile Events Support Tutorial
> Learn how to handle both desktop and mobile events in Konva using paired event bindings like mousedown/touchstart and mouseup/touchend.
Source: https://konvajs.org/docs/events/Desktop_and_Mobile.html
_Note: this demo may be outdate, because modern browsers support pointer events. And you can use pointer events in Konva too. See [Pointer Events Demo](/docs/events/Pointer_Events.html). But if you prefer not to use pointer events, keep reading..._
To add event handlers to shapes that work for both desktop and mobile applications with Konva, we can use the `on()` method and pass in paired events.
For example, in order for the `mousedown` event to be triggered on desktop and mobile applications, we can use the `"mousedown touchstart"` event pair to cover both mediums.
In order for the `mouseup` event to be triggered on both desktop and mobile applications, we can use the `"mouseup touchend"` event pair.
We can also use the `"dblclick dbltap"` event pair to bind a double click event that works for both desktop and mobile devices.
**Instructions: Mousedown, mouseup, touchstart, or touchend the circle on either a desktop or mobile device to observe the same functionality.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
function writeMessage(message) {
text.text(message);
}
// desktop and mobile events
circle.on('mousedown touchstart', function () {
writeMessage('Mousedown or touchstart');
});
circle.on('mouseup touchend', function () {
writeMessage('Mouseup or touchend');
});
layer.add(circle);
layer.add(text);
stage.add(layer);
```
```jsx
import { Stage, Layer, Circle, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [message, setMessage] = useState('');
return (
setMessage('Mousedown or touchstart')}
onTouchstart={() => setMessage('Mousedown or touchstart')}
onMouseup={() => setMessage('Mouseup or touchend')}
onTouchend={() => setMessage('Mouseup or touchend')}
/>
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Event Delegation with Konva
> Learn how to use event delegation on HTML5 Canvas with Konva.js. Listen for events on layers or groups instead of individual shapes for cleaner code.
Source: https://konvajs.org/docs/events/Event_Delegation.html
To get the event target with Konva, we can access the `target` property
of the Event object. This is particularly useful when using event delegation,
in which we can bind an event handler to a parent node, and listen to events
that occur on its children.
**Instructions: Click on the star and observe that the layer event binding
correctly identifies the shape that was clicked on.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
layer.add(text);
const star = new Konva.Star({
x: stage.width() / 2,
y: stage.height() / 2,
numPoints: 5,
innerRadius: 40,
outerRadius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
layer.add(star);
// add event delegation
layer.on('click', function (evt) {
const shape = evt.target;
text.text('click on ' + shape.getClassName());
});
```
```jsx
import { Stage, Layer, Star, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const handleLayerClick = (e) => {
const shape = e.target;
setMessage('click on ' + shape.getClassName());
};
return (
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Fire Event with Konva
> Learn how to programmatically fire events on shapes in Konva using the fire() method, including custom events.
Source: https://konvajs.org/docs/events/Fire_Events.html
To fire events with Konva, we can use the `fire()` method.
This enables us to programmatically fire events like `click`, `mouseover`,
`mousemove`, etc., and also fire custom events, like foo and bar.
> **Note**: While custom events are possible, it's generally better to use built-in interaction events like `click`, `mouseover`, `mousemove`, etc. Custom events can make code harder to maintain and debug.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
// add shape event listener
circle.on('customEvent', function (evt) {
alert('custom event fired');
});
// add button to trigger custom event
const button = document.createElement('button');
button.innerHTML = 'Fire Custom Event';
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
button.style.zIndex = '1';
document.body.appendChild(button);
button.addEventListener('click', () => {
// fire custom event
circle.fire('customEvent', {
bubbles: true,
});
});
layer.add(circle);
```
```jsx
import { Stage, Layer, Circle } from 'react-konva';
import { useRef } from 'react';
const App = () => {
const circleRef = useRef();
const handleCustomEvent = () => {
alert('custom event fired');
};
const fireCustomEvent = () => {
circleRef.current.fire('customevent', {
bubbles: true,
});
};
return (
<>
Fire Custom Event
>
);
};
export default App;
```
```html
Fire Custom Event
```
---
# HTML5 Canvas Image Events with Konva
> Learn how to handle events on images in HTML5 Canvas with Konva.js. Bind click, mouseover, and touch events to Konva.Image shapes.
Source: https://konvajs.org/docs/events/Image_Events.html
To only detect events for non transparent pixels in an image with Konva, we can use the `drawHitFromCache()` method to generate a more precise image hit region.
By default, events can be triggered for any pixel inside of an image, even if it's transparent. The `drawHitFromCache()` method also accepts an optional callback method to be executed whenever the image hit region has been created.
*Note: The `drawHitFromCache()` method requires that the image is hosted on a web server with the same domain as the code executing it.*
**Instructions: Mouse over the monkey and the lion and observe the mouseover event bindings. Notice that the event is triggered for the monkey if you mouseover any portion of the image, including transparent pixels. Since we created an image hit region for the lion, transparent pixels are ignored, which enables more precise event detection.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
layer.add(text);
function writeMessage(message) {
text.text(message);
}
const imageObj1 = new Image();
imageObj1.onload = () => {
const monkey = new Konva.Image({
x: 120,
y: 50,
image: imageObj1,
width: 200,
height: 200,
});
monkey.on('mouseover', function () {
writeMessage('mouseover monkey (regular image)');
});
monkey.on('mouseout', function () {
writeMessage('');
});
layer.add(monkey);
};
imageObj1.crossOrigin = 'Anonymous';
imageObj1.src = 'https://konvajs.org/assets/monkey.png';
const imageObj2 = new Image();
imageObj2.onload = () => {
const lion = new Konva.Image({
x: 320,
y: 50,
image: imageObj2,
width: 200,
height: 200,
});
// override color detection region
lion.on('mouseover', function () {
writeMessage('mouseover lion (with transparent pixels detection)');
});
lion.on('mouseout', function () {
writeMessage('');
});
layer.add(lion);
lion.cache();
lion.drawHitFromCache();
};
imageObj2.crossOrigin = 'Anonymous';
imageObj2.src = 'https://konvajs.org/assets/lion.png';
```
```jsx
import { Stage, Layer, Image, Text } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [message, setMessage] = useState('');
const lionRef = useRef();
const [monkeyImage] = useImage('https://konvajs.org/assets/monkey.png', 'anonymous');
const [lionImage] = useImage('https://konvajs.org/assets/lion.png', 'anonymous');
useEffect(() => {
if (lionImage) {
lionRef.current.cache();
lionRef.current.drawHitFromCache();
}
}, [lionImage]);
return (
{monkeyImage && (
setMessage('mouseover monkey (regular image)')}
onMouseout={() => setMessage('')}
/>
)}
{lionImage && (
setMessage('mouseover lion (with transparent pixels detection)')
}
onMouseout={() => setMessage('')}
/>
)}
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Keyboard events with Konva
> Learn how to handle keyboard events on HTML5 Canvas shapes with Konva.js. Implement keyboard shortcuts, arrow key movement, and key-based interactions.
Source: https://konvajs.org/docs/events/Keyboard_Events.html
There are no built-in keyboard events like `keydown` or `keyup` in Konva.
### But how to listen keydown or keyup events on canvas?
You can easily add them by two ways:
1. Listen global events on `window` object
2. Or make stage container focusable with `tabIndex` property and listen events on it.
**Instructions: click on stage to focus it, move a shape with arrows**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
layer.add(circle);
// make stage container focusable
stage.container().tabIndex = 1;
// focus it
// also stage will be in focus on its click
stage.container().focus();
const DELTA = 4;
// add keyboard events
stage.container().addEventListener('keydown', (e) => {
if (e.keyCode === 37) {
circle.x(circle.x() - DELTA);
} else if (e.keyCode === 38) {
circle.y(circle.y() - DELTA);
} else if (e.keyCode === 39) {
circle.x(circle.x() + DELTA);
} else if (e.keyCode === 40) {
circle.y(circle.y() + DELTA);
} else {
return;
}
e.preventDefault();
});
```
```jsx
import { Stage, Layer, Circle } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const stageRef = useRef();
const containerRef = useRef();
const [position, setPosition] = useState({
x: window.innerWidth / 2,
y: window.innerHeight / 2,
});
useEffect(() => {
// focus the div on mount
containerRef.current.focus();
}, []);
const handleKeyDown = (e) => {
const DELTA = 4;
switch (e.keyCode) {
case 37: // left
setPosition(pos => ({ ...pos, x: pos.x - DELTA }));
break;
case 38: // up
setPosition(pos => ({ ...pos, y: pos.y - DELTA }));
break;
case 39: // right
setPosition(pos => ({ ...pos, x: pos.x + DELTA }));
break;
case 40: // down
setPosition(pos => ({ ...pos, y: pos.y + DELTA }));
break;
default:
return;
}
e.preventDefault();
};
return (
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Listen or Don't Listen to Events with Konva
> Learn how to enable or disable event listening on shapes in Konva using the listening property and setListening() method.
Source: https://konvajs.org/docs/events/Listen_for_Events.html
To listen or don't listen to events with Konva, we can set the listening
property of the config object to true or false when a shape is instantiated,
or we can set the listening property with the `setListening()` method.
Once we've set the listening property for one or more nodes, we'll also need
to redraw the hit graph for each affected layer with the `drawHit()` method.
**Instructions: Mouseover the oval to observe that the event handler is not executed.
Click on "Listen" to start listening for events and observe that the event handler is now executed.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
layer.add(text);
const oval = new Konva.Ellipse({
x: stage.width() / 2,
y: stage.height() / 2,
radiusX: 100,
radiusY: 50,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
listening: false,
});
oval.on('mouseover', function () {
writeMessage('Mouseover oval');
});
oval.on('mouseout', function () {
writeMessage('');
});
function writeMessage(message) {
text.text(message);
}
layer.add(oval);
// add button to toggle listening
const button = document.createElement('button');
button.innerHTML = 'Listen';
document.body.appendChild(button);
button.addEventListener('click', () => {
const listening = !oval.listening();
oval.listening(listening);
button.innerHTML = listening ? 'Stop listening' : 'Listen';
layer.drawHit();
});
```
```jsx
import { Stage, Layer, Ellipse, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const [listening, setListening] = useState(false);
return (
<>
setListening(!listening)}>
{listening ? 'Stop listening' : 'Listen'}
setMessage('Mouseover oval')}
onMouseout={() => setMessage('')}
/>
>
);
};
export default App;
```
```html
{{ listening ? 'Stop listening' : 'Listen' }}
```
---
# HTML5 Canvas Mobile Touch Events Tutorial
> Learn how to handle touch events on HTML5 Canvas with Konva.js on mobile devices. Support tap, touchstart, touchmove, touchend, and multi-touch gestures.
Source: https://konvajs.org/docs/events/Mobile_Events.html
To bind event handlers to shapes on a mobile device with Konva, we can use the `on()` method.
The `on()` method requires an event type and a function to be executed when the event occurs.
Konva supports `touchstart`, `touchmove`, `touchend`, `tap`, `dbltap`, `dragstart`, `dragmove`, and `dragend` mobile events.
For more complex gestures like `rotate` take a look into [Gestures Demo](/docs/sandbox/Gestures.html).
If you are looking for pan and zoom logic for the whole stage take a look into [Multi-touch scale Stage demo](/docs/sandbox/Multi-touch_Scale_Stage.html).
*Note: This example only works on mobile devices because it makes use of touch events rather than mouse events.*
**Instructions: move your finger across the triangle to see touch coordinates and touch start and touch end the circle.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
const triangle = new Konva.RegularPolygon({
x: 80,
y: 120,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
});
const circle = new Konva.Circle({
x: 230,
y: 100,
radius: 60,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
function writeMessage(message) {
text.text(message);
}
triangle.on('touchmove', function () {
const touchPos = stage.getPointerPosition();
const x = touchPos.x;
const y = touchPos.y;
writeMessage('x: ' + x + ', y: ' + y);
});
circle.on('touchstart', function () {
writeMessage('touchstart circle');
});
circle.on('touchend', function () {
writeMessage('touchend circle');
});
layer.add(triangle);
layer.add(circle);
layer.add(text);
stage.add(layer);
```
```jsx
import { Stage, Layer, RegularPolygon, Circle, Text } from 'react-konva';
import { useState, useRef } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const stageRef = useRef();
const handleTriangleTouch = () => {
const touchPos = stageRef.current.getPointerPosition();
setMessage(`x: ${touchPos.x}, y: ${touchPos.y}`);
};
return (
setMessage('touchstart circle')}
onTouchend={() => setMessage('touchend circle')}
/>
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Mobile Scrolling and Native Events with Konva
> Learn how to control mobile scrolling behavior on Konva stages using the preventDefault property on shapes.
Source: https://konvajs.org/docs/events/Mobile_Scrolling.html
By default `Konva` will prevent default behaviour of all pointer interactions with a stage.
That will prevent unexpected scrolling of a page when you are trying to drag&drop a shape on a mobile device.
But in some cases you may want to keep default behaviour of browser events. In that case you may set `preventDefault` property of a shape to `false`.
**Instructions: if you are on mobile device try to scroll a page by each rectangle.
Green - should prevent default behaviour (no page scrolling).
Red - will keep default behaviour (scrolling should work).**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// green rectangle - will prevent scrolling
const greenRect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 600,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
layer.add(greenRect);
// red rectangle - will NOT prevent scrolling
const redRect = new Konva.Rect({
x: 200,
y: 50,
width: 100,
height: 600,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
preventDefault: false,
});
layer.add(redRect);
```
```jsx
import { Stage, Layer, Rect } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```html
```
---
# Why Your Handler Fires Twice on Mobile, and Click After Drag
> A single tap can fire tap, pointerclick and click in Konva, so a handler bound to 'click tap' runs twice on touch devices. How to bind one event family, and why click already does not fire after a drag.
Source: https://konvajs.org/docs/events/Mobile_Tap_And_Click.html
Two things surprise people about pointer input on canvas. One is a real problem
you have to handle. The other is already handled, and knowing that saves you a
workaround you do not need.
## A tap can fire three events
Konva listens to all three DOM input families — mouse, touch, and pointer — so
that a handler works everywhere without you choosing in advance. It does not
merge them.
On a touchscreen the browser sends a `touchstart`/`touchend` pair, a
`pointerdown`/`pointerup` pair, and then **synthesises** `mousedown`/`mouseup`
for compatibility with pages written before touch existed. Konva turns each of
those into its own event. One tap produces:
| Konva event | Comes from |
| --- | --- |
| `tap` | the touch pair |
| `pointerclick` | the pointer pair |
| `click` | the synthesised mouse pair |
So this runs your handler **twice** on a phone and once on a desktop:
```js
// Fires for `click` and again for `tap` on a touch device.
shape.on('click tap', handleSelect);
```
### Bind one family
The simplest fix is to stop pairing events and use the pointer family, which
already covers mouse, touch, and pen:
```js
shape.on('pointerclick', handleSelect); // once, on every device
```
`pointerdown`, `pointermove` and `pointerup` work the same way. Pointer events
are on by default (`Konva.pointerEventsEnabled`), and there is more in
[Pointer Events](/docs/events/Pointer_Events.html).
If you need to support a browser without pointer events, or you are maintaining
code that already pairs `click tap`, guard the handler instead:
```js
let lastHandled = 0;
function handleSelect(e) {
// A synthesised mouse event follows its touch within about 300ms.
const now = Date.now();
if (now - lastHandled < 400) return;
lastHandled = now;
// ...
}
```
Prefer binding one family. The timer is a workaround, and it will swallow a
genuine second tap from a fast user.
## Click already does not fire after a drag
This one is worth knowing because the usual workaround is unnecessary.
When a drag actually starts, Konva clears the flag that allows a click to be
dispatched, so `dragend` is **not** followed by `click`:
```js
shape.on('dragend', () => console.log('dragged'));
shape.on('click', () => console.log('clicked'));
// Drag the shape: you get 'dragged' only.
// Press and release without moving: you get 'clicked' only.
```
If you are seeing a click after a drag anyway, it is usually one of these.
**You are listening to `mouseup`, not `click`.** `mouseup` is a raw pointer
event and always fires, drag or not. `click` is the one Konva suppresses.
**The pointer never moved far enough to count as a drag.** `Konva.dragDistance`
defaults to `3` pixels. A press that wanders one or two pixels — which is normal
on a touchscreen — is treated as a click, and `dragstart` never fires either.
That is intended. Raise it if your users have shaky hands on a small target:
```js
Konva.dragDistance = 8;
```
**You set `dragDistance` to `0`.** Then every press is a drag, which suppresses
the click you wanted.
## Preventing the browser's own gestures
Separately from Konva, a touch on a canvas may scroll the page or trigger a
double-tap zoom. That is the browser, and it is stopped in CSS:
```css
#container {
touch-action: none;
}
```
Use `touch-action: pan-y` instead if the page should still scroll vertically
while the canvas handles horizontal drags. See
[Mobile Scrolling](/docs/events/Mobile_Scrolling.html) for the full picture.
## Related
- [Pointer Events](/docs/events/Pointer_Events.html) — one family for every device
- [Mobile Events](/docs/events/Mobile_Events.html) — the touch event list
- [Desktop and Mobile](/docs/events/Desktop_and_Mobile.html) — the older paired-event approach
---
# HTML5 Canvas Canvas Multi-Event Binding Tutorial
> Learn how to bind multiple events to a single handler in Konva using a space-delimited string with the on() method.
Source: https://konvajs.org/docs/events/Multi_Event.html
To bind multiple events to a single handler with Konva, we can use the `on()` method and pass in a space delimited string containing multiple event types.
```js
shape.on('mouseover mousedown mouseup', function (e) {
console.log('events: ' + e.type);
});
```
**Instructions: Mouseover, mousedown, and mouseup over the circle to observe that the function bound to the circle is executed for each event.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
layer.add(text);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
function writeMessage(message) {
text.text(message);
}
circle.on('mouseover mousedown mouseup', function (evt) {
writeMessage('event: ' + evt.type);
});
layer.add(circle);
```
```jsx
import { Stage, Layer, Circle, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const handleMultiEvent = (e) => {
setMessage('event: ' + e.type);
};
return (
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Pointer Events Tutorial
> Learn how to use pointer events in Konva to handle both mouse and touch input with a single event handler.
Source: https://konvajs.org/docs/events/Pointer_Events.html
Pointer events can be useful to handle both mobile and desktop events with one handler.
To bind pointer event handlers to shapes with Konva, we can use the `on()` method.
The `on()` method requires an event type and a function to be executed when the event occurs.
Konva supports `pointerdown`, `pointermove`, `pointerup`, `pointercancel`, `pointerover`, `pointerenter`, `pointerout`, `pointerleave`, `pointerclick`, `pointerdblclick` events.
_Note: This example works on both mobile and desktop devices._
**Instructions: move your mouse/finger across the triangle to see pointer coordinates.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
const triangle = new Konva.RegularPolygon({
x: stage.width() / 2,
y: stage.height() / 2,
sides: 3,
radius: 80,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
});
function writeMessage(message) {
text.text(message);
}
triangle.on('pointermove', function () {
const pos = stage.getPointerPosition();
writeMessage('x: ' + pos.x + ', y: ' + pos.y);
});
triangle.on('pointerout', function () {
writeMessage('');
});
layer.add(triangle);
layer.add(text);
stage.add(layer);
```
```jsx
import { Stage, Layer, RegularPolygon, Text } from 'react-konva';
import { useState, useRef } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const stageRef = useRef();
const handlePointerMove = () => {
const pos = stageRef.current.getPointerPosition();
setMessage(`x: ${pos.x}, y: ${pos.y}`);
};
return (
setMessage('')}
/>
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Remove Event Listener with Konva
> Learn how to remove event listeners from shapes in Konva using the off() method.
Source: https://konvajs.org/docs/events/Remove_Event.html
To remove an event listener with Konva, we can use the `off()` method of
a shape object which requires an event type such as click or mousedown.
**Instructions: Click on the circle to see an alert triggered from the onclick
event binding. Remove the event listener by clicking on the button and again
click on the circle to observe that the event binding has been removed.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
// add click listener
circle.on('click', function () {
alert('you clicked the circle');
});
layer.add(circle);
// add button to remove listener
const button = document.createElement('button');
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
button.innerHTML = 'Remove click listener';
document.body.appendChild(button);
button.addEventListener('click', () => {
// remove click listener
circle.off('click');
});
```
```jsx
import { Stage, Layer, Circle } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [hasListener, setHasListener] = useState(true);
return (
<>
setHasListener(false)}>
Remove click listener
alert('you clicked the circle') : null}
/>
>
);
};
export default App;
```
```html
Remove click listener
```
---
# HTML5 Canvas Remove Event Listener by Name with Konva
> Learn how to remove specific event listeners by namespace in Konva using the on() and off() methods with named events.
Source: https://konvajs.org/docs/events/Remove_by_Name.html
Konva event namespaces identify related listeners in imperative code. Add the
namespace after the event type, such as `click.menu`. Then pass the same name to
`off()` to remove that listener.
**Instructions:** Select the circle to run two listeners. Use each button to
remove one listener. Then select the circle again.
`react-konva` does not expose Konva event namespaces through React event props.
React has one prop for each event type, such as `onClick`. Store enabled states
in React, and conditionally pass one dispatcher to that prop.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
// add click listeners
circle.on('click.event1', function () {
alert('first click listener');
});
circle.on('click.event2', function () {
alert('second click listener');
});
layer.add(circle);
// add buttons to remove listeners
const button1 = document.createElement('button');
button1.innerHTML = 'Remove first listener';
button1.style.position = 'absolute';
button1.style.top = '0';
button1.style.left = '0';
button1.onclick = function() {
circle.off('click.event1');
};
document.getElementById('container').appendChild(button1);
const button2 = document.createElement('button');
button2.innerHTML = 'Remove second listener';
button2.style.position = 'absolute';
button2.style.top = '30px';
button2.style.left = '0';
button2.onclick = function() {
circle.off('click.event2');
};
document.getElementById('container').appendChild(button2);
```
```jsx
import { useState } from 'react';
import { Stage, Layer, Circle, Text } from 'react-konva';
const App = () => {
const [firstEnabled, setFirstEnabled] = useState(true);
const [secondEnabled, setSecondEnabled] = useState(true);
const [message, setMessage] = useState('Select the circle');
const handleClick =
firstEnabled || secondEnabled
? () => {
const messages = [];
if (firstEnabled) {
messages.push('first listener');
}
if (secondEnabled) {
messages.push('second listener');
}
setMessage(messages.join(' + '));
}
: undefined;
return (
<>
setFirstEnabled(false)}>
Remove first listener
setSecondEnabled(false)}>
Remove second listener
>
);
};
export default App;
```
---
# HTML5 Canvas Special Stage Events Konva
> Learn about special stage-level events in Konva.js: contentClick, contentMousemove, and other events that fire on the Stage container.
Source: https://konvajs.org/docs/events/Stage_Events.html
All events are started from Shapes. So if you click on an empty space within a canvas, a `click` event will not trigger on `Layer` but it will trigger on the `Stage` object instead.
**Instructions: Click on empty space and on shapes to see different event behaviors.**
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 10,
y: 10,
fontFamily: 'Calibri',
fontSize: 24,
text: '',
fill: 'black',
});
layer.add(text);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
function writeMessage(message) {
text.text(message);
}
// handle stage click
stage.on('click', function (e) {
if (e.target === stage) {
writeMessage('clicked on stage');
return;
}
writeMessage('clicked on ' + e.target.name());
});
// add shape
circle.name('circle');
layer.add(circle);
```
```jsx
import { Stage, Layer, Circle, Text } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [message, setMessage] = useState('');
const handleStageClick = (e) => {
if (e.target === e.target.getStage()) {
setMessage('clicked on stage');
return;
}
setMessage('clicked on ' + e.target.name());
};
return (
);
};
export default App;
```
```html
```
---
# HTML5 Canvas Brighten Image Filter Tutorial
> Deprecated Brighten filter tutorial for Konva.js. Use the newer Brightness filter instead for adjusting image brightness.
Source: https://konvajs.org/docs/filters/Brighten.html
**Note**: This filter is deprecated and will be removed in the future. Use the `Brightness` filter instead.
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To brighten or darken an image with Konva, we can use the `Konva.Filters.Brighten`
filter and set the brightness amount with the `brightness` property.
The `brightness` property can be set to any number between -1 and 1.
Negative values darken the image, and positive values brighten the image.
**Instructions**: Slide the control to adjust the brightness
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Brighten]);
image.brightness(0.3);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '-1';
slider.max = '1';
slider.step = '0.1';
slider.value = image.brightness();
slider.style.position = 'absolute';
slider.style.top = '20px';
slider.style.left = '20px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image.brightness(value);
});
document.body.appendChild(slider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [brightness, setBrightness] = useState(0.3);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Brighten]}
brightness={brightness}
/>
setBrightness(parseFloat(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Brightness Image Filter Tutorial
> Learn how to adjust image brightness on HTML5 Canvas using the Konva.js Brightness filter, similar to CSS filter brightness.
Source: https://konvajs.org/docs/filters/Brightness.html
**Note**: This filter was introduced in Konva 10.0.0 to replace the `Brighten` filter. The `Brighten` filter is still available for backward compatibility, but it is deprecated and will be removed in the future.
New filter renders closer to CSS `filter: brightness(0.5);`
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To brighten or darken an image with Konva, we can use the `Konva.Filters.Brightness`
filter and set the brightness amount with the `brightness` property.
The `brightness` property can be set to any number from 0 to 2, where:
- 0 creates a completely black image
- 1 is the original image (no change)
- Values greater than 1 brighten the image
- 2 creates a very bright image
**Instructions**: Slide the control to adjust the brightness
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Brightness]);
image.brightness(1.5);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '0';
slider.max = '2';
slider.step = '0.1';
slider.value = image.brightness();
slider.style.position = 'absolute';
slider.style.top = '20px';
slider.style.left = '20px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image.brightness(value);
});
document.body.appendChild(slider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [brightness, setBrightness] = useState(1.5);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Brightness]}
brightness={brightness}
/>
setBrightness(parseFloat(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Contrast filter Image Tutorial
> Learn how to adjust image contrast on HTML5 Canvas using the Konva.js Contrast filter with an interactive slider demo.
Source: https://konvajs.org/docs/filters/Contrast.html
To apply filter to an `Konva.Node`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To change contrast of an image with Konva, we can use the `Konva.Filters.Contrast`.
**Instructions**: Slide the control to change contrast value.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Contrast]);
image.contrast(30);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '-100';
slider.max = '100';
slider.value = image.contrast();
slider.style.position = 'absolute';
slider.style.top = '20px';
slider.style.left = '20px';
slider.addEventListener('input', (e) => {
const value = parseInt(e.target.value);
image.contrast(value);
});
document.body.appendChild(slider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [contrast, setContrast] = useState(30);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Contrast]}
contrast={contrast}
/>
setContrast(parseInt(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Custom Filter Tutorial
> Learn how to create and apply custom image filters in Konva.js by manipulating canvas ImageData pixels directly.
Source: https://konvajs.org/docs/filters/Custom_Filter.html
## How apply custom filter for Konva nodes?
This demo demonstrate how to use custom filters with `Konva` framework.
`Filter` is a function that have canvas ImageData as input and it should mutate it.
```javascript
function Filter(imageData) {
// do something with image data
imageData.data[0] = 0;
}
```
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
Also take a look into [Image Border Demo](/docs/sandbox/Image_Border.html) for custom filter example.
**In this demo we will remove all transparency from the image.**
```js
import Konva from 'konva';
// create our custom filter
Konva.Filters.RemoveAlpha = function (imageData) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i + 3] = 255; // set alpha to 1
}
};
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.RemoveAlpha]);
};
imageObj.src = 'https://konvajs.org/assets/lion.png';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
// create our custom filter
Konva.Filters.RemoveAlpha = function (imageData) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i + 3] = 255; // set alpha to 1
}
};
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [image] = useImage('https://konvajs.org/assets/lion.png', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image) {
imageRef.current.cache();
}
}, [image]);
return (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.RemoveAlpha]}
/>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Emboss filter Image Tutorial
> Learn how to apply an emboss filter to images on HTML5 Canvas using Konva.js with adjustable strength, white level, and blend controls.
Source: https://konvajs.org/docs/filters/Emboss.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
**Instructions**: Slide the controls to change emboss values.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Emboss]);
image.embossStrength(0.5);
image.embossWhiteLevel(0.5);
image.embossDirection('top-left');
image.embossBlend(0.5);
// create sliders
const strengthSlider = document.createElement('input');
strengthSlider.type = 'range';
strengthSlider.min = '0';
strengthSlider.max = '1';
strengthSlider.step = '0.1';
strengthSlider.value = image.embossStrength();
strengthSlider.style.position = 'absolute';
strengthSlider.style.top = '20px';
strengthSlider.style.left = '20px';
const whiteLevelSlider = document.createElement('input');
whiteLevelSlider.type = 'range';
whiteLevelSlider.min = '0';
whiteLevelSlider.max = '1';
whiteLevelSlider.step = '0.1';
whiteLevelSlider.value = image.embossWhiteLevel();
whiteLevelSlider.style.position = 'absolute';
whiteLevelSlider.style.top = '45px';
whiteLevelSlider.style.left = '20px';
const blendSlider = document.createElement('input');
blendSlider.type = 'range';
blendSlider.min = '0';
blendSlider.max = '1';
blendSlider.step = '0.1';
blendSlider.value = image.embossBlend();
blendSlider.style.position = 'absolute';
blendSlider.style.top = '70px';
blendSlider.style.left = '20px';
// add labels
const strengthLabel = document.createElement('div');
strengthLabel.textContent = 'Strength';
strengthLabel.style.position = 'absolute';
strengthLabel.style.top = '20px';
strengthLabel.style.left = '200px';
const whiteLevelLabel = document.createElement('div');
whiteLevelLabel.textContent = 'White Level';
whiteLevelLabel.style.position = 'absolute';
whiteLevelLabel.style.top = '45px';
whiteLevelLabel.style.left = '200px';
const blendLabel = document.createElement('div');
blendLabel.textContent = 'Blend';
blendLabel.style.position = 'absolute';
blendLabel.style.top = '70px';
blendLabel.style.left = '200px';
// add event listeners
strengthSlider.addEventListener('input', (e) => {
image.embossStrength(parseFloat(e.target.value));
});
whiteLevelSlider.addEventListener('input', (e) => {
image.embossWhiteLevel(parseFloat(e.target.value));
});
blendSlider.addEventListener('input', (e) => {
image.embossBlend(parseFloat(e.target.value));
});
// add elements to the page
document.body.appendChild(strengthSlider);
document.body.appendChild(whiteLevelSlider);
document.body.appendChild(blendSlider);
document.body.appendChild(strengthLabel);
document.body.appendChild(whiteLevelLabel);
document.body.appendChild(blendLabel);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [strength, setStrength] = useState(0.5);
const [whiteLevel, setWhiteLevel] = useState(0.5);
const [blend, setBlend] = useState(0.5);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Emboss]}
embossStrength={strength}
embossWhiteLevel={whiteLevel}
embossDirection="top-left"
embossBlend={blend}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Enhance Image Filter Tutorial
> Learn how to enhance images on HTML5 Canvas using the Konva.js Enhance filter with adjustable enhancement levels.
Source: https://konvajs.org/docs/filters/Enhance.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To enhance an image with Konva, we can use the `Konva.Filters.Enhance` filter
and set the enhance amount with the `enhance` property.
**Instructions**: Slide the control to adjust the enhance value.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Enhance]);
image.enhance(0.4);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '-1';
slider.max = '1';
slider.step = '0.1';
slider.value = image.enhance();
slider.style.position = 'absolute';
slider.style.top = '20px';
slider.style.left = '20px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image.enhance(value);
});
document.body.appendChild(slider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [enhance, setEnhance] = useState(0.4);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Enhance]}
enhance={enhance}
/>
setEnhance(parseFloat(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Grayscale Image Filter Tutorial
> Learn how to convert images to grayscale on HTML5 Canvas using the Konva.js Grayscale filter.
Source: https://konvajs.org/docs/filters/Grayscale.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()` function. Then apply filter with `filters()` function.
To invert the colors of an image with Konva, we can use the
`Konva.Filters.Grayscale` filter.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Grayscale]);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image) {
imageRef.current.cache();
}
}, [image]);
return (
{image && (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Grayscale]}
/>
)}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Hue, Saturation and Luminance filter Image Tutorial
> Learn how to adjust hue, saturation, and luminance of images on HTML5 Canvas using the Konva.js HSL filter.
Source: https://konvajs.org/docs/filters/HSL.html
To apply filter to an `Konva.Node`, we have to cache it first with `cache()` function. Then apply filter with `filters()` function.
To change hue, saturation and luminance components of an image with Konva, we can use the `Konva.Filters.HSL`.
**Instructions**: Slide the controls to change HSL values.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.HSL]);
// create sliders
const createSlider = (label, min, max, defaultValue, property) => {
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = `${label}: `;
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = min;
slider.max = max;
slider.step = '0.1';
slider.value = defaultValue;
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image[property](value);
});
container.appendChild(slider);
return container;
};
const hueSlider = createSlider('Hue', -180, 180, 0, 'hue');
hueSlider.style.top = '20px';
document.body.appendChild(hueSlider);
const saturationSlider = createSlider('Saturation', -2, 10, 0, 'saturation');
saturationSlider.style.top = '45px';
document.body.appendChild(saturationSlider);
const luminanceSlider = createSlider('Luminance', -2, 2, 0, 'luminance');
luminanceSlider.style.top = '70px';
document.body.appendChild(luminanceSlider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [hue, setHue] = useState(0);
const [saturation, setSaturation] = useState(0);
const [luminance, setLuminance] = useState(0);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.HSL]}
hue={hue}
saturation={saturation}
luminance={luminance}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Hue, Saturation and Value filter Image Tutorial
> Learn how to adjust hue, saturation, and value of images on HTML5 Canvas using the Konva.js HSV filter.
Source: https://konvajs.org/docs/filters/HSV.html
To apply filter to an `Konva.Node`, we have to cache it first with `cache()` function. Then apply filter with `filters()` function.
To change hue, saturation and value components of an image with Konva, we can use the `Konva.Filters.HSV`.
**Instructions**: Slide the controls to change HSV values.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.HSV]);
// create sliders
const createSlider = (label, min, max, defaultValue, property) => {
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = `${label}: `;
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = min;
slider.max = max;
slider.step = '0.1';
slider.value = defaultValue;
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image[property](value);
});
container.appendChild(slider);
return container;
};
const hueSlider = createSlider('Hue', -180, 180, 0, 'hue');
hueSlider.style.top = '20px';
document.body.appendChild(hueSlider);
const saturationSlider = createSlider('Saturation', -2, 10, 0, 'saturation');
saturationSlider.style.top = '45px';
document.body.appendChild(saturationSlider);
const value = createSlider('Value', -2, 2, 0, 'value');
value.style.top = '70px';
document.body.appendChild(value);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [hue, setHue] = useState(0);
const [saturation, setSaturation] = useState(0);
const [value, setValue] = useState(0);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.HSV]}
hue={hue}
saturation={saturation}
value={value}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Invert Image Filter Tutorial
> Learn how to invert image colors on HTML5 Canvas using the Konva.js Invert filter for a negative photo effect.
Source: https://konvajs.org/docs/filters/Invert.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()` function. Then apply filter with `filters()` function.
To invert the colors of an image with Konva, we can use the
`Konva.Filters.Invert` filter.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Invert]);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useRef, useEffect } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
{image && (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Invert]}
/>
)}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Kaleidoscope Image Filter Tutorial
> Learn how to create a kaleidoscope effect on images using the Konva.js Kaleidoscope filter with adjustable power and angle.
Source: https://konvajs.org/docs/filters/Kaleidoscope.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To create a kaleidoscope with Konva, we can use the `Konva.Filters.Kaleidoscope`
filter and set the `kaleidoscopePower` and `kaleidoscopeAngle` properties.
**Instructions**: Slide the controls to adjust the kaleidoscope power and angle.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Kaleidoscope]);
image.kaleidoscopePower(3);
image.kaleidoscopeAngle(0);
// create sliders
const createSlider = (label, min, max, defaultValue, property) => {
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = `${label}: `;
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = min;
slider.max = max;
slider.step = property === 'kaleidoscopePower' ? '1' : '0.1';
slider.value = defaultValue;
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image[property](value);
});
container.appendChild(slider);
return container;
};
const powerSlider = createSlider('Power', 2, 8, 3, 'kaleidoscopePower');
powerSlider.style.top = '20px';
document.body.appendChild(powerSlider);
const angleSlider = createSlider('Angle', 0, 360, 0, 'kaleidoscopeAngle');
angleSlider.style.top = '45px';
document.body.appendChild(angleSlider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [angle, setAngle] = useState(0);
const [power, setPower] = useState(3);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Kaleidoscope]}
kaleidoscopePower={power}
kaleidoscopeAngle={angle}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Mask Image Filter Tutorial
> Learn how to remove image backgrounds on HTML5 Canvas using the Konva.js Mask filter with adjustable threshold.
Source: https://konvajs.org/docs/filters/Mask.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()` function. Then apply filter with `filters()` function.
To mask the colors of an image with Konva, we can use the
`Konva.Filters.Mask` filter.
The `Konva.Filters.Mask` filter attempts to remove the background from an image. It works by:
1. Sampling the color of the four corners of the image.
2. If the corner colors are similar (within the `threshold`), it assumes this color represents the background.
3. It then creates a mask where pixels similar to the identified background color become transparent, and other pixels remain opaque.
4. This mask is refined using image processing techniques (like erosion and dilation) to remove noise and smooth edges.
5. Finally, the refined mask is applied to the image's alpha channel.
The `threshold` property (ranging from 0 to 255) controls how similar a pixel's color must be to the background color to be masked out. A lower threshold means only colors very close to the background will be removed, while a higher threshold will remove a wider range of colors.
**Instructions**: Slide the control to adjust the mask threshold.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Mask]);
image.threshold(10);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '0';
slider.max = '255';
slider.value = image.threshold();
slider.style.position = 'absolute';
slider.style.top = '20px';
slider.style.left = '20px';
slider.addEventListener('input', (e) => {
const value = parseInt(e.target.value);
image.threshold(value);
});
document.body.appendChild(slider);
};
imageObj.src = 'https://konvajs.org/assets/space.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [threshold, setThreshold] = useState(10);
const [image] = useImage('https://konvajs.org/assets/space.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Mask]}
threshold={threshold}
/>
setThreshold(parseInt(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Multiple Filters Tutorial
> Learn how to apply multiple filters like blur, brightness, and contrast simultaneously to images using Konva.js.
Source: https://konvajs.org/docs/filters/Multiple_Filters.html
To apply multiple filters to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filters with `filters()` function.
**Instructions**: Use the checkboxes to toggle different filters and adjust their values with the sliders.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
// Create controls container
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.top = '20px';
container.style.left = '20px';
document.body.appendChild(container);
// Filter states
const filterStates = {
blur: false,
brightness: false,
contrast: false,
};
const filterValues = {
blur: 10,
brightness: 1.3,
contrast: 50,
};
// Create filter controls
const createFilterControl = (name, min, max, step, defaultValue) => {
const div = document.createElement('div');
div.style.marginBottom = '10px';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = name;
checkbox.checked = filterStates[name];
const label = document.createElement('label');
label.htmlFor = name;
label.textContent = ` ${name.charAt(0).toUpperCase() + name.slice(1)}: `;
const slider = document.createElement('input');
slider.type = 'range';
slider.min = min;
slider.max = max;
slider.step = step;
slider.value = defaultValue;
slider.style.width = '200px';
slider.disabled = !filterStates[name];
div.appendChild(checkbox);
div.appendChild(label);
div.appendChild(slider);
checkbox.addEventListener('change', (e) => {
filterStates[name] = e.target.checked;
slider.disabled = !e.target.checked;
updateFilters();
});
slider.addEventListener('input', (e) => {
filterValues[name] = parseFloat(e.target.value);
updateFilters();
});
return div;
};
// Add controls
container.appendChild(createFilterControl('blur', 0, 40, 1, filterValues.blur));
container.appendChild(createFilterControl('brightness', 0, 2, 0.1, filterValues.brightness));
container.appendChild(createFilterControl('contrast', -100, 100, 1, filterValues.contrast));
function updateFilters() {
const activeFilters = [];
if (filterStates.blur) {
activeFilters.push(Konva.Filters.Blur);
image.blurRadius(filterValues.blur);
}
if (filterStates.brightness) {
activeFilters.push(Konva.Filters.Brightness);
image.brightness(filterValues.brightness);
}
if (filterStates.contrast) {
activeFilters.push(Konva.Filters.Contrast);
image.contrast(filterValues.contrast);
}
image.filters(activeFilters);
}
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const FilterControl = ({ name, min, max, step, filters, setFilters }) => {
const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
return (
{
setFilters({
...filters,
[name]: { ...filters[name], active: e.target.checked },
});
}}
/>
{capitalizedName}:
{
setFilters({
...filters,
[name]: { ...filters[name], value: parseFloat(e.target.value) },
});
}}
style={{ width: '200px' }}
/>
);
};
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [filters, setFilters] = useState({
blur: { active: false, value: 10 },
brightness: { active: false, value: 1.3 },
contrast: { active: false, value: 50 },
});
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
const activeFilters = [];
if (filters.blur.active) activeFilters.push(Konva.Filters.Blur);
if (filters.brightness.active) activeFilters.push(Konva.Filters.Brightness);
if (filters.contrast.active) activeFilters.push(Konva.Filters.Contrast);
return (
<>
{image && (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={activeFilters}
blurRadius={filters.blur.value}
brightness={filters.brightness.value}
contrast={filters.contrast.value}
/>
)}
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Noise filter Image Tutorial
> Learn how to add a noise effect to images on HTML5 Canvas using the Konva.js Noise filter with adjustable intensity.
Source: https://konvajs.org/docs/filters/Noise.html
To apply filter to an `Konva.Node`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To change noise of an image with Konva, we can use the `Konva.Filters.Noise`.
**Instructions**: Slide the control to change noise value.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Noise]);
image.noise(0.3);
// create slider
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.top = '20px';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = 'Noise: ';
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '0';
slider.max = '1';
slider.step = '0.1';
slider.value = image.noise();
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image.noise(value);
});
container.appendChild(slider);
document.body.appendChild(container);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [noise, setNoise] = useState(0.3);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Noise]}
noise={noise}
/>
setNoise(parseFloat(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Pixelate filter Image Tutorial
> Learn how to pixelate images on HTML5 Canvas using the Konva.js Pixelate filter with adjustable pixel size.
Source: https://konvajs.org/docs/filters/Pixelate.html
To apply filter to an `Konva.Node`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To change pixelate effect of an image with Konva, we can use the `Konva.Filters.Pixelate`.
**Instructions**: Slide the control to change pixel size value.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Pixelate]);
image.pixelSize(8);
// create slider
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.top = '20px';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = 'Pixel Size: ';
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '2';
slider.max = '32';
slider.step = '1';
slider.value = image.pixelSize();
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseInt(e.target.value);
image.pixelSize(value);
});
container.appendChild(slider);
document.body.appendChild(container);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [pixelSize, setPixelSize] = useState(8);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Pixelate]}
pixelSize={pixelSize}
/>
setPixelSize(parseInt(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas RGB filter Image Tutorial
> Learn how to adjust red, green, and blue color channels of images on HTML5 Canvas using the Konva.js RGB filter.
Source: https://konvajs.org/docs/filters/RGB.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To change rgb components of an image with Konva, we can use the `Konva.Filters.RGB`.
**Instructions**: Slide the controls to change RGB values.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.RGB]);
image.red(100);
image.green(100);
image.blue(100);
// create sliders
const createSlider = (label, property) => {
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = `${label}: `;
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '0';
slider.max = '255';
slider.step = '1';
slider.value = image[property]();
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseInt(e.target.value);
image[property](value);
});
container.appendChild(slider);
return container;
};
const redSlider = createSlider('Red', 'red');
redSlider.style.top = '20px';
document.body.appendChild(redSlider);
const greenSlider = createSlider('Green', 'green');
greenSlider.style.top = '45px';
document.body.appendChild(greenSlider);
const blueSlider = createSlider('Blue', 'blue');
blueSlider.style.top = '70px';
document.body.appendChild(blueSlider);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [red, setRed] = useState(100);
const [green, setGreen] = useState(100);
const [blue, setBlue] = useState(100);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.RGB]}
red={red}
green={green}
blue={blue}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Sepia filter Image Tutorial
> Learn how to apply a sepia tone effect to images on HTML5 Canvas using the Konva.js Sepia filter.
Source: https://konvajs.org/docs/filters/Sepia.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To apply a sepia effect to an image with Konva, we can use the `Konva.Filters.Sepia`.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
// Apply Sepia filter
image.cache();
image.filters([Konva.Filters.Sepia]);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useRef, useEffect } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
{image && (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Sepia]}
/>
)}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Solarize filter Image Tutorial
> Learn how to apply a solarize effect to images on HTML5 Canvas using the Konva.js Solarize filter with adjustable threshold.
Source: https://konvajs.org/docs/filters/Solarize.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To apply a solarize effect to an image with Konva, we can use the `Konva.Filters.Solarize`.
**Instructions**: Slide the control to change threshold value.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Solarize]);
image.threshold(0.5);
// create slider
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.top = '20px';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = 'Threshold: ';
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '0';
slider.max = '1';
slider.step = '0.1';
slider.value = image.threshold();
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image.threshold(value);
});
container.appendChild(slider);
document.body.appendChild(container);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [threshold, setThreshold] = useState(0.5);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Solarize]}
threshold={threshold}
/>
setThreshold(parseFloat(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Threshold filter Image Tutorial
> Learn how to convert images to black and white using the Konva.js Threshold filter with an adjustable threshold value.
Source: https://konvajs.org/docs/filters/Threshold.html
To apply filter to an `Konva.Image`, we have to cache it first with `cache()`
function. Then apply filter with `filters()` function.
To apply a threshold effect to an image with Konva, we can use the `Konva.Filters.Threshold`.
The threshold filter converts the image into a black and white image where all pixels above the threshold value become white and all pixels below become black.
**Instructions**: Slide the control to change threshold value.
For all available filters go to [Filters Documentation](/api/Konva.Filters.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(image);
image.cache();
image.filters([Konva.Filters.Threshold]);
image.threshold(0.5);
// create slider
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.top = '20px';
container.style.left = '20px';
const text = document.createElement('span');
text.textContent = 'Threshold: ';
container.appendChild(text);
const slider = document.createElement('input');
slider.type = 'range';
slider.min = '0';
slider.max = '1';
slider.step = '0.1';
slider.value = image.threshold();
slider.style.width = '200px';
slider.addEventListener('input', (e) => {
const value = parseFloat(e.target.value);
image.threshold(value);
});
container.appendChild(slider);
document.body.appendChild(container);
};
imageObj.src = 'https://konvajs.org/assets/darth-vader.jpg';
imageObj.crossOrigin = 'anonymous';
```
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
const [threshold, setThreshold] = useState(0.5);
const [image] = useImage('https://konvajs.org/assets/darth-vader.jpg', 'anonymous');
const imageRef = useRef(null);
useEffect(() => {
if (image && imageRef.current) {
imageRef.current.cache();
}
}, [image]);
return (
<>
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
filters={[Konva.Filters.Threshold]}
threshold={threshold}
/>
setThreshold(parseFloat(e.target.value))}
style={{ position: 'absolute', top: '20px', left: '20px' }}
/>
>
);
};
export default App;
```
```js
```
---
# Move Shape to Another Container
> Learn how to move shapes between groups, layers, and containers in Konva using the moveTo() method.
Source: https://konvajs.org/docs/groups_and_layers/Change_Containers.html
To move a shape from one container into another with Konva, we can use the `moveTo()` method which requires a container as a parameter.
A container can be another stage, a layer, or a group. You can also move groups into other groups and layers, or shapes from groups directly into other layers.
**Instructions: Drag and drop the groups and observe that the red rectangle is bound to either the yellow group or the blue group. Use the buttons on the left to move the box from one group into another.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
// yellow group
const group1 = new Konva.Group({
x: 50,
y: 50,
draggable: true,
});
const yellow = new Konva.Rect({
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
});
group1.add(yellow);
// blue group
const group2 = new Konva.Group({
x: 200,
y: 50,
draggable: true,
});
const blue = new Konva.Rect({
width: 100,
height: 100,
fill: 'blue',
stroke: 'black',
strokeWidth: 4,
});
group2.add(blue);
// red box
const redBox = new Konva.Rect({
x: 10,
y: 10,
width: 30,
height: 30,
fill: 'red',
});
group1.add(redBox);
layer.add(group1);
layer.add(group2);
stage.add(layer);
// create buttons
const moveToGroup1Btn = document.createElement('button');
moveToGroup1Btn.textContent = 'Move to yellow group';
moveToGroup1Btn.addEventListener('click', () => {
redBox.moveTo(group1);
});
const moveToGroup2Btn = document.createElement('button');
moveToGroup2Btn.textContent = 'Move to blue group';
moveToGroup2Btn.addEventListener('click', () => {
redBox.moveTo(group2);
});
document.body.appendChild(moveToGroup1Btn);
document.body.appendChild(moveToGroup2Btn);
````
```js
import { Stage, Layer, Rect, Group } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [redBoxGroup, setRedBoxGroup] = useState('yellow');
const [groupPositions, setGroupPositions] = useState({
yellow: { x: 50, y: 50 },
blue: { x: 200, y: 50 },
});
const handleDragEnd = (group, e) => {
setGroupPositions((positions) => ({
...positions,
[group]: { x: e.target.x(), y: e.target.y() },
}));
};
return (
<>
setRedBoxGroup('yellow')}>
Move to yellow group
setRedBoxGroup('blue')}>
Move to blue group
handleDragEnd('yellow', e)}
>
{redBoxGroup === 'yellow' && (
)}
handleDragEnd('blue', e)}
>
{redBoxGroup === 'blue' && (
)}
>
);
};
export default App;
````
```js
Move to yellow group
Move to blue group
```
---
# Shape Groups
> Learn how to group multiple shapes together with Konva.Group to move, rotate, and scale them as a single unit.
Source: https://konvajs.org/docs/groups_and_layers/Groups.html
To group multiple shapes together with Konva, we can instantiate a `Konva.Group()` object and then add shapes to it with the `add()` method.
Grouping shapes together is really handy when we want to transform multiple shapes together, e.g. if we want to move, rotate, or scale multiple shapes at once.
Groups can also be added to other groups to create more complex Node trees.
For a full list of attributes and methods, check out the [Konva.Group documentation](/api/Konva.Group.html).
**Instructions: Try to drag the group. Notice how all shapes move together.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
const group = new Konva.Group({
x: 50,
y: 50,
draggable: true,
});
const circle = new Konva.Circle({
x: 40,
y: 40,
radius: 30,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
const rect = new Konva.Rect({
x: 80,
y: 20,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
group.add(circle);
group.add(rect);
layer.add(group);
stage.add(layer);
````
```js
import { Stage, Layer, Group, Circle, Rect } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
return (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
>
);
};
export default App;
````
```js
```
---
# Shape Layering
> Learn how to reorder shapes on the canvas using moveToTop, moveToBottom, moveUp, and moveDown methods in Konva.
Source: https://konvajs.org/docs/groups_and_layers/Layering.html
To layer shapes with Konva, we can use one of the following layering methods:
`moveToTop()`, `moveToBottom()`, `moveUp()`, `moveDown()`, or `zIndex()`.
You can also layer groups and layers.
**Instructions: Drag and drop the boxes to move them around, and then use the buttons on the left to reorder the yellow box.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
const yellowBox = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
const redBox = new Konva.Rect({
x: 100,
y: 100,
width: 100,
height: 100,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
layer.add(yellowBox);
layer.add(redBox);
stage.add(layer);
// create buttons
const toTopBtn = document.createElement('button');
toTopBtn.textContent = 'Move yellow box to top';
toTopBtn.addEventListener('click', () => {
yellowBox.moveToTop();
});
const toBottomBtn = document.createElement('button');
toBottomBtn.textContent = 'Move yellow box to bottom';
toBottomBtn.addEventListener('click', () => {
yellowBox.moveToBottom();
});
document.body.prepend(toTopBtn);
document.body.prepend(toBottomBtn);
````
```js
import { Stage, Layer, Rect } from 'react-konva';
import { useRef, useState } from 'react';
const App = () => {
const yellowRef = useRef();
const [positions, setPositions] = useState({
yellow: { x: 50, y: 50 },
red: { x: 100, y: 100 },
});
const handleDragEnd = (color, e) => {
setPositions((currentPositions) => ({
...currentPositions,
[color]: { x: e.target.x(), y: e.target.y() },
}));
};
return (
<>
yellowRef.current.moveToTop()}>
Move yellow box to top
yellowRef.current.moveToBottom()}>
Move yellow box to bottom
handleDragEnd('yellow', e)}
/>
handleDragEnd('red', e)}
/>
>
);
};
export default App;
````
```js
Move yellow box to top
Move yellow box to bottom
```
---
# Understanding Node zIndex
> Understand how zIndex works in Konva as the index of a node within its parent's children array, and how it differs from CSS z-index.
Source: https://konvajs.org/docs/groups_and_layers/zIndex.html
## What is zIndex of a node?
You can get/set zIndex of a node in this way:
```javascript
// get
const zIndex = shape.zIndex();
// set
shape.zIndex(1);
```
zIndex is just the index of a node in its parent's children array. Please don't confuse `zIndex` in Konva with `z-index` in CSS.
```javascript
const group = new Konva.Group();
const circle = new Konva.Circle({});
group.add(circle);
// it will log 0
console.log(circle.zIndex());
// the next line will not work because the group has only one child
circle.zIndex(1);
// still logs 0
console.log(circle.zIndex());
// for any node this equation will be true:
console.log(circle.zIndex() === circle.getParent().children.indexOf(circle));
```
You can't use `zIndex` to set absolute position of the node, like we do in CSS.
Konva draws nodes in the strict order as they are defined in the nodes tree.
**Instructions: Try to change zIndex of shapes using the buttons. Notice how the order of shapes changes.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
// first group
const group1 = new Konva.Group();
layer.add(group1);
const blackRect = new Konva.Rect({
x: 10,
y: 10,
width: 100,
height: 100,
fill: 'black',
});
group1.add(blackRect);
const redCircle = new Konva.Circle({
x: 80,
y: 80,
radius: 40,
fill: 'red',
});
group1.add(redCircle);
// second group
const group2 = new Konva.Group();
layer.add(group2);
const greenRect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'green',
});
group2.add(greenRect);
stage.add(layer);
// create buttons
const btn1 = document.createElement('button');
btn1.textContent = 'Move red circle to group2';
btn1.addEventListener('click', () => {
redCircle.moveTo(group2);
});
const btn2 = document.createElement('button');
btn2.textContent = 'Move red circle to group1';
btn2.addEventListener('click', () => {
redCircle.moveTo(group1);
});
document.body.appendChild(btn1);
document.body.appendChild(btn2);
````
```js
import { Stage, Layer, Group, Rect, Circle } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [redCircleGroup, setRedCircleGroup] = useState('group1');
return (
<>
setRedCircleGroup('group2')}>
Move red circle to group2
setRedCircleGroup('group1')}>
Move red circle to group1
{redCircleGroup === 'group1' && (
)}
{redCircleGroup === 'group2' && (
)}
>
);
};
export default App;
````
```js
Move red circle to group2
Move red circle to group1
```
---
# HTML5 Canvas How to avoid Memory leaks Tip
> Prevent memory leaks in HTML5 Canvas applications with Konva.js. Learn how to properly destroy shapes, detach events, and manage canvas resources.
Source: https://konvajs.org/docs/performance/Avoid_Memory_Leaks.html
### Deleting shapes
There are two very close methods `remove()` and `destroy()`. If you need to completely delete a node you should `destroy()` it. The `destroy()` method deletes all references to node from the KonvaJS engine. If you are going to reuse a node you should `remove()` it then later you can add it again to any container.
### Tweening
When you are using `Konva.Tween` instance you have to destroy it after usage.
Here's a demo showing proper memory management:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// Create circle
const circle = new Konva.Circle({
x: 100,
y: 100,
radius: 30,
fill: 'red'
});
layer.add(circle);
// Add buttons
const addButton = document.createElement('button');
addButton.textContent = 'Add Circle';
document.body.appendChild(addButton);
const removeButton = document.createElement('button');
removeButton.textContent = 'Remove Circle';
document.body.appendChild(removeButton);
const animateButton = document.createElement('button');
animateButton.textContent = 'Animate';
document.body.appendChild(animateButton);
// Handle adding/removing
addButton.addEventListener('click', () => {
layer.add(circle);
});
removeButton.addEventListener('click', () => {
// Just remove from layer, can be added back
circle.remove();
});
animateButton.addEventListener('click', () => {
// Using to() method which auto-destroys the tween
circle.to({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
duration: 1
});
// If using Tween directly, make sure to destroy it
const tween = new Konva.Tween({
node: circle,
rotation: 360,
duration: 1,
onFinish: function() {
// Clean up the tween
tween.destroy();
}
}).play();
});
```
```js
import { Stage, Layer, Circle } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [isVisible, setIsVisible] = useState(true);
const [position, setPosition] = useState({ x: 100, y: 100 });
const [rotation, setRotation] = useState(0);
const handleAdd = () => {
setIsVisible(true);
};
const handleRemove = () => {
setIsVisible(false);
};
const handleAnimate = () => {
// Update position
setPosition({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight
});
// Update rotation
setRotation(rotation + 360);
};
return (
Add Circle
Remove Circle
Animate
{isVisible && (
)}
);
};
export default App;
```
```js
Add Circle
Remove Circle
Animate
```
---
# Automatic Redraws — Do You Need draw() or batchDraw()?
> Konva redraws automatically since version 8, so layer.draw() and layer.batchDraw() are no longer needed after changing a shape. When a manual redraw is still required, and how to turn batching off.
Source: https://konvajs.org/docs/performance/Batch_Draw.html
**Short answer: no. Since Konva 8, you do not need to call `draw()` or
`batchDraw()` after changing a shape.**
```js
// This is enough. Konva schedules the redraw itself.
rect.fill('red');
rect.x(120);
// Both of these are redundant.
layer.draw();
layer.batchDraw();
```
Setting any attribute marks the layer as dirty, and Konva redraws it once on
the next animation frame. Changing ten attributes in a row still produces one
redraw, so calling `batchDraw()` yourself adds nothing — the batching already
happened.
You will still see `layer.draw()` in older tutorials, in answers written before
2021, and in code that AI assistants generate, because that is what the corpus
is full of. It is harmless, just unnecessary. Deleting those calls will not
change what you see on screen.
## When you do still need a manual redraw
Auto-draw reacts to *Konva* changes. If something changes outside Konva's
knowledge, nothing marks the layer dirty and you have to say so yourself.
The usual case is a `Konva.Image` backed by a source that mutates on its own —
a `` element, an animated GIF, or a raw canvas another library draws
into:
```js
// An external library drew a new GIF frame into our canvas.
// Konva has no attribute change to notice, so ask for the redraw.
function onDrawFrame(ctx, frame) {
ctx.drawImage(frame.buffer, 0, 0);
layer.draw();
}
```
For video, prefer [`Konva.Animation`](/docs/animations/Create_an_Animation.html),
which redraws every frame for you. There is a worked example in
[Video on canvas](/docs/sandbox/Video_On_Canvas.html) and
[GIF on canvas](/docs/sandbox/GIF_On_Canvas.html).
## Turning batching off
`Konva.autoDrawEnabled = false` restores the old behaviour, where nothing is
drawn until you ask. It is worth it only if you are driving the render loop
yourself and want exact control over when frames happen. With it off,
`batchDraw()` becomes useful again: it collapses many calls into one redraw per
frame, instead of redrawing on every one.
The demo below turns auto-draw off so `batchDraw()` has something to do. The
rectangle rotates on `mousemove`, an event that can fire far more often than the
screen refreshes.
**Instructions:** Move your mouse over the stage to spin the rectangle
```js
import Konva from 'konva';
// Turned off on purpose, so batchDraw has a job to do
Konva.autoDrawEnabled = false;
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: stage.width() / 2 - 50,
y: stage.height() / 2 - 25,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
layer.add(rect);
stage.on('mousemove', () => {
// rotate rectangle on mouse move
rect.rotate(5);
// auto-draw is off, so ask for a batched redraw
layer.batchDraw();
});
```
```js
import { Stage, Layer, Rect } from 'react-konva';
import { useRef, useEffect } from 'react';
// Turned off on purpose, so batchDraw has a job to do
Konva.autoDrawEnabled = false;
const App = () => {
const rectRef = useRef(null);
const layerRef = useRef(null);
useEffect(() => {
const stage = rectRef.current.getStage();
stage.on('mousemove', () => {
// rotate rectangle on mouse move
rectRef.current.rotate(5);
// auto-draw is off, so ask for a batched redraw
layerRef.current.getLayer().batchDraw();
});
}, []);
return (
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Disable Perfect Drawing Tip
> Improve Konva canvas performance by disabling perfectDrawEnabled to skip buffer canvas rendering for shapes with opacity.
Source: https://konvajs.org/docs/performance/Disable_Perfect_Draw.html
In some cases drawing on canvas has unexpected results.
For example, let's draw a shape with fill, stroke and opacity.
As strokes are drawn on top of fill, there's a line of half the size of the stroke inside the shape which is darker
because it's the intersection of the fill and the stroke.
Probably that is not expected for you. So `Konva` fixes such behavior with using buffer canvas.
In this case `Konva` is doing these steps:
1. Draw shape on buffer canvas
2. Fill and stroke it WITHOUT opacity
3. Apply opacity on layer's canvas
4. Then draw on layer canvas result from buffer
But using buffer canvas might drop performance. So you can disable such fixing:
```javascript
shape.perfectDrawEnabled(false);
```
See difference here:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// With perfect drawing (default)
const perfectCircle = new Konva.Circle({
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 10,
opacity: 0.5,
});
// Without perfect drawing
const nonPerfectCircle = new Konva.Circle({
x: 250,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 10,
opacity: 0.5,
perfectDrawEnabled: false,
});
// Add labels
const perfectLabel = new Konva.Text({
x: 50,
y: 170,
text: 'Perfect Drawing',
fontSize: 16,
});
const nonPerfectLabel = new Konva.Text({
x: 200,
y: 170,
text: 'Perfect Drawing Disabled',
fontSize: 16,
});
layer.add(perfectCircle);
layer.add(nonPerfectCircle);
layer.add(perfectLabel);
layer.add(nonPerfectLabel);
```
```js
import { Stage, Layer, Circle, Text } from 'react-konva';
const App = () => {
return (
{/* With perfect drawing (default) */}
{/* Without perfect drawing */}
{/* Labels */}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Layer Management Performance Tip
> Improve HTML5 Canvas performance with Konva.js multi-layer architecture. Separate static and dynamic content across layers to avoid unnecessary redraws.
Source: https://konvajs.org/docs/performance/Layer_Management.html
When creating Konva applications, the most important thing to consider,
in regards to performance, is layer management. One of the things that makes
Konva stand out from other canvas libraries is that it enables us to create
individual layers, each with their own canvas elements. This means that we can
animate, transition, or update some stage elements, while not redrawing others.
If we inspect the DOM of a Konva stage, we'll see that there is actually one
canvas element per layer.
This tutorial has two layers, one layer that's animated, and another static layer
that contains text. Since there's no reason to continually redraw the text, it's placed in its own layer.
**Note: Do not create too many layers. Usually 3-5 is max.**
Below is a demo showing efficient layer management:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
// Static layer for text
const textLayer = new Konva.Layer();
stage.add(textLayer);
// Animated layer for shapes
const animLayer = new Konva.Layer();
stage.add(animLayer);
// Add static text
const text = new Konva.Text({
x: 20,
y: 20,
text: 'This text is in a static layer.\nThe circle below is in an animated layer.',
fontSize: 16,
fill: 'black'
});
textLayer.add(text);
// Add animated circle
const circle = new Konva.Circle({
x: 100,
y: 100,
radius: 30,
fill: 'red',
});
animLayer.add(circle);
// Create animation
const anim = new Konva.Animation((frame) => {
// Move circle in a figure-8 pattern
const scale = 100;
const centerX = stage.width() / 2;
const centerY = stage.height() / 2;
circle.x(centerX + Math.sin(frame.time / 1000) * scale);
circle.y(centerY + Math.sin(frame.time / 2000) * scale);
}, animLayer);
anim.start();
```
```js
import { Stage, Layer, Text, Circle } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const circleRef = useRef(null);
useEffect(() => {
const anim = new Konva.Animation((frame) => {
const scale = 100;
const centerX = window.innerWidth / 2;
const centerY = window.innerHeight / 2;
circleRef.current.x(centerX + Math.sin(frame.time / 1000) * scale);
circleRef.current.y(centerY + Math.sin(frame.time / 2000) * scale);
}, circleRef.current.getLayer());
anim.start();
return () => anim.stop();
}, []);
return (
{/* Static layer for text */}
{/* Animated layer for shapes */}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Listening False Performance Tip
> Boost HTML5 Canvas performance with Konva.js by disabling event listening on shapes that don't need interaction. Set listening:false to skip hit detection.
Source: https://konvajs.org/docs/performance/Listening_False.html
If you have a lot of shapes on the canvas and you don't need to detect events for some of them,
you can set `listening` to `false` to improve performance.
When `listening = false`, the shape will be ignored from event detection (like mouseover, drag and drop, click, etc).
This can significantly improve performance for complex applications.
Below is a demo showing the performance difference between listening and non-listening shapes:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// Create many circles with listening enabled
for (let i = 0; i < 100; i++) {
const circle = new Konva.Circle({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
radius: 20,
fill: 'blue',
opacity: 0.5,
// Enable event detection (default)
listening: true,
});
// Add hover effect
circle.on('mouseover', function() {
this.fill('red');
});
circle.on('mouseout', function() {
this.fill('blue');
});
layer.add(circle);
}
// Create many circles with listening disabled
for (let i = 0; i < 1000; i++) {
const circle = new Konva.Circle({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
radius: 20,
fill: 'green',
opacity: 0.5,
// Disable event detection for better performance
listening: false,
});
layer.add(circle);
}
// Add text explanation
const text = new Konva.Text({
x: 10,
y: 10,
text: 'Blue circles (100) have event listeners (hover them)\nGreen circles (1000) have no listeners (better performance)',
fontSize: 16,
fill: 'black',
});
layer.add(text);
```
```js
import { Stage, Layer, Circle, Text } from 'react-konva';
import { useState } from 'react';
// Generate circles data
const listeningCircles = Array.from({ length: 100 }, (_, i) => ({
id: i,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
}));
const nonListeningCircles = Array.from({ length: 1000 }, (_, i) => ({
id: i + 100,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
}));
const App = () => {
const [hoveredId, setHoveredId] = useState(null);
return (
{/* Circles with event listeners */}
{listeningCircles.map((circle) => (
setHoveredId(circle.id)}
onMouseLeave={() => setHoveredId(null)}
/>
))}
{/* Circles without event listeners */}
{nonListeningCircles.map((circle) => (
))}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Optimize Animation Performance Tip
> Boost Konva animation performance with shape caching, Konva.Animation, and minimizing animated nodes.
Source: https://konvajs.org/docs/performance/Optimize_Animation.html
When creating animations with Konva, it's important to optimize them for better performance.
Here are some key tips:
1. Use `Konva.Animation` instead of `requestAnimationFrame` directly
2. Only animate properties that need to change
3. Consider using shape caching for complex shapes
4. Minimize the number of nodes being animated
Below is a demo showing optimized animation techniques:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// Create a complex star shape
const star = new Konva.Star({
x: stage.width() / 2,
y: stage.height() / 2,
numPoints: 6,
innerRadius: 40,
outerRadius: 70,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
});
// Cache the shape for better performance
star.cache();
layer.add(star);
// Create simple circle that doesn't need caching
const circle = new Konva.Circle({
x: 100,
y: 100,
radius: 20,
fill: 'red',
});
layer.add(circle);
// Create optimized animation
const anim = new Konva.Animation((frame) => {
// Rotate star (cached shape)
star.rotation(frame.time * 0.1);
// Move circle in a circle pattern
circle.x(100 + Math.cos(frame.time * 0.002) * 50);
circle.y(100 + Math.sin(frame.time * 0.002) * 50);
}, layer);
// Add start/stop button
const button = document.createElement('button');
button.textContent = 'Toggle Animation';
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
document.body.appendChild(button);
let isPlaying = true;
button.addEventListener('click', () => {
if (isPlaying) {
anim.stop();
button.textContent = 'Start Animation';
} else {
anim.start();
button.textContent = 'Stop Animation';
}
isPlaying = !isPlaying;
});
anim.start();
```
```js
import { Stage, Layer, Star, Circle } from 'react-konva';
import { useState, useEffect, useRef } from 'react';
const App = () => {
const [isPlaying, setIsPlaying] = useState(true);
const starRef = useRef(null);
const circleRef = useRef(null);
const animRef = useRef(null);
useEffect(() => {
// Cache the star shape for better performance
if (starRef.current) {
starRef.current.cache();
}
// Create animation
const anim = new Konva.Animation((frame) => {
// Rotate star (cached shape)
starRef.current.rotation(frame.time * 0.1);
// Move circle in a circle pattern
circleRef.current.x(100 + Math.cos(frame.time * 0.002) * 50);
circleRef.current.y(100 + Math.sin(frame.time * 0.002) * 50);
}, starRef.current.getLayer());
animRef.current = anim;
anim.start();
return () => anim.stop();
}, []);
const toggleAnimation = () => {
if (isPlaying) {
animRef.current.stop();
} else {
animRef.current.start();
}
setIsPlaying(!isPlaying);
};
return (
{isPlaying ? 'Stop Animation' : 'Start Animation'}
);
};
export default App;
```
```js
{{ isPlaying ? 'Stop Animation' : 'Start Animation' }}
```
---
# HTML5 Canvas Optimize Strokes Performance Tip
> Optimize Konva stroke rendering performance by disabling shadowForStrokeEnabled to skip extra drawing passes.
Source: https://konvajs.org/docs/performance/Optimize_Strokes.html
When drawing shapes with strokes and shadows in Konva, there's an extra internal drawing step that occurs.
This is because Konva needs to ensure that the stroke's shadow is drawn correctly.
However, this can impact performance, especially when dealing with many shapes.
To optimize performance, you can disable the stroke shadow by setting `shadowForStrokeEnabled(false)`.
This is particularly useful when you don't need the stroke to cast a shadow.
Below is a demo showing the performance difference with and without stroke shadows:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// Create shape with shadow for stroke (default)
const circleWithShadow = new Konva.Circle({
x: 100,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: { x: 5, y: 5 },
shadowOpacity: 0.5,
});
// Create shape without shadow for stroke (optimized)
const circleOptimized = new Konva.Circle({
x: 250,
y: 100,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: { x: 5, y: 5 },
shadowOpacity: 0.5,
shadowForStrokeEnabled: false,
});
// Add labels
const defaultLabel = new Konva.Text({
x: 50,
y: 170,
text: 'With Stroke Shadow',
fontSize: 16,
});
const optimizedLabel = new Konva.Text({
x: 200,
y: 170,
text: 'Without Stroke Shadow\n(Better Performance)',
fontSize: 16,
});
// Add FPS counter
const fpsText = new Konva.Text({
x: 10,
y: 10,
text: 'FPS: 0',
fontSize: 16,
});
layer.add(circleWithShadow);
layer.add(circleOptimized);
layer.add(defaultLabel);
layer.add(optimizedLabel);
layer.add(fpsText);
// Create animation to demonstrate performance
const anim = new Konva.Animation((frame) => {
circleWithShadow.rotation(frame.time * 0.1);
circleOptimized.rotation(frame.time * 0.1);
// Update FPS counter
fpsText.text('FPS: ' + frame.frameRate.toFixed(1));
}, layer);
anim.start();
```
```js
import { Stage, Layer, Circle, Text } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const circleWithShadowRef = useRef(null);
const circleOptimizedRef = useRef(null);
const fpsTextRef = useRef(null);
useEffect(() => {
const anim = new Konva.Animation((frame) => {
// Rotate circles
circleWithShadowRef.current.rotation(frame.time * 0.1);
circleOptimizedRef.current.rotation(frame.time * 0.1);
// Update FPS counter
fpsTextRef.current.text('FPS: ' + frame.frameRate.toFixed(1));
}, circleWithShadowRef.current.getLayer());
anim.start();
return () => anim.stop();
}, []);
return (
{/* Circle with shadow for stroke (default) */}
{/* Circle without shadow for stroke (optimized) */}
{/* Labels */}
{/* FPS counter */}
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Shape Caching Performance Tip
> Speed up HTML5 Canvas rendering with Konva.js shape caching. Cache complex shapes as images for dramatically better performance.
Source: https://konvajs.org/docs/performance/Shape_Caching.html
If you have a complex shape with many drawing operations, or if you're applying filters,
you can improve performance by caching the shape. When you cache a shape, Konva will draw
it onto an internal canvas buffer. After that, instead of redrawing the shape every time,
Konva will simply use the cached version.
This is particularly useful for:
1. Complex shapes with many drawing operations
2. Shapes with filters
3. Shapes that don't change often but need to be redrawn frequently
To cache a shape, simply call the `cache()` method. You can clear the cache with `clearCache()`.
## How caching works?
When you call the `cache()` method on a shape, Konva:
1. Creates an internal canvas buffer
2. Draws the shape onto this buffer
3. Stores this buffer for future use
After caching, instead of redrawing the shape every time it needs to be displayed, Konva simply uses the cached version from the buffer. This is much faster than redrawing the shape repeatedly.
## Guidelines
1. Don't cache simple shapes without filters. It may be faster to render it dirrectly, than from cached version.
2. Every cached node creates several canvas buffers. So don't overuse it, as it will consume a lot of memory.
3. It is better to cache groups of shapes, then to cache each shape individually.
4. Remember to always measure performance with and without caching to see the actual difference.
Below is a demo showing the performance difference between cached and non-cached complex shapes:
**Instructions:**
- Click anywhere on the stage to add 1000 more circles
- Toggle the checkbox to enable/disable caching
- Watch the FPS counter to see the performance difference
- The group of circles rotates continuously
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// Create a group of circles
const group = new Konva.Group({
x: stage.width() / 2,
y: stage.height() / 2,
});
layer.add(group);
// Add initial circles
const addCircles = (count) => {
const radius = 300;
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * radius;
const x = Math.cos(angle) * distance;
const y = Math.sin(angle) * distance;
const circle = new Konva.Circle({
x,
y,
radius: 5 + Math.random() * 10,
fill: Konva.Util.getRandomColor(),
shadowColor: 'black',
shadowBlur: 10,
shadowOpacity: 0.5,
shadowOffset: { x: 2, y: 2 },
listening: false,
});
group.add(circle);
}
};
// Add initial circles
addCircles(5000);
// Add FPS counter
const fpsText = new Konva.Text({
x: 10,
y: 10,
text: 'FPS: 0',
fontSize: 16,
fill: 'white',
shadowColor: 'black',
shadowBlur: 5,
shadowOffset: { x: 1, y: 1 }
});
layer.add(fpsText);
// Add circle count text
const countText = new Konva.Text({
x: 10,
y: 40,
text: 'Circles: 1000',
fontSize: 16,
fill: 'white',
shadowColor: 'black',
shadowBlur: 5,
shadowOffset: { x: 1, y: 1 }
});
layer.add(countText);
// Create animation
const anim = new Konva.Animation((frame) => {
group.rotation(frame.time * 0.05);
// Update FPS counter
fpsText.text('FPS: ' + frame.frameRate.toFixed(1));
}, layer);
// Add click handler to add more circles
stage.on('click', () => {
addCircles(1000);
countText.text('Circles: ' + group.children.length);
});
// Add DOM checkbox
const container = stage.container();
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = 'cache-toggle';
checkbox.style.position = 'absolute';
checkbox.style.top = '70px';
checkbox.style.left = '10px';
checkbox.style.zIndex = '100';
container.appendChild(checkbox);
const label = document.createElement('label');
label.htmlFor = 'cache-toggle';
label.textContent = 'Enable Caching';
label.style.position = 'absolute';
label.style.top = '70px';
label.style.left = '30px';
label.style.color = 'white';
label.style.textShadow = '0 0 5px black';
label.style.zIndex = '100';
container.appendChild(label);
// Toggle caching
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
group.cache();
} else {
group.clearCache();
}
});
anim.start();
```
```js
import { Stage, Layer, Circle, Text, Group } from 'react-konva';
import { useEffect, useRef, useState } from 'react';
import Konva from 'konva';
const App = () => {
const [circles, setCircles] = useState([]);
const [isCached, setIsCached] = useState(false);
const fpsTextRef = useRef(null);
const groupRef = useRef(null);
useEffect(() => {
// Add initial circles
addCircles(5000);
// Setup animation
const anim = new Konva.Animation((frame) => {
if (groupRef.current) {
groupRef.current.rotation(frame.time * 0.05);
}
// Update FPS counter
fpsTextRef.current.text('FPS: ' + frame.frameRate.toFixed(1));
}, fpsTextRef.current.getLayer());
anim.start();
return () => anim.stop();
}, []);
// Toggle caching
useEffect(() => {
if (groupRef.current) {
if (isCached) {
groupRef.current.cache();
} else {
groupRef.current.clearCache();
}
}
}, [isCached]);
// Add circles
const addCircles = (count) => {
const newCircles = [];
const radius = 300;
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * radius;
const x = Math.cos(angle) * distance;
const y = Math.sin(angle) * distance;
newCircles.push({
id: circles.length + i,
x,
y,
radius: 5 + Math.random() * 10,
fill: Konva.Util.getRandomColor(),
shadowColor: 'black',
shadowBlur: 10,
shadowOpacity: 0.5,
shadowOffset: { x: 2, y: 2 },
listening: false
});
}
setCircles(prev => [...prev, ...newCircles]);
};
return (
<>
addCircles(1000)}
>
{circles.map((circle) => (
))}
setIsCached(e.target.checked)}
/>
Enable Caching
>
);
};
export default App;
```
```js
Enable Caching
```
---
# Why Your HTML5 Canvas Looks Blurry, and How to Fix It
> Fix a blurry HTML5 canvas on retina and high-DPI screens. How devicePixelRatio works, why Konva already handles it, and why the common double-scaling fix makes it worse.
Source: https://konvajs.org/docs/posts/Canvas_Blurry.html
A canvas has two sizes, and they are easy to confuse:
- **The display size** — how large the element is on the page, in CSS pixels.
- **The bitmap size** — how many real pixels the browser allocates to draw into.
When the bitmap is smaller than the display size, the browser stretches the
result and the drawing looks soft. On a retina screen the display size is
already two or three device pixels wide for every CSS pixel, so a canvas that
allocates one bitmap pixel per CSS pixel is stretched by default.
## Konva already handles this
This is the part most advice gets wrong, so it is worth stating plainly.
`Konva.pixelRatio` defaults to `window.devicePixelRatio`, and every canvas Konva
creates allocates its bitmap at that ratio. **You give Konva sizes in CSS
pixels and it sizes the bitmap for you.**
```js
// This is correct on a retina screen. Nothing else is needed.
const stage = new Konva.Stage({
container: 'container',
width: 600, // CSS pixels
height: 400, // CSS pixels
});
```
On a 2× screen that stage draws into a 1200 × 800 bitmap and displays at
600 × 400. Text and strokes are crisp.
## The fix that makes it worse
Search for "blurry canvas" and you will find this pattern, which is correct for
a raw `` and wrong for Konva:
```js
// DO NOT do this in Konva
const dpr = window.devicePixelRatio;
stage.width(container.clientWidth * dpr);
stage.height(container.clientHeight * dpr);
stage.scale({ x: dpr, y: dpr });
```
It multiplies by the device pixel ratio twice: once here, and once inside Konva.
On a 2× screen the stage ends up **twice as large as its container** and renders
into a bitmap four times the area it needs. The drawing is sharp, so the mistake
is easy to miss — what you notice is that the scene is too big and the memory
use is high.
The advice is not wrong in general. It is what you would write against a bare
canvas context, where nothing sets the bitmap size for you. Konva is not a bare
context.
## When it really is blurry
If a Konva scene still looks soft, the cause is usually one of these.
### The container is scaled with CSS
A `transform: scale()` on the container, or a `width` in percent that does not
match the stage size, stretches the finished bitmap. Konva cannot see that.
Size the stage to the container instead — see
[Responsive Canvas](/docs/posts/responsive-canvas.html).
### pixelRatio was turned off
`Konva.pixelRatio = 1` is a real performance tip, and it costs sharpness on
retina screens. It is worth it for heavy scenes and not worth it for text.
If someone set it globally, that is your answer.
```js
Konva.pixelRatio = 1; // faster, and soft on retina
```
### A cached node was cached at the wrong ratio
`node.cache()` renders the node into its own bitmap. That bitmap is fixed at the
moment you cache it, so a node cached before a zoom is upscaled afterwards.
Cache at the ratio you will display at, or re-cache after the scale changes.
```js
node.cache({ pixelRatio: 2 });
```
### An exported image is blurry
Export is a separate setting. `toDataURL()` defaults to `pixelRatio: 1`
regardless of the screen, so an export looks softer than the canvas it came
from. Ask for more:
```js
stage.toDataURL({ pixelRatio: 2 });
```
There is more on this in
[High quality export](/docs/data_and_serialization/High-Quality-Export.html).
## Thin lines look soft, not blurry
A one-pixel stroke drawn at a whole coordinate straddles two pixels, because the
line is centred on the path. Half of it lands in each, and the browser blends
both. This is a different problem from device pixel ratio and it happens at any
zoom level.
Offset by half a pixel so the stroke fills one row:
```js
const line = new Konva.Line({
points: [10, 20.5, 200, 20.5], // .5 puts the 1px stroke inside one pixel row
stroke: 'black',
strokeWidth: 1,
});
```
The demo below draws the same horizontal line twice. The top one sits on a whole
coordinate and looks grey; the bottom one is offset by half a pixel and looks
black.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: 400,
height: 140,
});
const layer = new Konva.Layer();
stage.add(layer);
// On a whole coordinate: the 1px stroke straddles two pixel rows.
layer.add(
new Konva.Line({
points: [20, 40, 380, 40],
stroke: 'black',
strokeWidth: 1,
})
);
layer.add(
new Konva.Text({ x: 20, y: 48, text: 'y = 40 — blended across two rows', fontSize: 13 })
);
// Offset by half a pixel: the stroke fills a single row.
layer.add(
new Konva.Line({
points: [20, 100.5, 380, 100.5],
stroke: 'black',
strokeWidth: 1,
})
);
layer.add(
new Konva.Text({ x: 20, y: 108, text: 'y = 100.5 — inside one row', fontSize: 13 })
);
```
Konva can do this for you on shapes with a stroke. `strokeScaleEnabled` and
`perfectDrawEnabled` control related behaviour, and for crisp axis-aligned
strokes the half-pixel offset is the reliable answer.
## Reading the actual numbers
When you are not sure which size is wrong, print both:
```js
const canvas = stage.container().querySelector('canvas');
console.log('CSS size ', canvas.clientWidth, canvas.clientHeight);
console.log('bitmap size', canvas.width, canvas.height);
console.log('ratio ', canvas.width / canvas.clientWidth);
console.log('devicePixelRatio', window.devicePixelRatio);
```
If the ratio matches `devicePixelRatio`, the canvas is correct and the softness
is coming from CSS, from a cache, or from an export. If the ratio is `1` on a
retina screen, `pixelRatio` was turned off somewhere.
---
# What is the difference between position and offset in Konva
> Understand the difference between position (x, y) and offset (offsetX, offsetY) in Konva and how they affect shape origin and rotation.
Source: https://konvajs.org/docs/posts/Position_vs_Offset.html
There are several properties in Konva that looks similar and may lead some confusion but have a different effect and purpose.
In the post I will explain the difference between position (x and y coordinates) and offset (offsetX and offsetY).
So x and y properties define position of Node on canvas. If you set `draggable = true` property and start dragging, Konva will change x and y properties of the node. That logic will be applied for all nodes (even Konva.Line).
Position of a rectangle shape defines its top-left point. Position of circle defines its center.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
var layer = new Konva.Layer();
var rect = new Konva.Rect({
x: 20,
y: 20,
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
// add the shape to the layer
layer.add(rect);
var circle = new Konva.Circle({
x: 150,
y: 120,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
// add the shape to the layer
layer.add(circle);
var text = new Konva.Text();
layer.add(text);
// add the layer to the stage
stage.add(layer);
function updateText(e) {
text.text('Position: x = ' + e.target.x() + ' y = ' + e.target.y());
}
rect.on('dragmove', updateText);
circle.on('dragmove', updateText);
```
## Why do we need an `offset` property?
When you are changing the offset property it may looks like you are changing position of the node. But actually not. You are changing ORIGIN of the shape.
What is it origin? You may think of it as "point from where we start drawing of a shape" or "center of the shape" or "the point around which we rotating a shape".
Just a small note, long time ago offset property was called "center" in Konva codebase (when it was KineticJS project). But later it was refactored to "offset".
Take a look into this demo. All rectangles here have the same `y` position, but a different `offset` property.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const centerY = stage.height() / 2;
// rectangle with no offset
const rect1 = new Konva.Rect({
x: 50,
y: centerY,
width: 100,
height: 100,
fill: 'red',
stroke: 'black',
strokeWidth: 2,
});
layer.add(rect1);
// rectangle with offset at center
const rect2 = new Konva.Rect({
x: 200,
y: centerY,
width: 100,
height: 100,
fill: 'green',
stroke: 'black',
strokeWidth: 2,
offsetX: 50,
offsetY: 50,
rotation: 45
});
layer.add(rect2);
// rectangle with custom offset
const rect3 = new Konva.Rect({
x: 350,
y: centerY,
width: 100,
height: 100,
fill: 'blue',
stroke: 'black',
strokeWidth: 2,
offsetX: 100,
offsetY: 0,
rotation: 45
});
layer.add(rect3);
```
And you should understand that Konva has two main methods to define origin of the shape.
So "circle-like" shapes have origin at actual center of the shape (Circle, Ellipse, Wedge, Star, Ring ,etc).
When you set `{x, y}` of a circle you are defining "where will be the center of the circle".
And "rectangle-like" shapes has origin at TOP LEFT (Rectangle, Sprite, Text, Image, etc)
When you set `{x, y}` of a rectangle you are defining "where will be the top-left point of the rect".
So a shape will be rotated around its origin point (around its "center"). So if you set rotation 45 deg of a star it will be rotated around its actual center.
But if you set rotation 45 deg of rectangle, it will be rotated around top-left. But in some cases it is not convenient. Sometimes you may want to rotate the shape around its center.
In this case you can set `offset` property. By using it we will tell konva: "Hey, use this point as the new origin of the shape".
## How to set rotation point of a shape?
Now let's think you are placed a 100x100 rectangle in `x = 0, y = 0`, and now you want to rotate it around its center.
If you are not using offset you have to recalculate position of it't top left edge (recall you trigonometry lessons from the school).
You can do this by using something like this:
```javascript
const rotatePoint = ({ x, y }, rad) => {
const rcos = Math.cos(rad);
const rsin = Math.sin(rad);
return { x: x * rcos - y * rsin, y: y * rcos + x * rsin };
};
// will work for shapes with top-left origin, like rectangle
function rotateAroundCenter(node, rotation) {
//current rotation origin (0, 0) relative to desired origin - center (node.width()/2, node.height()/2)
const topLeft = { x: -node.width() / 2, y: -node.height() / 2 };
const current = rotatePoint(topLeft, Konva.getAngle(node.rotation()));
const rotated = rotatePoint(topLeft, Konva.getAngle(rotation));
const dx = rotated.x - current.x,
dy = rotated.y - current.y;
node.rotation(rotation);
node.x(node.x() + dx);
node.y(node.y() + dy);
}
// then use it
rotateAroundCenter(rect, 180);
```
Or you can set `offsetX = width / 2` and `offsetY = height / 2`. But the rectangle will be moved on the canvas (since you change its origin). So you will need to adjust the position.
Still have a question? Ask in comments.
---
# Resolving "Tainted canvases may not be exported" with Konva
> Fix the 'Tainted canvases may not be exported' CORS error when exporting or filtering cross-origin images in Konva.
Source: https://konvajs.org/docs/posts/Tainted_Canvas.html
When you are trying to export a canvas you may have an error like:
> Unable to get data URL. Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
> Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
Or when you apply filters you can have this error:
> Unable to apply filter. Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
> Unable to apply filter. The operation is insecure.
## Why do we have that insecure error?
That is a [CORS error](https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/CORS_enabled_image). For security reasons, a browser can mark a canvas as tainted when you load images from another domain. In that case the browser blocks canvas exporting into `dataURL` or `imageData` (that is what we are doing on export or when filters are used).
## How to fix CORS issue?
First you may try to set `crossOrigin = Anonymous` attribute of the loading image. This approach will work only if requested domain has an `Access-Control-Allow-Origin` headers that allow shared requests.
```js
import Konva from 'konva';
// Method 1: native image loading
const imageObj = new Image();
imageObj.onload = () => {
const image = new Konva.Image({
x: 50,
y: 50,
image: imageObj
});
layer.add(image);
};
imageObj.crossOrigin = 'Anonymous';
imageObj.src = url;
// Method 2: using Konva helper method
// crossOrigin is set automatically to Anonymous
Konva.Image.fromURL(url, (image) => {
image.setAttrs({
x: 50,
y: 50
});
layer.add(image);
});
```
```js
import { Stage, Layer, Image } from 'react-konva';
import useImage from 'use-image';
const MyImage = ({ url }) => {
// useImage hook handles crossOrigin automatically
const [image] = useImage(url, 'Anonymous');
return (
);
}
const App = () => {
return (
);
};
```
```js
```
### What if it doesn't work?
**It may still not work for all cases. If it doesn't work, then you have to configure your server in a different way (it is out of Konva scope) or you can try to store images somewhere else where CORS requests are supported.**
---
# Canvas Drag and Drop with JavaScript and Konva
> Implement canvas drag and drop with pointer events, hit detection, and Konva draggable nodes in JavaScript or React.
Source: https://konvajs.org/docs/posts/canvas-drag-and-drop.html
The Canvas API does not provide drag and drop for drawn shapes. A raw Canvas
application must implement four parts:
1. Store each shape and its position.
2. Detect the shape under the pointer.
3. Convert browser coordinates to canvas coordinates.
4. Update the shape and redraw the scene during a drag.
Pointer events support a mouse, pen, and touch input through one event model.
Call `setPointerCapture()` after `pointerdown` in a raw Canvas implementation.
The capture keeps move events active outside the canvas.
## Drag a Konva node
Konva implements hit detection and pointer tracking. Set `draggable` on a node.
Then store its final position in application state.
```jsx
import { useState } from 'react';
import { Stage, Layer, Rect, Text } from 'react-konva';
const App = () => {
const [position, setPosition] = useState({ x: 80, y: 100 });
const [dragging, setDragging] = useState(false);
return (
setDragging(true)}
onDragMove={(event) => {
setPosition({ x: event.target.x(), y: event.target.y() });
}}
onDragEnd={(event) => {
setDragging(false);
setPosition({ x: event.target.x(), y: event.target.y() });
}}
/>
);
};
export default App;
```
Konva changes the node position during a drag. The example also writes that
position to React state. This state keeps the application model synchronized.
For large scenes, do not update unrelated React state on each `dragmove` event.
Update the Konva node during the drag. Save the final application state on
`dragend` when other components do not need live coordinates.
Canvas shapes are not keyboard controls. Provide an HTML control for each
essential drag action. The control can move the same shape in fixed steps.
See the [drag and drop guide](/docs/drag_and_drop/Drag_and_Drop.html) for Vanilla,
React, Vue, Svelte, and Angular examples.
---
# How to Export an HTML5 Canvas as an Image
> Export an HTML5 canvas as PNG or JPEG with Konva, control output resolution, and avoid cross-origin image errors.
Source: https://konvajs.org/docs/posts/canvas-export-image.html
The browser can encode a canvas as a data URL or a `Blob`. Konva provides
`toDataURL()` and `toBlob()` on stages and nodes.
Use `toBlob()` for large exports when the browser supports it. A `Blob` does not
create the large encoded string that a data URL creates.
## Export a Konva stage from React
This example exports the stage as a PNG. The `pixelRatio` value doubles each
output dimension.
```jsx
import { useRef } from 'react';
import { Stage, Layer, Rect, Circle, Text } from 'react-konva';
const App = () => {
const stageRef = useRef(null);
const downloadImage = () => {
const dataUrl = stageRef.current.toDataURL({ pixelRatio: 2 });
const link = document.createElement('a');
link.download = 'konva-scene.png';
link.href = dataUrl;
document.body.appendChild(link);
link.click();
link.remove();
};
return (
<>
Download PNG
>
);
};
export default App;
```
The export includes only canvas content. HTML controls above the stage are not
part of the image.
## Set the file type and area
Pass `mimeType: 'image/jpeg'` for JPEG output. JPEG has no transparency, so add
a background shape before the other scene content.
Pass `x`, `y`, `width`, and `height` to export part of a stage. Pass
`pixelRatio` to increase the resolution without changing scene coordinates.
High pixel ratios use more memory. A 2× ratio creates four times as many output
pixels. Use the smallest ratio that meets the output requirement.
## Prevent a tainted canvas
The browser blocks export after Canvas draws an image without permitted
cross-origin access. The image server must return a suitable
`Access-Control-Allow-Origin` header. Load the image with `crossOrigin` set
before its `src` value.
Read the [tainted canvas guide](/docs/posts/Tainted_Canvas.html) for complete
image-loading examples. Read [high-quality export](/docs/data_and_serialization/High-Quality-Export.html)
for more resolution details.
---
# HTML5 Canvas Performance with Konva
> Improve HTML5 canvas performance with fewer redraws, separate Konva layers, disabled hit detection, and measured optimizations.
Source: https://konvajs.org/docs/posts/canvas-performance.html
Canvas performance depends on scene complexity, redraw frequency, pixel count,
and hit detection. Optimize the measured limit, not the node count alone.
Use browser performance tools on a representative device. Record frame time,
memory use, and interaction delay for the real scene.
## High-value Konva changes
- Put static and changing content on separate layers.
- Set `listening(false)` on nodes or layers that do not receive events.
- Hide nodes that are outside the visible area when the scene is very large.
- Cache a complex, stable node after measurement shows a rendering benefit.
- Reduce shadows, filters, and large transparent areas when they dominate frame time.
- Draw only after a data change. Konva batches layer draws automatically.
Do not cache every node. Each cache uses memory and creates extra work when its
content changes.
## Separate the interactive layer
This example puts 2,000 noninteractive circles on one layer. The layer skips
the hit canvas because `listening` is false. A second layer contains one
draggable shape.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: 420,
});
const backgroundLayer = new Konva.Layer({ listening: false });
const interactionLayer = new Konva.Layer();
stage.add(backgroundLayer, interactionLayer);
for (let index = 0; index < 2000; index += 1) {
backgroundLayer.add(
new Konva.Circle({
x: Math.random() * stage.width(),
y: Math.random() * stage.height(),
radius: 2 + Math.random() * 4,
fill: Konva.Util.getRandomColor(),
opacity: 0.55,
perfectDrawEnabled: false,
})
);
}
const handle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 36,
fill: '#ff922b',
stroke: '#7c2d12',
strokeWidth: 3,
draggable: true,
});
const label = new Konva.Text({
x: 16,
y: 16,
text: 'The orange circle stays interactive',
fontSize: 18,
fill: '#111827',
});
interactionLayer.add(handle, label);
```
Layer separation has a cost because each layer creates scene and hit canvases.
Use a small number of layers with clear update patterns.
Read [all Konva performance tips](/docs/performance/All_Performance_Tips.html)
for animation, caching, pixel ratio, and shape-specific guidance.
---
# Canvas Undo and Redo with React and Konva
> Add undo and redo to a canvas editor with immutable application state, a history index, React, and Konva.
Source: https://konvajs.org/docs/posts/canvas-undo-redo.html
Canvas pixels do not contain the application history. Store history in the data
model that produces the canvas scene.
For small editors, store an immutable snapshot after each complete user action.
Keep a current history index. Remove future snapshots after a new edit that
follows an undo.
Do not store Konva node instances in history. Store plain application data such
as positions, colors, text, and stable identifiers.
## Snapshot history in React
Drag the rectangle. Each completed drag adds one snapshot. The Undo and Redo
buttons change the current history index.
```jsx
import { useRef, useState } from 'react';
import { Stage, Layer, Rect, Text } from 'react-konva';
const initialRectangle = {
id: 'rectangle-1',
x: 80,
y: 100,
width: 170,
height: 110,
fill: '#4dabf7',
};
const App = () => {
const history = useRef([initialRectangle]);
const historyIndex = useRef(0);
const [rectangle, setRectangle] = useState(initialRectangle);
const commit = (nextRectangle) => {
const previousSnapshots = history.current.slice(0, historyIndex.current + 1);
history.current = [...previousSnapshots, nextRectangle];
historyIndex.current = history.current.length - 1;
setRectangle(nextRectangle);
};
const undo = () => {
if (historyIndex.current === 0) {
return;
}
historyIndex.current -= 1;
setRectangle(history.current[historyIndex.current]);
};
const redo = () => {
if (historyIndex.current === history.current.length - 1) {
return;
}
historyIndex.current += 1;
setRectangle(history.current[historyIndex.current]);
};
return (
<>
Undo
Redo
{
commit({
...rectangle,
x: event.target.x(),
y: event.target.y(),
});
}}
/>
>
);
};
export default App;
```
Commit one history entry for one user action. Do not add an entry for each
pointer move. This rule keeps undo behavior predictable and limits memory use.
Large documents can make full snapshots expensive. In that case, store commands
or patches with enough data to apply and reverse each change. This model is more
complex, so use it only after measurement shows a snapshot limit.
Define a history limit for a long editor session. Remove the oldest snapshots
after the limit is reached. Keep saved document versions separate from the local
undo history.
See the focused [React undo and redo example](/docs/react/Undo-Redo.html) for a
smaller implementation.
---
# Canvas vs SVG for Interactive Graphics
> Compare Canvas and SVG for interactive web graphics, and learn when the Konva canvas object model is a good fit.
Source: https://konvajs.org/docs/posts/canvas-vs-svg.html
Canvas and SVG use different rendering models. This difference affects
performance, interaction code, accessibility, and export behavior.
SVG keeps each shape as a DOM element. Browser tools and CSS can inspect each
element. This model works well for diagrams with a small number of shapes.
Canvas draws pixels into one bitmap. The browser does not keep the drawn shapes.
Canvas often works better for scenes with many shapes or frequent redraws.
| Requirement | Usual choice |
| --- | --- |
| Accessible DOM elements for each shape | SVG |
| CSS selectors and DOM events on each shape | SVG |
| Thousands of frequently changed shapes | Canvas |
| Pixel editing or image filters | Canvas |
| A retained shape model on Canvas | Konva |
These choices are not absolute. Measure the real scene on the devices that your
users have.
## The raw Canvas tradeoff
The Canvas API does not remember this rectangle after `fillRect()` returns:
```js
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
context.fillStyle = '#4dabf7';
context.fillRect(40, 40, 120, 80);
```
Your application must store the rectangle data. It must also redraw the scene
and detect pointer hits.
## Add an object model with Konva
Konva stores nodes in a scene graph. Each node has properties, events, and
methods. Konva also manages hit detection and redraws.
Drag the rectangle in this example:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: 360,
});
const layer = new Konva.Layer();
stage.add(layer);
const rectangle = new Konva.Rect({
x: 40,
y: 60,
width: 160,
height: 100,
fill: '#4dabf7',
stroke: '#1c7ed6',
strokeWidth: 3,
cornerRadius: 8,
draggable: true,
});
const label = new Konva.Text({
x: 40,
y: 20,
text: 'Drag the rectangle',
fontSize: 18,
fill: '#212529',
});
rectangle.on('dragmove', () => {
label.text(`Position: ${Math.round(rectangle.x())}, ${Math.round(rectangle.y())}`);
});
layer.add(label, rectangle);
```
Konva does not create accessible DOM elements for canvas shapes. Keep essential
controls and content in HTML. Add keyboard controls and accessible labels to
those HTML elements.
Use SVG when each graphic element must participate in the DOM. Use Konva when
the scene needs Canvas performance and an interactive object model.
---
# How to Make a Responsive Canvas with Konva
> Make a responsive HTML5 canvas with Konva, ResizeObserver, and a fixed virtual scene size that preserves its aspect ratio.
Source: https://konvajs.org/docs/posts/responsive-canvas.html
A canvas has a display size and a drawing size. A CSS resize changes only the
display size. This change can stretch the rendered pixels.
For a responsive Konva scene, define a fixed virtual size. Then scale the stage
from the available container width.
The scale formula is:
```text
scale = containerWidth / virtualWidth
```
The displayed height is `virtualHeight * scale`. All node coordinates stay in
the virtual coordinate system.
## Responsive React example
This example uses `ResizeObserver`. The observer responds when the container
changes size, not only when the browser window changes size.
```jsx
import { useEffect, useRef, useState } from 'react';
import { Stage, Layer, Rect, Circle, Text } from 'react-konva';
const VIRTUAL_WIDTH = 900;
const VIRTUAL_HEIGHT = 420;
const App = () => {
const containerRef = useRef(null);
const [containerWidth, setContainerWidth] = useState(VIRTUAL_WIDTH);
const [circlePosition, setCirclePosition] = useState({
x: VIRTUAL_WIDTH / 2,
y: VIRTUAL_HEIGHT / 2,
});
const [rectanglePosition, setRectanglePosition] = useState({
x: VIRTUAL_WIDTH - 190,
y: VIRTUAL_HEIGHT - 130,
});
useEffect(() => {
const container = containerRef.current;
const observer = new ResizeObserver(([entry]) => {
setContainerWidth(entry.contentRect.width);
});
observer.observe(container);
return () => observer.disconnect();
}, []);
const scale = containerWidth / VIRTUAL_WIDTH;
return (
{
setCirclePosition(event.target.position());
}}
/>
{
setRectanglePosition(event.target.position());
}}
/>
);
};
export default App;
```
Do not store scaled coordinates in application state. Store virtual coordinates.
Konva applies the stage scale during rendering and pointer conversion.
For high pixel density, Konva uses the device pixel ratio by default. Do not
multiply the stage size by `devicePixelRatio` unless the application manages
the backing store itself.
For more stage-resize details, see the [responsive sandbox demo](/docs/sandbox/Responsive_Canvas.html).
---
# How to access Konva nodes from react-konva?
> Learn how to access Konva nodes from react-konva using refs and event callbacks with code examples.
Source: https://konvajs.org/docs/react/Access_Konva_Nodes.html
In some cases you may need to use the `Konva` API directly. For example for exporting canvases or animations.
There are two ways to access Konva nodes/shapes from `react-konva`.
## Using the `refs` API.
You can use the [React refs API](https://react.dev/learn/manipulating-the-dom-with-refs) to access a Konva node.
```js
import React from 'react';
import { Stage, Layer, Circle } from 'react-konva';
const App = () => {
const shapeRef = React.useRef(null);
React.useEffect(() => {
// it will log `Konva.Circle` instance
console.log(shapeRef.current);
});
return (
);
};
export default App;
```
## Using an event object inside of the event callback
Another common way to access a Konva node is to just use an event object that you have as an argument in any event:
```js
import { Stage, Layer, Circle } from 'react-konva';
const App = () => {
const handleClick = (e) => {
// logs clicked Konva.Circle instance
console.log(e.target);
};
return (
);
};
export default App;
```
---
# How to export a canvas into an image from react-konva?
> Learn how to export canvas to an image in React with react-konva. Use stage.toDataURL() to save canvas as PNG or JPEG.
Source: https://konvajs.org/docs/react/Canvas_Export.html
## How to save a drawing from react-konva?
To export any `Konva` node into an image you can either use the [node.toDataURL()](/api/Konva.Node.html#toDataURL) or the [node.toImage()](https://konvajs.org/api/Konva.Node.html#toImage) API. Take a look into the [vanilla Konva image export demo](https://konvajs.org/docs/data_and_serialization/Stage_Data_URL.html).
You will need to use the [Refs API](/docs/react/Access_Konva_Nodes.html) to access a `Konva` node directly in order to call these methods.
```js
import React, { Fragment } from 'react';
import { Stage, Layer, Rect } from 'react-konva';
// function from https://stackoverflow.com/a/15832662/512042
function downloadURI(uri, name) {
var link = document.createElement('a');
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
const App = () => {
const width = window.innerWidth;
const height = window.innerHeight;
const stageRef = React.useRef(null);
const handleExport = () => {
const uri = stageRef.current.toDataURL();
console.log(uri);
// we also can save uri as file
downloadURI(uri, 'stage.png');
};
return (
Click here to export stage as image
);
};
export default App;
```
## Before you raise the pixel ratio
Browsers cap canvas width, height, and total area, so a high `pixelRatio` can
return a blank image rather than an error. See
[high quality export](/docs/data_and_serialization/High-Quality-Export.html) for
the limits and the ways around them.
---
# How to use portals in react-konva?
> Learn how to use canvas portals in react-konva to move nodes between layers for drag overlays and z-ordering.
Source: https://konvajs.org/docs/react/Canvas_Portal.html
## How does react-konva control the zIndex?
`react-konva` strictly follows the order of elements in the way that you define them in your render. For more info take a look into the [zIndex demo](/docs/react/zIndex.html).
## Is it possible to move a node into another container with `react-konva`?
Currently `react-konva` doesn't support the `React.createPortal` API.
But we can use ` ` component from [react-konva-utils package](https://github.com/konvajs/react-konva-utils)
Such a portal can be useful when you want to temporarily move a node into another container. The common use cases are:
1. Move a dragging shape into another layer for better performance
2. Show an element on top of other elements, but still keep it deep down in the components tree
**Instructions: try to drag a rectangle. You will see that it is visible on top, but in render it is still the first element.**
```js
import React from 'react';
import { Stage, Layer, Rect, Text, Circle, Line } from 'react-konva';
import { Portal } from 'react-konva-utils';
const App = () => {
const [isDragging, setDragging] = React.useState(false);
const [rectanglePosition, setRectanglePosition] = React.useState({
x: 20,
y: 50,
});
const [linePosition, setLinePosition] = React.useState({ x: 20, y: 200 });
return (
{
setDragging(true);
}}
onDragEnd={(event) => {
setRectanglePosition(event.target.position());
setDragging(false);
}}
/>
{
setLinePosition(event.target.position());
}}
/>
);
};
export default App;
```
---
# How to build complex canvas animations with React and react-spring?
> Learn how to create complex canvas animations in React with react-konva and react-spring. Animate colors, size, position, and shadows smoothly.
Source: https://konvajs.org/docs/react/Complex_Animations.html
For complex animations and high performance updates you can use the [react-spring library](https://github.com/pmndrs/react-spring).
```js
import React, { useState } from 'react';
import { Stage, Layer, Text } from 'react-konva';
import { Spring, animated } from '@react-spring/konva';
const ColoredRect = () => {
const [flag, setFlag] = useState(false);
const handleClick = () => setFlag((prev) => !prev);
return (
{(props) => (
)}
);
};
const App = () => {
return (
);
};
export default App;
```
[Open the interactive demo](https://codesandbox.io/embed/github/konvajs/site/tree/master/react-demos/complex_animations?hidenavigation=1&view=split&fontsize=10)
---
# How to draw custom shapes with React?
> Learn how to draw custom shapes with react-konva using the Shape component and HTML5 Canvas context.
Source: https://konvajs.org/docs/react/Custom_Shape.html
To create a custom shape with `react-konva`, we should use the `Shape` component.
When creating a custom shape, we need to define a drawing function that is passed to a Konva.Canvas renderer.
We can use the renderer to access the HTML5 Canvas context, and to use special methods like `context.fillStrokeShape(shape)` which automatically handle filling, stroking, and applying shadows.
```js
import React from 'react';
import { Stage, Layer, Shape } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
---
# Render DOM elements inside a canvas stage
> Learn how to render DOM elements like inputs and divs inside a Konva canvas stage using react-konva-utils Html component.
Source: https://konvajs.org/docs/react/DOM_Portal.html
## How to put DOM elements (like inputs or divs) inside of a Konva stage?
If you want to have some DOM nodes as part of your canvas tree you can use ` ` component from [react-konva-utils package](https://github.com/konvajs/react-konva-utils).
Remember that DOM nodes are not direct children of Konva containers. ` ` is just a wrapper to work with a Portal-like API. HTML content will be not visible if you try to export canvas as image.
```js
import React from 'react';
import { Stage, Layer, Rect } from 'react-konva';
import { Html } from 'react-konva-utils';
const App = () => {
return (
);
};
export default App;
```
---
# Drag and drop canvas shapes
> Learn how to implement drag and drop on canvas in React with react-konva. Make shapes draggable with the draggable prop and handle drag events.
Source: https://konvajs.org/docs/react/Drag_And_Drop.html
To enable drag&drop for any node on the canvas you just need to pass the `draggable` property into the component.
When you drag&drop a shape it is recommended to save its position into your app store. You can use the `onDragMove` and `onDragEnd` events for that purpose.
```js
import React from 'react';
import { Stage, Layer, Text } from 'react-konva';
const App = () => {
const [isDragging, setIsDragging] = React.useState(false);
const [position, setPosition] = React.useState({
x: 50,
y: 50,
});
return (
{
setIsDragging(true);
}}
onDragEnd={(e) => {
setIsDragging(false);
setPosition({
x: e.target.x(),
y: e.target.y(),
});
}}
/>
);
};
export default App;
```
---
# How to drop image elements into a canvas with React?
> Learn how to drag and drop images from the page into a Konva canvas using HTML5 drag and drop with React.
Source: https://konvajs.org/docs/react/Drop_Image.html
You can use HTML drag-and-drop events to add images or other elements to a canvas.
```js
import React from 'react';
import { Stage, Layer, Image } from 'react-konva';
import useImage from 'use-image';
const URLImage = ({ image, onChange }) => {
const [img] = useImage(image.src);
return (
{
onChange({ ...image, ...event.target.position() });
}}
/>
);
};
const App = () => {
const dragUrl = React.useRef();
const stageRef = React.useRef();
const nextImageId = React.useRef(0);
const [images, setImages] = React.useState([]);
return (
Try to drag and drop the image into the stage:
{
dragUrl.current = e.target.src;
}}
/>
{
e.preventDefault();
// register event position
stageRef.current.setPointersPositions(e);
// add image
setImages((currentImages) =>
currentImages.concat([
{
id: nextImageId.current++,
...stageRef.current.getPointerPosition(),
src: dragUrl.current,
},
])
);
}}
onDragOver={(e) => e.preventDefault()}
>
{images.map((image) => {
return (
{
setImages((currentImages) =>
currentImages.map((currentImage) =>
currentImage.id === newAttributes.id
? newAttributes
: currentImage
)
);
}}
/>
);
})}
);
};
export default App;
```
---
# How to listen to an event on a canvas shape with React and Konva?
> Learn how to handle canvas events in React with react-konva. Use onClick, onMouseEnter, onDragEnd, and other event props on Konva components.
Source: https://konvajs.org/docs/react/Events.html
With `react-konva` you can attach any events that `Konva` supports to canvas nodes.
To do that you can use the `onEventName` scheme, like `onMouseDown` for `mousedown`, `onDragEnd` for `dragend`, etc.
For the full list of events take a look into the [on() method documentation](/api/Konva.Node.html).
In this demo you can see how we are using `dragstart` and `dragend` events to create an interactive star field:
```js
import React from 'react';
import { Stage, Layer, Star, Text } from 'react-konva';
function generateShapes() {
return [...Array(10)].map((_, i) => ({
id: i.toString(),
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
rotation: Math.random() * 180,
isDragging: false,
}));
}
const App = () => {
const [stars, setStars] = React.useState(generateShapes());
const handleDragStart = (e) => {
const id = e.target.id();
setStars((currentStars) =>
currentStars.map((star) => {
return {
...star,
isDragging: star.id === id,
};
})
);
};
const handleDragEnd = (e) => {
const id = e.target.id();
const position = e.target.position();
setStars((currentStars) =>
currentStars.map((star) => {
return {
...star,
isDragging: false,
...(star.id === id ? position : {}),
};
})
);
};
return (
{stars.map((star) => (
))}
);
};
export default App;
```
---
# How to apply canvas filters with React and Konva?
> Learn how to apply canvas filters like blur and noise to shapes and images using react-konva with caching.
Source: https://konvajs.org/docs/react/Filters.html
To apply filters to a Konva node, you need to:
1. Cache the node using `node.cache()`
2. Apply filters using the `filters` prop
3. Re-cache the node when its properties change
```js
import React from 'react';
import Konva from 'konva';
import { Stage, Layer, Rect, Image } from 'react-konva';
import useImage from 'use-image';
// Example of functional component with image filter
const FilterImage = () => {
const [image] = useImage('https://konvajs.org/assets/lion.png', 'anonymous');
const imageRef = React.useRef();
// when image is loaded we need to cache the shape
React.useEffect(() => {
if (image) {
// you many need to reapply cache on some props changes like shadow, stroke, etc.
imageRef.current.cache();
}
}, [image]);
return (
);
};
// Example of class component with noise filter
// Try to click on rect to see color updates
const FilterRect = () => {
const [color, setColor] = React.useState('green');
const rectRef = React.useRef();
React.useEffect(() => {
if (rectRef.current) {
rectRef.current.cache();
}
}, []);
const handleClick = () => {
setColor(Konva.Util.getRandomColor());
// recache shape when we updated it
rectRef.current.cache();
};
return (
);
};
const App = () => {
return (
);
};
export default App;
```
---
# How to implement free drawing on canvas with React?
> Learn how to implement free drawing (whiteboard) on canvas in React with react-konva. Track mouse/touch events to draw lines freehand.
Source: https://konvajs.org/docs/react/Free_Drawing.html
This demo shows how to implement a free drawing app the "React way" with full vector representation.
Such an implementation should work well for many whiteboard apps. It allows you to simply add [undo/redo functions](/docs/react/Undo-Redo.html) and save the full state to the backend.
Note: It will get slower if you have too many lines in the state. So you will have to do some extra optimizations if you want to enable drawings of hundreds or thousands of lines.
The demo shows how to:
1. Track drawing state using `React.useRef` for performance
2. Store lines as vector data in React state
3. Handle mouse/touch events for drawing
4. Implement both pen and eraser tools using `globalCompositeOperation`
5. Create smooth lines with rounded caps and tension
```js
import React from 'react';
import { Stage, Layer, Line, Text } from 'react-konva';
const App = () => {
const [tool, setTool] = React.useState('pen');
const [lines, setLines] = React.useState([]);
const isDrawing = React.useRef(false);
const handleMouseDown = (e) => {
isDrawing.current = true;
const pos = e.target.getStage().getPointerPosition();
setLines([...lines, { tool, points: [pos.x, pos.y] }]);
};
const handleMouseMove = (e) => {
// no drawing - skipping
if (!isDrawing.current) {
return;
}
const stage = e.target.getStage();
const point = stage.getPointerPosition();
let lastLine = lines[lines.length - 1];
// add point
lastLine.points = lastLine.points.concat([point.x, point.y]);
// replace last
lines.splice(lines.length - 1, 1, lastLine);
setLines(lines.concat());
};
const handleMouseUp = () => {
isDrawing.current = false;
};
return (
{
setTool(e.target.value);
}}
>
Pen
Eraser
{lines.map((line, i) => (
))}
);
};
export default App;
```
---
# How to draw images on canvas with React?
> Learn how to draw images on canvas in React with react-konva. Load and display images using the Image component with useImage hook.
Source: https://konvajs.org/docs/react/Images.html
To render image on canvas with react you can use the [use-image](https://github.com/konvajs/use-image) hook.
```js
import React from 'react';
import { Stage, Layer, Image } from 'react-konva';
import useImage from 'use-image';
const URLImage = ({ src, ...rest }) => {
const [image] = useImage(src, 'anonymous');
return ;
};
const App = () => {
return (
);
};
export default App;
```
---
# Drawing canvas shapes with React
> Learn how to draw shapes on canvas with React using react-konva. Use Rect, Circle, Line, Text, and other Konva components as React elements.
Source: https://konvajs.org/docs/react/Shapes.html
All `react-konva` components correspond to `Konva` components of the same name.
All the parameters available for `Konva` objects are valid props for
corresponding `react-konva` components, unless noted otherwise.
Core shapes are: `Rect`, `Circle`, `Ellipse`, `Line`, `Image`, `Text`, `TextPath`, `Star`,
`Label`, `SVG Path`, `RegularPolygon`. You can also create [custom shapes](/docs/react/Custom_Shape.html).
Here is a demo showing some of the basic shapes with various styling options:
```js
import React from 'react';
import { Stage, Layer, Rect, Text, Circle, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
---
# How to animate canvas shapes with React and Konva?
> Learn how to animate canvas shapes in React with react-konva. Use React state, requestAnimationFrame, and Konva.Tween for smooth animations.
Source: https://konvajs.org/docs/react/Simple_Animations.html
Konva itself has two methods for animations: [Tween](/docs/tweens/Linear_Easing.html) and [Animation](/docs/animations/Rotation.html). You can apply both of them to nodes manually.
For simple use cases we recommend using the `node.to()` method. For more complex animations take a look at the [Complex react-konva animation demo](/docs/react/Complex_Animations.html).
The demo is using the [refs API](/docs/react/Access_Konva_Nodes.html) to access shape instances directly.
Instructions: Try to drag the rectangle to see it animate.
```js
import React, { useRef } from 'react';
import { Stage, Layer, Rect } from 'react-konva';
const MyRect = () => {
const rectRef = useRef(null);
const changeSize = () => {
// to() is a method of `Konva.Node` instances
rectRef.current.to({
scaleX: Math.random() + 0.8,
scaleY: Math.random() + 0.8,
duration: 0.2,
});
};
return (
);
};
const App = () => {
return (
);
};
export default App;
```
---
# How to test react-konva components
> Test react-konva components in a real browser instead of jsdom, where canvas is stubbed and tests pass while asserting nothing. Getting a stage handle, asserting on the scene graph, simulating drags, and checking for leaks.
Source: https://konvajs.org/docs/react/Testing.html
Testing canvas is not like testing DOM. Your shapes are not elements, so
Testing Library queries never find them, and the environment most React projects
test in cannot draw.
## Run canvas tests in a real browser
Use jsdom for your store and your logic. Use a real browser for anything that
touches a `Stage`.
This matters more than it sounds, because **jsdom does not fail loudly**. There
is no canvas implementation in it, so the usual fix is a mock that returns empty
values from every context call. Nothing throws. Your test goes green. What you
are actually asserting is:
| What you test | What jsdom gives you |
| --- | --- |
| `stage.getIntersection(pos)` | `null`, whatever is under the point |
| `text.width()` after measuring | a value from a font that was never loaded |
| `stage.toDataURL()` | a stub string, not an image |
| pixel readback | fully transparent |
Only the parts that are pure JavaScript — `find()`, attributes, the scene graph
— behave correctly. So a hit-testing test passes without testing hit testing.
That is the one failure a coding agent cannot catch for you: it writes the test,
the test is green, and the loop ends.
`react-konva` itself is tested this way. Its config is short:
```js
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
export default defineConfig({
test: {
browser: {
enabled: true,
provider: playwright(),
headless: process.env.HEADLESS !== 'false',
instances: [{ browser: 'chromium' }],
},
globals: true,
},
});
```
If you have existing jsdom tests, keep them. Split the run into two projects —
one jsdom for logic, one browser for canvas — rather than moving everything.
## Getting hold of the stage
A `ref` is the cleanest route:
```jsx
const stageRef = React.useRef(null);
render(… );
stageRef.current.find('#target');
```
When the component under test owns its own stage and gives you no ref, Konva
keeps a registry:
```js
import Konva from 'konva';
const stage = Konva.stages[Konva.stages.length - 1];
```
**`data-testid` on `` does not work.** The component renders a container
`div` and forwards only `id`, `accessKey`, `className`, `role`, `style`,
`tabIndex` and `title`. Anything else, including `data-*`, is dropped. Use `id`
if you want to query the container, and remember the container is a `div` — the
`` elements live inside it.
## Assert on the scene graph, not the DOM
Shapes are Konva nodes. Query them the way Konva does:
```js
const rect = stage.findOne('#card');
expect(rect.width()).toBe(120);
expect(stage.find('Circle')).toHaveLength(3);
expect(stage.find('.selected')).toHaveLength(1); // .name, not a CSS class
```
`find('Rect')` matches by node type, `find('.name')` by the `name` attribute,
and `findOne('#id')` by `id`. None of this needs the browser to have painted
anything, so these assertions are the fast, reliable core of a canvas test suite.
For anything about *appearance* — did this actually render, is it the right
colour — you need real pixels, which is the other reason for the browser:
```js
const ctx = layer.getContext();
const ratio = layer.getCanvas().getPixelRatio();
const { data } = ctx.getImageData(x * ratio, y * ratio, 1, 1);
expect([data[0], data[1], data[2]]).toEqual([255, 0, 0]);
```
Scale the coordinates by the layer's pixel ratio, or you sample the wrong pixel
on a retina screen. See [Blurry canvas](/docs/posts/Canvas_Blurry.html) for why.
## Simulating a drag
Konva does not listen for `mousemove` and `mouseup` on the shape, or even on the
stage container. It attaches them to `window` when a drag begins, so the drag
survives the pointer leaving the canvas. A synthetic drag has to follow that:
```js
const container = stage.container();
container.dispatchEvent(new MouseEvent('mousedown', { clientX: 20, clientY: 20, bubbles: true }));
window.dispatchEvent(new MouseEvent('mousemove', { clientX: 90, clientY: 60, bubbles: true }));
window.dispatchEvent(new MouseEvent('mouseup', { clientX: 90, clientY: 60, bubbles: true }));
```
Two things to know:
- **Dispatch `MouseEvent`, not only `PointerEvent`.** Konva's drag handling is
driven by the mouse family, so a pointer-only sequence can be ignored and the
drag becomes a silent no-op. Dispatching both is the safe option.
- **`Konva.dragDistance` defaults to 3 pixels.** A move of one or two pixels is
treated as a click and `dragstart` never fires. Move further than that, or set
the threshold in the test.
`react-konva` flushes React updates after each Konva event handler. You do not
need an extra microtask between events. Wait only if your own handler starts
asynchronous work.
## Clean up, and check nothing leaked
Every stage registers itself in `Konva.stages`. If a test unmounts and a stage
survives, later tests inherit it and start failing in confusing ways. Assert on
that rather than hoping:
```js
afterEach(async () => {
cleanup();
// Stage.destroy() is deferred a tick so a StrictMode remount can reuse it.
await new Promise((r) => setTimeout(r, 0));
const leaked = Konva.stages.length;
[...Konva.stages].forEach((s) => s.destroy());
expect(leaked).toBe(0);
});
```
## End-to-end tests
For a full application, drive a real browser with Playwright and skip synthetic
events entirely — real input reaches `window` naturally, so the trap above does
not apply:
```js
const start = await page.evaluate(() => {
const stage = window.Konva.stages[0];
return stage.findOne('#card').position();
});
await page.mouse.move(120, 120); // A point inside #card.
await page.mouse.down();
await page.mouse.move(260, 200);
await page.mouse.up();
const pos = await page.evaluate(() => {
const stage = window.Konva.stages[0];
return stage.findOne('#card').position();
});
expect(pos).toEqual({ x: start.x + 140, y: start.y + 80 });
```
Konva preserves the pointer offset inside the node. It checks `dragDistance`
against the total displacement from the start, so one move of more than three
pixels can start a drag.
## Plain Konva without React
Konva runs in Node with a backend module, which is a different setup from
everything above. See [Node.js](/docs/nodejs/nodejs-setup) — and note that Konva
prints the exact install and import lines if you forget them.
---
# How to resize and rotate canvas shapes with React and Konva?
> Learn how to resize and rotate canvas shapes in React with react-konva Transformer. Add interactive selection handles to shapes.
Source: https://konvajs.org/docs/react/Transformer.html
Currently there is no good, pure declarative "react-way" to use the Transformer tool.
But you can still use it with some small manual requests to the Konva nodes.
And it will work just fine.
The idea: you need to create a `Konva.Transformer` node, and attach it to the required node manually.
Instructions: Click on one of the rectangles to select it. Then you can:
- Drag it to move
- Use handles to resize
- Click outside to deselect
```js
import React from 'react';
import { Stage, Layer, Rect, Transformer } from 'react-konva';
const Rectangle = ({ shapeProps, isSelected, onSelect, onChange }) => {
const shapeRef = React.useRef();
const trRef = React.useRef();
React.useEffect(() => {
if (isSelected) {
// we need to attach transformer manually
trRef.current.nodes([shapeRef.current]);
}
}, [isSelected]);
return (
{
onChange({
...shapeProps,
x: e.target.x(),
y: e.target.y(),
});
}}
onTransformEnd={(e) => {
// transformer is changing scale of the node
// and NOT its width or height
// but in the store we have only width and height
// to match the data better we will reset scale on transform end
const node = shapeRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
// we will reset it back
node.scaleX(1);
node.scaleY(1);
onChange({
...shapeProps,
x: node.x(),
y: node.y(),
// set minimal value
width: Math.max(5, node.width() * scaleX),
height: Math.max(5, node.height() * scaleY),
});
}}
/>
{isSelected && (
{
// limit resize
if (Math.abs(newBox.width) < 5 || Math.abs(newBox.height) < 5) {
return oldBox;
}
return newBox;
}}
/>
)}
);
};
const initialRectangles = [
{
x: 10,
y: 10,
width: 100,
height: 100,
fill: 'red',
id: 'rect1',
},
{
x: 150,
y: 150,
width: 100,
height: 100,
fill: 'green',
id: 'rect2',
},
];
const App = () => {
const [rectangles, setRectangles] = React.useState(initialRectangles);
const [selectedId, selectShape] = React.useState(null);
const checkDeselect = (e) => {
// deselect when clicked on empty area
const clickedOnEmpty = e.target === e.target.getStage();
if (clickedOnEmpty) {
selectShape(null);
}
};
return (
{rectangles.map((rect, i) => {
return (
{
selectShape(rect.id);
}}
onChange={(newAttrs) => {
const rects = rectangles.slice();
rects[i] = newAttrs;
setRectangles(rects);
}}
/>
);
})}
);
};
export default App;
```
## What Transformer does not do
`Transformer` draws the handles and applies the scale. Snapping to other
objects, alignment guides, a shared bounding box for a multi-selection, and
per-shape aspect rules are all yours to build — see
[objects snapping](/docs/sandbox/Objects_Snapping.html) for one approach.
A production editor also needs text editing, templates, and export around the
Transformer. If you would rather not build those, [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=docs&utm_content=react-transformer) is a commercial
design editor SDK built on Konva by the Konva maintainers that ships them.
---
# How to implement undo/redo on canvas with React?
> Learn how to implement undo and redo functionality for canvas interactions using React state history management.
Source: https://konvajs.org/docs/react/Undo-Redo.html
To implement undo/redo functionality with React you don't need to use Konva's serialization and deserialization methods.
You just need to save a history of all the state changes within your app. There are many ways to do this. It may be simpler to do that if you use immutable structures.
Instructions: Try to move the square by dragging it. Then use the "undo" and "redo" buttons to revert or replay your actions.
```js
import React, { Component } from 'react';
import { Stage, Layer, Rect, Text } from 'react-konva';
const App = () => {
const [position, setPosition] = React.useState({ x: 20, y: 20 });
// We use refs to keep history to avoid unnecessary re-renders
const history = React.useRef([{ x: 20, y: 20 }]);
const historyStep = React.useRef(0);
const handleUndo = () => {
if (historyStep.current === 0) {
return;
}
historyStep.current -= 1;
const previous = history.current[historyStep.current];
setPosition(previous);
};
const handleRedo = () => {
if (historyStep.current === history.current.length - 1) {
return;
}
historyStep.current += 1;
const next = history.current[historyStep.current];
setPosition(next);
};
const handleDragEnd = (e) => {
// Remove all states after current step
history.current = history.current.slice(0, historyStep.current + 1);
const pos = {
x: e.target.x(),
y: e.target.y(),
};
// Push the new state
history.current = history.current.concat([pos]);
historyStep.current += 1;
setPosition(pos);
};
return (
);
};
export default App;
```
## Where a hand-built history stops
The history above records one value per step. A production editor has to record
grouped operations, so that a multi-select drag undoes as a single step, plus
transforms and images that finish loading after the action. That state machine
usually grows larger than the drawing code, so plan the history around document
operations rather than around raw node state.
---
# How to change the zIndex of nodes with React?
> Learn how to control zIndex and reorder shapes in react-konva by managing component state instead of manual z-ordering.
Source: https://konvajs.org/docs/react/zIndex.html
## How to change the zIndex and reorder components in `react-konva`?
When you are working with `Konva` directly you have many methods to change the order of nodes like `node.zIndex(5)`, `node.moveToTop()`, etc. [Tutorial](/docs/groups_and_layers/Layering.html).
But it is not recommended to use these methods when you are working with the React framework.
`react-konva` is strictly follows the order of the nodes exactly as you described them in your component. So instead of changing the zIndex manually, you just need to update the state of the app correctly.
Don't use the `zIndex` for your canvas components.
If you want to temporarily move a node into another container, for example when you want to show an overlay, take a look into the [Canvas Portal demo](/docs/react/Canvas_Portal.html).
**Instructions: Try to drag a circle. See how it goes to the top. We are doing this by manipulating the state so that componet render returns the correct order.**
```js
import React, { Component } from 'react';
import Konva from 'konva';
import { Stage, Layer, Circle } from 'react-konva';
function generateItems() {
const items = [];
for (let i = 0; i < 10; i++) {
items.push({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
id: 'node-' + i,
color: Konva.Util.getRandomColor(),
});
}
return items;
}
const App = () => {
const [items, setItems] = React.useState(generateItems());
const handleDragStart = (e) => {
const id = e.target.name();
const itemsCopy = items.slice();
const item = itemsCopy.find((i) => i.id === id);
const index = itemsCopy.indexOf(item);
// remove from the list:
itemsCopy.splice(index, 1);
// add to the top
itemsCopy.push(item);
setItems(itemsCopy);
};
const onDragEnd = (e) => {
const id = e.target.name();
const itemsCopy = items.slice();
const item = items.find((i) => i.id === id);
const index = items.indexOf(item);
// update item position
itemsCopy[index] = {
...item,
x: e.target.x(),
y: e.target.y(),
};
setItems(itemsCopy);
};
return (
{items.map((item) => (
))}
);
};
export default App;
```
---
# HTML5 Canvas Shape Resize Relative to Center
> Learn how to resize shapes from the center using Konva Transformer's centeredScaling property or ALT key.
Source: https://konvajs.org/docs/select_and_transform/Centered_Scaling.html
To resize a node into both sides at the same time you can set `centeredScaling` to true or hold `ALT` key while moving an anchor (even if `centeredScaling` is false).
**Instructions: Try to resize texts.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 50,
y: 80,
text: 'Simple text',
fontSize: 30,
draggable: true,
width: 200,
});
layer.add(text);
const text2 = new Konva.Text({
x: 50,
y: 180,
text: 'Simple text',
fontSize: 30,
draggable: true,
width: 200,
});
layer.add(text2);
const tr = new Konva.Transformer({
nodes: [text],
centeredScaling: true,
});
layer.add(tr);
const tr2 = new Konva.Transformer({
nodes: [text2],
});
layer.add(tr2);
````
```js
import { useRef, useEffect, useState } from 'react'
import { Stage, Layer, Text, Transformer } from 'react-konva';
const App = () => {
const [text1Position, setText1Position] = useState({ x: 50, y: 80 });
const [text2Position, setText2Position] = useState({ x: 50, y: 180 });
const text1Ref = useRef()
const text2Ref = useRef()
const tr1Ref = useRef()
const tr2Ref = useRef()
useEffect(() => {
tr1Ref.current.nodes([text1Ref.current]);
tr2Ref.current.nodes([text2Ref.current]);
}, []);
return (
setText1Position(e.target.position())}
onTransformEnd={(e) => setText1Position(e.target.position())}
/>
setText2Position(e.target.position())}
onTransformEnd={(e) => setText2Position(e.target.position())}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Force Update Tutorial
> Manually refresh Konva Transformer with forceUpdate when it cannot automatically detect deep changes in groups.
Source: https://konvajs.org/docs/select_and_transform/Force_Update.html
`Konva.Transformer` automatically tracks properties of attached nodes.
So it will adopt its own properties automatically.
But in some cases `Konva.Transformer` can't do this. Currently `Konva.Transformer` can not track deep changes inside `Konva.Group` node. In this case you will need to use `forceUpdate` method to reset transforming tools.
**Instructions: Click the button. See how transformer is changed.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const group = new Konva.Group({
x: 50,
y: 50,
draggable: true,
});
layer.add(group);
const text = new Konva.Text({
text: 'Some text here',
fontSize: 24,
});
group.add(text);
const rect = new Konva.Rect({
width: text.width(),
height: text.height(),
fill: 'yellow',
});
group.add(rect);
// add the shape to the layer
rect.moveToBottom();
const tr = new Konva.Transformer({
nodes: [group],
padding: 5,
// enable only one anchor
enabledAnchors: ['middle-left', 'middle-right'],
});
layer.add(tr);
const button = document.createElement('button');
button.innerHTML = 'Change text';
document.body.appendChild(button);
button.addEventListener('click', () => {
text.text('Something else is here');
rect.width(text.width());
// we need to update transformer manually
tr.forceUpdate();
});
````
```js
import { Stage, Layer, Text, Rect, Group, Transformer } from 'react-konva';
import { useState, useRef, useLayoutEffect } from 'react';
const App = () => {
const [text, setText] = useState('Some text here');
const [groupPosition, setGroupPosition] = useState({ x: 50, y: 50 });
const groupRef = useRef();
const textRef = useRef();
const rectRef = useRef();
const trRef = useRef();
const handleClick = () => {
setText('Something else is here');
};
useLayoutEffect(() => {
const transformer = trRef.current;
const label = textRef.current;
const background = rectRef.current;
if (!transformer || !label || !background) return;
background.size({ width: label.width(), height: label.height() });
transformer.nodes([groupRef.current]);
transformer.forceUpdate();
}, [text]);
return (
<>
Change text
setGroupPosition(event.target.position())}
onTransformEnd={(event) => setGroupPosition(event.target.position())}
>
>
);
};
export default App;
````
```js
Change text
```
---
# How to resize shape on canvas without changing its stroke size?
> Prevent stroke width from scaling during shape resize with Konva Transformer using ignoreStroke and strokeScaleEnabled.
Source: https://konvajs.org/docs/select_and_transform/Ignore_Stroke_On_Transform.html
Remember, that `Konva.Transformer` is changing `scaleX` and `scaleY` properties of a node.
By default, if you are transforming a shape, its stroke will be scaled too. In some cases that is not a good behavior.
There are two ways to prevent stroke scaling:
1. Reset scale of a shape
2. Use `shape.strokeScaleEnabled(false)` and `transformer.ignoreStroke(true)`
**Instructions: there are two rectangles to resize. The green one will reset its scale. The red one will just disable stroke scaling.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
// first way - reset scale on transform end
const rect1 = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: '#00ff00',
stroke: 'black',
strokeWidth: 5,
draggable: true,
});
layer.add(rect1);
const tr1 = new Konva.Transformer({
nodes: [rect1],
});
layer.add(tr1);
rect1.on('transformend', () => {
const scaleX = rect1.scaleX();
const scaleY = rect1.scaleY();
// apply the scale to the size, then reset it
rect1.scaleX(1);
rect1.scaleY(1);
rect1.width(Math.max(5, rect1.width() * scaleX));
rect1.height(Math.max(5, rect1.height() * scaleY));
});
// second way - disable stroke scaling
const rect2 = new Konva.Rect({
x: 200,
y: 50,
width: 100,
height: 100,
fill: '#ff0000',
stroke: 'black',
strokeWidth: 5,
draggable: true,
strokeScaleEnabled: false,
});
layer.add(rect2);
const tr2 = new Konva.Transformer({
nodes: [rect2],
ignoreStroke: true,
});
layer.add(tr2);
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rect1Attrs, setRect1Attrs] = useState({
x: 50,
y: 50,
width: 100,
height: 100,
});
const [rect2Position, setRect2Position] = useState({ x: 200, y: 50 });
const rect1Ref = useRef();
const rect2Ref = useRef();
const tr1Ref = useRef();
const tr2Ref = useRef();
useEffect(() => {
tr1Ref.current.nodes([rect1Ref.current]);
tr2Ref.current.nodes([rect2Ref.current]);
}, []);
return (
{
setRect1Attrs((attrs) => ({
...attrs,
...e.target.position(),
}));
}}
onTransformEnd={(e) => {
const node = rect1Ref.current;
const nextAttrs = {
x: node.x(),
y: node.y(),
width: node.width() * node.scaleX(),
height: node.height() * node.scaleY(),
};
node.scaleX(1);
node.scaleY(1);
setRect1Attrs(nextAttrs);
}}
/>
setRect2Position(e.target.position())}
onTransformEnd={(e) => setRect2Position(e.target.position())}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Shape Resize With Ratio Preserved
> Learn how to preserve aspect ratio when resizing shapes with Konva Transformer using keepRatio and SHIFT key.
Source: https://konvajs.org/docs/select_and_transform/Keep_Ratio.html
By default when you resize with corner anchors (`top-left`, `top-right`, `bottom-left` or `bottom-right`) Transformer will save ratio of a node.
You can set `keepRatio` to `false` if you don't need that behavior.
Even if you set `keepRatio` to `false` you can hold `SHIFT` to still keep ratio.
**Instructions: Try to resize texts.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 50,
y: 50,
text: 'keep ratio by default',
fontSize: 20,
draggable: true,
width: 200,
});
layer.add(text);
const text2 = new Konva.Text({
x: 50,
y: 150,
text: 'no ratio, but hold shift to keep ratio',
fontSize: 20,
draggable: true,
width: 200,
});
layer.add(text2);
const tr = new Konva.Transformer({
nodes: [text],
});
layer.add(tr);
const tr2 = new Konva.Transformer({
nodes: [text2],
keepRatio: false,
});
layer.add(tr2);
````
```js
import { Stage, Layer, Text, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [text1Position, setText1Position] = useState({ x: 50, y: 50 });
const [text2Position, setText2Position] = useState({ x: 50, y: 150 });
const text1Ref = useRef();
const text2Ref = useRef();
const tr1Ref = useRef();
const tr2Ref = useRef();
useEffect(() => {
tr1Ref.current.nodes([text1Ref.current]);
tr2Ref.current.nodes([text2Ref.current]);
}, []);
return (
setText1Position(e.target.position())}
onTransformEnd={(e) => setText1Position(e.target.position())}
/>
setText2Position(e.target.position())}
onTransformEnd={(e) => setText2Position(e.target.position())}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Shape Resize and Transform Limits
> Learn how to set minimum and maximum size limits when resizing shapes on HTML5 Canvas with Konva.js Transformer.
Source: https://konvajs.org/docs/select_and_transform/Resize_Limits.html
To limit or change resize and transform behavior you can use `boundBoxFunc` property.
It works a bit similar to [dragBoundFunc](/docs/drag_and_drop/Simple_Drag_Bounds.html).
**Instructions: Try to resize a shape. You will see that its width is limited to 200.**
The demo clamps the new bounding box to the limit instead of rejecting it. If you simply `return oldBox` when the size is over the limit, a fast mouse move overshoots the boundary in one event, and the shape gets stuck below the limit. Interpolating between `oldBox` and `newBox` lands exactly on the limit and keeps the opposite side of the shape pinned for every anchor.
Also you can control movement of every anchors individually. See [Resize Snap Demo](https://konvajs.org/docs/select_and_transform/Resize_Snaps.html).
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
draggable: true,
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
boundBoxFunc: (oldBox, newBox) => {
// limit resize to a maximum width of 200.
// clamp the box between oldBox and newBox instead of
// rejecting it, so a fast drag still lands exactly on the limit
if (newBox.width > 200) {
const t = (200 - oldBox.width) / (newBox.width - oldBox.width);
return {
x: oldBox.x + t * (newBox.x - oldBox.x),
y: oldBox.y + t * (newBox.y - oldBox.y),
width: 200,
height: oldBox.height + t * (newBox.height - oldBox.height),
rotation: newBox.rotation,
};
}
return newBox;
},
});
layer.add(tr);
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rectPosition, setRectPosition] = useState({ x: 50, y: 50 });
const rectRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([rectRef.current]);
}, []);
return (
setRectPosition(e.target.position())}
onTransformEnd={(e) => setRectPosition(e.target.position())}
/>
{
// limit resize to a maximum width of 200.
// clamp the box between oldBox and newBox instead of
// rejecting it, so a fast drag still lands exactly on the limit
if (newBox.width > 200) {
const t = (200 - oldBox.width) / (newBox.width - oldBox.width);
return {
x: oldBox.x + t * (newBox.x - oldBox.x),
y: oldBox.y + t * (newBox.y - oldBox.y),
width: 200,
height: oldBox.height + t * (newBox.height - oldBox.height),
rotation: newBox.rotation,
};
}
return newBox;
}}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Shape Resize Snapping
> Snap shape resize anchors to guide lines using anchorDragBoundFunc in Konva Transformer.
Source: https://konvajs.org/docs/select_and_transform/Resize_Snaps.html
In some applications, you may want to snap resizing near some values. Snapping makes a shape "sticky" near provided values and works like rounding.
You can control anchor position behavior with the [anchorDragBoundFunc](/api/Konva.Transformer.html#anchorDragBoundFunc) method.
```js
transformer.anchorDragBoundFunc(function (oldAbsPos, newAbsPos, event) {
// limit any another position on the x axis
return {
x: 0,
y: newAbsolutePosition.y,
};
});
```
**Instructions: Try to resize a shape. You will see how transformer is trying to snap to guide lines.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
// create guides
const horizontalLine = new Konva.Line({
points: [0, height / 2, width, height / 2],
stroke: '#000',
strokeWidth: 1,
dash: [4, 4],
});
layer.add(horizontalLine);
const verticalLine = new Konva.Line({
points: [width / 2, 0, width / 2, height],
stroke: '#000',
strokeWidth: 1,
dash: [4, 4],
});
layer.add(verticalLine);
const rect = new Konva.Rect({
x: 60,
y: 60,
width: 100,
height: 100,
fill: 'red',
draggable: true,
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
anchorDragBoundFunc: function (oldPos, newPos) {
const dist = Math.sqrt(Math.pow(newPos.x - width / 2, 2));
if (dist < 10) {
return {
...newPos,
x: width / 2,
};
}
return newPos;
},
});
layer.add(tr);
````
```js
import { Stage, Layer, Line, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rectPosition, setRectPosition] = useState({ x: 60, y: 60 });
const rectRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([rectRef.current]);
}, []);
return (
setRectPosition(e.target.position())}
onTransformEnd={(e) => setRectPosition(e.target.position())}
/>
{
const dist = Math.sqrt(Math.pow(newPos.x - window.innerWidth / 2, 2));
if (dist < 10) {
return {
...newPos,
x: window.innerWidth / 2,
};
}
return newPos;
}}
/>
);
};
export default App;
````
```js
```
---
# How to resize text on canvas?
> Learn how to resize text on HTML5 Canvas with Konva.js. Change font size when the Transformer is used to scale a Text shape.
Source: https://konvajs.org/docs/select_and_transform/Resize_Text.html
Remember, that `Konva.Transformer` is changing `scaleX` and `scaleY` properties of a node.
If you want to change width of the text, without changing its size, you should reset scale of a text back to 1 and adjust `width` accordingly.
You can use `transform` event to update text's properties as you need it.
**Instructions: Try to resize a text.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const text = new Konva.Text({
x: 50,
y: 50,
text: 'Hello from Konva! Try to resize me.',
fontSize: 24,
draggable: true,
width: 200,
});
layer.add(text);
const tr = new Konva.Transformer({
nodes: [text],
// enable only one anchor to see better what is happening
// we are changing width only
enabledAnchors: ['middle-left', 'middle-right'],
});
layer.add(tr);
text.on('transform', function () {
// reset scale on transform
text.setAttrs({
width: text.width() * text.scaleX(),
scaleX: 1,
});
});
````
```js
import { Stage, Layer, Text, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [textAttrs, setTextAttrs] = useState({
x: 50,
y: 50,
width: 200,
});
const textRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([textRef.current]);
}, []);
return (
{
setTextAttrs((attrs) => ({
...attrs,
...e.target.position(),
}));
}}
onTransform={() => {
const node = textRef.current;
const nextWidth = node.width() * node.scaleX();
node.scaleX(1);
setTextAttrs({
x: node.x(),
y: node.y(),
width: nextWidth,
});
}}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Shape Snap Rotation
> Snap shape rotation to specific angles like 0, 90, 180, and 270 degrees using Konva Transformer rotationSnaps.
Source: https://konvajs.org/docs/select_and_transform/Rotation_Snaps.html
In some applications, you may want to snap rotation near some values. Snapping makes a shape "sticky" near provided values and works like rounding.
Most common snaps are 0, 45, 90, 135, 180, etc degrees. Snaps allow simpler setting of rotation to exactly these values.
For instance, if you have snap point at 45 deg, a user will not be able to set rotation to 43 deg. It will be rounded to 45 deg. But a user still will be able to set rotation to 35 deg, as it is too far from 45 so it will not be snapped.
**Instructions: Try to rotate a shape. See snapping at 0, 90, 180 and 270 deg.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 50,
fill: 'yellow',
stroke: 'black',
draggable: true,
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
rotationSnaps: [0, 90, 180, 270],
rotationSnapTolerance: 30,
});
layer.add(tr);
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rectPosition, setRectPosition] = useState({ x: 50, y: 50 });
const rectRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([rectRef.current]);
}, []);
return (
setRectPosition(e.target.position())}
onTransformEnd={(e) => setRectPosition(e.target.position())}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Stop Shape Transform
> Programmatically stop an active transform using the stopTransform method on Konva Transformer.
Source: https://konvajs.org/docs/select_and_transform/Stop_Transform.html
If you need to stop transforming immediately you can use `stopTransform` method of `Konva.Transformer` instance.
**Instructions: Try to resize a shape. If width of the shape is bigger than 200 transforming will be stopped.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
draggable: true,
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
});
layer.add(tr);
rect.on('transform', function () {
const width = rect.width() * rect.scaleX();
if (width > 200) {
tr.stopTransform();
}
});
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rectPosition, setRectPosition] = useState({ x: 50, y: 50 });
const rectRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([rectRef.current]);
}, []);
return (
setRectPosition(e.target.position())}
onTransform={() => {
const node = rectRef.current;
const width = node.width() * node.scaleX();
if (width > 200) {
trRef.current.stopTransform();
}
}}
onTransformEnd={(e) => setRectPosition(e.target.position())}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Transform and Resize events
> Learn how to listen to transform and resize events on HTML5 Canvas with Konva.js Transformer. Handle transformstart, transform, and transformend.
Source: https://konvajs.org/docs/select_and_transform/Transform_Events.html
`Konva.Transformer` object has special transform events that you can use in your app: `transformstart`, `transform` and `transformend`.
These events also will be triggered on attached node.
**Instructions: Open console, try to transform, see logs**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
draggable: true,
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
});
layer.add(tr);
tr.on('transformstart', () => {
console.log('transform start');
});
tr.on('transform', () => {
console.log('transforming');
});
tr.on('transformend', () => {
console.log('transform end');
});
rect.on('transformstart', () => {
console.log('rect transform start');
});
rect.on('transform', () => {
console.log('rect transforming');
});
rect.on('transformend', () => {
console.log('rect transform end');
});
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rectPosition, setRectPosition] = useState({ x: 50, y: 50 });
const rectRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([rectRef.current]);
}, []);
return (
setRectPosition(e.target.position())}
onTransformStart={() => console.log('rect transform start')}
onTransform={() => console.log('rect transforming')}
onTransformEnd={(e) => {
console.log('rect transform end');
setRectPosition(e.target.position());
}}
/>
console.log('transform start')}
onTransform={() => console.log('transforming')}
onTransformEnd={() => console.log('transform end')}
/>
);
};
export default App;
````
```js
console.log('rect transform start')"
@transform="() => console.log('rect transforming')"
@transformend="() => console.log('rect transform end')"
/>
console.log('transform start')"
@transform="() => console.log('transforming')"
@transformend="() => console.log('transform end')"
/>
```
---
# Deep Style Konva Transformer
> Use anchorStyleFunc for advanced per-anchor styling of Konva Transformer, including custom sizes and visibility.
Source: https://konvajs.org/docs/select_and_transform/Transformer_Complex_Styling.html
You can use `anchorStyleFunc` property of `Konva.Transformer` to have deeper control on styling of anchors.
Also take a look into [Transformer Styling](/docs/select_and_transform/Transformer_Styling.html) for simpler use cases.
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
draggable: true,
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
anchorStyleFunc: (anchor) => {
// make all anchors circles
anchor.cornerRadius(50);
// make all anchors red
anchor.fill('red');
// make right-middle bigger
if (anchor.hasName('middle-right')) {
anchor.scale({ x: 2, y: 2 });
}
// make top-left invisible
if (anchor.hasName('top-left')) {
anchor.scale({ x: 0, y: 0 });
}
},
});
layer.add(tr);
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rectPosition, setRectPosition] = useState({ x: 50, y: 50 });
const rectRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([rectRef.current]);
}, []);
return (
setRectPosition(e.target.position())}
onTransformEnd={(e) => setRectPosition(e.target.position())}
/>
{
// make all anchors circles
anchor.cornerRadius(50);
// make all anchors red
anchor.fill('red');
// make right-middle bigger
if (anchor.hasName('middle-right')) {
anchor.scale({ x: 2, y: 2 });
}
// make top-left invisible
if (anchor.hasName('top-left')) {
anchor.scale({ x: 0, y: 0 });
}
}}
/>
);
};
export default App;
````
```js
```
---
# Style Konva Transformer
> Customize Konva Transformer appearance by changing anchor fill, stroke, size, border color, and corner radius.
Source: https://konvajs.org/docs/select_and_transform/Transformer_Styling.html
You can adjust styles of `Konva.Transformer` for your web app. You can change stroke, size and fill of all anchors.
Also you can change stroke color and size of border.
Also take a look into [Complex Transformer Styling](/docs/select_and_transform/Transformer_Complex_Styling.html) for fine tuning.
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'yellow',
stroke: 'black',
draggable: true,
});
layer.add(rect);
const tr = new Konva.Transformer({
nodes: [rect],
// add border
borderStroke: '#000',
borderStrokeWidth: 3,
// add anchors
anchorFill: '#fff',
anchorStroke: '#000',
anchorStrokeWidth: 2,
anchorSize: 20,
// make all anchors look like circles
anchorCornerRadius: 50,
});
layer.add(tr);
````
```js
import { Stage, Layer, Rect, Transformer } from 'react-konva';
import { useRef, useEffect, useState } from 'react';
const App = () => {
const [rectPosition, setRectPosition] = useState({ x: 50, y: 50 });
const rectRef = useRef();
const trRef = useRef();
useEffect(() => {
trRef.current.nodes([rectRef.current]);
}, []);
return (
setRectPosition(e.target.position())}
onTransformEnd={(e) => setRectPosition(e.target.position())}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Select Shape by Name Tutorial
> Learn how to select and find shapes by name in Konva using the find() method with the dot (.) selector.
Source: https://konvajs.org/docs/selectors/Select_by_Name.html
To select shapes by name with Konva, we can use the `find()` method using the `.` selector.
The `find()` method returns an array of nodes that match the selector string.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// create shapes with names
const circle1 = new Konva.Circle({
x: 50,
y: stage.height() / 2,
radius: 30,
fill: 'red',
name: 'myCircle'
});
const circle2 = new Konva.Circle({
x: 150,
y: stage.height() / 2,
radius: 30,
fill: 'green',
name: 'myCircle'
});
const rect = new Konva.Rect({
x: 250,
y: stage.height() / 2 - 25,
width: 50,
height: 50,
fill: 'blue',
name: 'myRect'
});
layer.add(circle1);
layer.add(circle2);
layer.add(rect);
// find all circles by name
const circles = layer.find('.myCircle');
circles.forEach(circle => {
// add animation to circles only
circle.to({
duration: 1,
rotation: 360,
easing: Konva.Easings.EaseInOut
});
});
```
```js
import { Stage, Layer, Circle, Rect } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const layerRef = useRef(null);
useEffect(() => {
// find all circles by name and animate them
const circles = layerRef.current.find('.myCircle');
circles.forEach(circle => {
circle.to({
duration: 1,
rotation: 360,
easing: Konva.Easings.EaseInOut
});
});
}, []);
return (
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Select Shape by Type Tutorial
> Learn how to select and find shapes by their type or class name in Konva using the find() method.
Source: https://konvajs.org/docs/selectors/Select_by_Type.html
To select shapes by type with Konva, we can use the `find()` method with the name of the type or class name.
The `find()` method returns an array of nodes that match the selector string.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// create shapes of different types
const circle1 = new Konva.Circle({
x: 50,
y: stage.height() / 2,
radius: 30,
fill: 'red'
});
const circle2 = new Konva.Circle({
x: 150,
y: stage.height() / 2,
radius: 30,
fill: 'green'
});
const rect = new Konva.Rect({
x: 250,
y: stage.height() / 2 - 25,
width: 50,
height: 50,
fill: 'blue'
});
layer.add(circle1);
layer.add(circle2);
layer.add(rect);
// find all circles by type
const circles = layer.find('Circle');
circles.forEach(circle => {
// add animation to circles only
circle.to({
duration: 1,
scale: { x: 1.5, y: 1.5 },
easing: Konva.Easings.EaseInOut
});
});
```
```js
import { Stage, Layer, Circle, Rect } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const layerRef = useRef(null);
useEffect(() => {
// find all circles by type and animate them
const circles = layerRef.current.find('Circle');
circles.forEach(circle => {
circle.to({
duration: 1,
scale: { x: 1.5, y: 1.5 },
easing: Konva.Easings.EaseInOut
});
});
}, []);
return (
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Select Shape by id Tutorial
> Learn how to select and find shapes by id in Konva using the find() and findOne() methods with the hash (#) selector.
Source: https://konvajs.org/docs/selectors/Select_by_id.html
To select a shape by id with Konva, we can use the `find()` method using the # selector.
The `find()` method always returns an array of elements, even if we are expecting it to return one element.
if you need only one element you can use `findOne()` method.
The `find()` method works for any node, including the stage, layers, groups, and shapes.
**Instructions:** press the "Activate Rectangle" button to select the rectangle by id and perform a transition. You can also drag and drop the rectangle.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
// create a rectangle with id
const rect = new Konva.Rect({
x: stage.width() / 2 - 25,
y: stage.height() / 2 - 25,
width: 50,
height: 50,
fill: 'red',
id: 'myRect',
draggable: true
});
layer.add(rect);
// add button
const button = document.createElement('button');
button.textContent = 'Activate Rectangle';
document.body.appendChild(button);
button.addEventListener('click', () => {
// find rectangle by id and animate it
const rectangle = layer.findOne('#myRect');
rectangle.to({
duration: 1,
rotation: 360,
fill: 'blue',
easing: Konva.Easings.EaseInOut
});
});
```
```js
import { Stage, Layer, Rect } from 'react-konva';
import { useRef, useState } from 'react';
const App = () => {
const [position, setPosition] = useState({
x: window.innerWidth / 2 - 25,
y: window.innerHeight / 2 - 25
});
const layerRef = useRef(null);
const handleClick = () => {
// find rectangle by id and animate it
const rectangle = layerRef.current.findOne('#myRect');
rectangle.to({
duration: 1,
rotation: 360,
fill: 'blue',
easing: Konva.Easings.EaseInOut
});
};
const handleDragEnd = (e) => {
setPosition({
x: e.target.x(),
y: e.target.y()
});
};
return (
Activate Rectangle
);
};
export default App;
```
```js
Activate Rectangle
```
---
# HTML5 canvas Arc Tutorial
> Learn how to draw arcs on HTML5 Canvas with Konva.js. Set inner/outer radius, angle, and clockwise direction with the Konva.Arc shape.
Source: https://konvajs.org/docs/shapes/Arc.html
To create an arc shape with `Konva`, we can instantiate a `Konva.Arc()` object.
For full list of properties and methods, see the [Arc API Reference](/api/Konva.Arc.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const arc = new Konva.Arc({
x: stage.width() / 2,
y: stage.height() / 2,
innerRadius: 40,
outerRadius: 70,
angle: 60,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4
});
layer.add(arc);
```
```js
import { Stage, Layer, Arc } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
```js
```
```js
import { Component } from '@angular/core';
import { StageConfig } from 'konva/lib/Stage';
import { ArcConfig } from 'konva/lib/shapes/Arc';
import {
CoreShapeComponent,
// NgKonvaEventObject,
StageComponent,
} from 'ng2-konva';
@Component({
selector: 'app-root',
template: `
`,
imports: [StageComponent, CoreShapeComponent],
})
export default class App {
public configStage: StageConfig = {
width: window.innerWidth,
height: window.innerHeight,
};
public configArc: ArcConfig = {
x: window.innerWidth / 2,
y: window.innerHeight / 2,
innerRadius: 40,
outerRadius: 70,
angle: 60,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4,
};
}
```
---
# HTML5 canvas Arrow Tutorial
> Learn how to draw arrows on HTML5 Canvas with Konva.js. Create arrows with customizable pointers, stroke, and fill using the Konva.Arrow shape.
Source: https://konvajs.org/docs/shapes/Arrow.html
To create an arrow shape with `Konva`, we can instantiate a `Konva.Arrow()` object.
For full list of properties and methods, see the [Arrow API Reference](/api/Konva.Arrow.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const arrow = new Konva.Arrow({
x: stage.width() / 4,
y: stage.height() / 4,
points: [0, 0, 100, 100],
pointerLength: 20,
pointerWidth: 20,
fill: 'black',
stroke: 'black',
strokeWidth: 4
});
layer.add(arrow);
```
```js
import { Stage, Layer, Arrow } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Circle Tutorial
> Learn how to draw circles on HTML5 Canvas with Konva.js. Set radius, fill, stroke, opacity, and shadows with the Konva.Circle shape.
Source: https://konvajs.org/docs/shapes/Circle.html
To create a circle shape with `Konva`, we can instantiate a `Konva.Circle()` object.
For full list of properties and methods, see the [Circle API Reference](/api/Konva.Circle.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const circle = new Konva.Circle({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
layer.add(circle);
```
```js
import { Stage, Layer, Circle } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Custom Shape Tutorial
> Learn how to draw custom shapes on HTML5 Canvas with Konva.js. Use the Konva.Shape sceneFunc to create any shape with the native Canvas 2D context.
Source: https://konvajs.org/docs/shapes/Custom.html
To create a custom shape with `Konva`, you can use the `Konva.Shape()` object and define a custom drawing function.
When creating a custom shape, you need to define a drawing function that is passed a [Konva.Context](/api/Konva.Context.html) renderer and a shape instance. Here's a simple rectangle example:
```javascript
const rect = new Konva.Shape({
x: 10,
y: 20,
fill: '#00D2FF',
width: 100,
height: 50,
sceneFunc: function (context, shape) {
context.beginPath();
// don't need to set position of rect, Konva will handle it
context.rect(0, 0, shape.getAttr('width'), shape.getAttr('height'));
// (!) Konva specific method, it is very important
// it will apply all required styles
context.fillStrokeShape(shape);
}
});
```
`Konva.Context` is a wrapper around native 2d canvas context that has the same properties and methods with some additional API.
There are two properties that can be used for drawing custom shapes:
- `sceneFunc` - defines visual appearance of a shape
- `hitFunc` - optional function to define custom hit region for events (see [Custom Hit Region demo](/docs/events/Custom_Hit_Region.html))
### Best practices for writing `sceneFunc` and `hitFunc`:
1. Optimize the function as it can be called many times per second. Avoid creating images or large objects.
2. The function should not have side effects like moving shapes, attaching events or changing app state.
3. Define custom `hitFunc` when applying complex styles or drawing images.
4. Don't manually apply position and scaling in `sceneFunc`. Let Konva handle it through shape properties.
5. Avoid manual styling in `sceneFunc`. Use `context.fillStrokeShape(shape)` for styling.
6. Reference [Konva core shapes implementations](https://github.com/konvajs/konva/tree/master/src/shapes) for more examples.
For full list of properties and methods, see the [Shape API Reference](/api/Konva.Shape.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const triangle = new Konva.Shape({
sceneFunc: function (context, shape) {
context.beginPath();
context.moveTo(20, 50);
context.lineTo(220, 80);
context.lineTo(100, 150);
context.closePath();
context.fillStrokeShape(shape);
},
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4
});
layer.add(triangle);
```
```js
import { Stage, Layer, Shape } from 'react-konva';
const App = () => {
return (
{
context.beginPath();
context.moveTo(20, 50);
context.lineTo(220, 80);
context.lineTo(100, 150);
context.closePath();
context.fillStrokeShape(shape);
}}
fill="#00D2FF"
stroke="black"
strokeWidth={4}
/>
);
};
export default App;
```
```js
```
---
# HTML5 canvas Ellipse Tutorial
> Learn how to draw ellipses on HTML5 Canvas with Konva.js. Set radiusX, radiusY, fill, stroke, and more with the Konva.Ellipse shape.
Source: https://konvajs.org/docs/shapes/Ellipse.html
To create an ellipse shape with `Konva`, we can instantiate a `Konva.Ellipse()` object.
For full list of properties and methods, see the [Ellipse API Reference](/api/Konva.Ellipse.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const ellipse = new Konva.Ellipse({
x: stage.width() / 2,
y: stage.height() / 2,
radiusX: 100,
radiusY: 50,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4
});
layer.add(ellipse);
```
```js
import { Stage, Layer, Ellipse } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Group Tutorial
> Learn how to group shapes on HTML5 Canvas with Konva.js. Use Konva.Group to move, rotate, and scale multiple shapes together.
Source: https://konvajs.org/docs/shapes/Group.html
To create a group of shapes with `Konva`, we can instantiate a `Konva.Group()` object.
For full list of properties and methods, see the [Group API Reference](/api/Konva.Group.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const group = new Konva.Group({
x: 50,
y: 50,
draggable: true
});
const circle = new Konva.Circle({
x: 0,
y: 0,
radius: 30,
fill: 'red'
});
const rect = new Konva.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
fill: 'green'
});
group.add(circle);
group.add(rect);
layer.add(group);
```
```js
import { Stage, Layer, Group, Circle, Rect } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
return (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
>
);
};
export default App;
```
```js
```
---
# HTML5 canvas Image Tutorial
> Learn how to draw images on HTML5 Canvas with Konva.js. Load images from URLs, crop, resize, and apply filters with the Konva.Image shape.
Source: https://konvajs.org/docs/shapes/Image.html
To create an image with `Konva`, you can use the `Konva.Image()` object.
For full list of properties and methods, see the [Image API Reference](/api/Konva.Image.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
// main API:
const imageObj = new Image();
imageObj.onload = function () {
const yoda = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
width: 106,
height: 118
});
layer.add(yoda);
};
imageObj.src = 'https://konvajs.org/assets/yoda.jpg';
// alternative API:
Konva.Image.fromURL('https://konvajs.org/assets/darth-vader.jpg', function (darthNode) {
darthNode.setAttrs({
x: 200,
y: 50,
scaleX: 0.5,
scaleY: 0.5,
cornerRadius: 20
});
layer.add(darthNode);
});
```
```js
import { Stage, Layer, Image } from 'react-konva';
import { useEffect, useState } from 'react';
import useImage from 'use-image';
const App = () => {
const [yodaImage] = useImage('https://konvajs.org/assets/yoda.jpg');
const [vaderImage] = useImage('https://konvajs.org/assets/darth-vader.jpg');
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Label Tutorial
> Learn how to draw labels with text and tags on HTML5 Canvas with Konva.js. Create tooltips, callouts, and annotations using the Konva.Label shape.
Source: https://konvajs.org/docs/shapes/Label.html
To create a label with `Konva`, you can use the `Konva.Label()` object.
For full list of properties and methods, see the [Label API Reference](/api/Konva.Label.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
// tooltip pointing down
const tooltip = new Konva.Label({
x: 170,
y: 75,
opacity: 0.75
});
tooltip.add(
new Konva.Tag({
fill: 'black',
pointerDirection: 'down',
pointerWidth: 10,
pointerHeight: 10,
lineJoin: 'round',
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.5
})
);
tooltip.add(
new Konva.Text({
text: 'Tooltip pointing down',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white'
})
);
// label pointing left
const labelLeft = new Konva.Label({
x: 20,
y: 130,
opacity: 0.75
});
labelLeft.add(
new Konva.Tag({
fill: 'green',
pointerDirection: 'left',
pointerWidth: 20,
pointerHeight: 28,
lineJoin: 'round'
})
);
labelLeft.add(
new Konva.Text({
text: 'Label pointing left',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white'
})
);
// simple label
const simpleLabel = new Konva.Label({
x: 180,
y: 150,
opacity: 0.75
});
simpleLabel.add(
new Konva.Tag({
fill: 'yellow'
})
);
simpleLabel.add(
new Konva.Text({
text: 'Simple label',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'black'
})
);
layer.add(tooltip).add(labelLeft).add(simpleLabel);
stage.add(layer);
```
```js
import { Stage, Layer, Label, Tag, Text } from 'react-konva';
const App = () => {
return (
{/* tooltip pointing down */}
{/* label pointing left */}
{/* simple label */}
);
};
export default App;
```
```js
```
---
# HTML5 canvas Line Tutorial
> Learn how to draw lines on HTML5 Canvas with Konva.js. Create simple lines, splines, polygons, and blobs with the Konva.Line shape.
Source: https://konvajs.org/docs/shapes/Line.html
To create line shapes with `Konva`, we can instantiate a `Konva.Line()` object. Lines can be configured in different ways to create various shapes like simple lines, splines, blobs, and polygons.
For full list of properties and methods, see the [Line API Reference](/api/Konva.Line.html).
## Simple Line
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const redLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'red',
strokeWidth: 15,
lineCap: 'round',
lineJoin: 'round'
});
// dashed line
const greenLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'green',
strokeWidth: 2,
lineJoin: 'round',
dash: [33, 10]
});
greenLine.y(50);
layer.add(redLine, greenLine);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
## Spline (Curved Line)
To create a curved line, add the `tension` property:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const line = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'red',
strokeWidth: 15,
lineCap: 'round',
lineJoin: 'round',
tension: 1
});
layer.add(line);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
## Polygon
To create a polygon, set the `closed` property to true:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const polygon = new Konva.Line({
points: [73, 192, 73, 160, 340, 23, 500, 109, 499, 139, 342, 93],
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 5,
closed: true
});
layer.add(polygon);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
## Blob
To create a blob, combine `closed` and `tension` properties:
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const blob = new Konva.Line({
points: [23, 20, 23, 160, 70, 93, 150, 109, 290, 139, 270, 93],
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 5,
closed: true,
tension: 0.3
});
layer.add(blob);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Blob Tutorial
> Learn how to draw blob shapes on the HTML5 canvas with Konva using Konva.Line with closed and tension properties.
Source: https://konvajs.org/docs/shapes/Line_-_Blob.html
To create a blob with `Konva`, we can instantiate a `Konva.Line()` object with both `closed` and `tension` properties.
For full list of properties and methods, see the [Line API Reference](/api/Konva.Line.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const blob = new Konva.Line({
points: [23, 20, 23, 160, 70, 93, 150, 109, 290, 139, 270, 93],
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 5,
closed: true,
tension: 0.3
});
layer.add(blob);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Polygon Tutorial
> Learn how to draw polygon shapes on the HTML5 canvas with Konva using Konva.Line with the closed property set to true.
Source: https://konvajs.org/docs/shapes/Line_-_Polygon.html
To create a polygon with `Konva`, we can instantiate a `Konva.Line()` object with the `closed` property set to `true`.
For full list of properties and methods, see the [Line API Reference](/api/Konva.Line.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const polygon = new Konva.Line({
points: [73, 192, 73, 160, 340, 23, 500, 109, 499, 139, 342, 93],
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 5,
closed: true
});
layer.add(polygon);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Simple, Dashed and Dotted Lines
> Learn how to draw simple lines, dashed lines, and dotted lines on the HTML5 canvas using Konva.Line.
Source: https://konvajs.org/docs/shapes/Line_-_Simple_Line.html
To create a simple line with `Konva`, we can instantiate a `Konva.Line()` object.
For full list of properties and methods, see the [Line API Reference](/api/Konva.Line.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const redLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'red',
strokeWidth: 15,
lineCap: 'round',
lineJoin: 'round'
});
// line segments with a length of 33px with a gap of 10px
const greenLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'green',
strokeWidth: 2,
lineJoin: 'round',
dash: [33, 10]
});
// line segments with a length of 29px with a gap of 20px
// followed by a dot (0.001px) and another gap of 20px
const blueLine = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'blue',
strokeWidth: 10,
lineCap: 'round',
lineJoin: 'round',
dash: [29, 20, 0.001, 20]
});
redLine.move({ x: 0, y: 5 });
greenLine.move({ x: 0, y: 55 });
blueLine.move({ x: 0, y: 105 });
layer.add(redLine, greenLine, blueLine);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Spline Tutorial
> Learn how to draw smooth curved spline lines on the HTML5 canvas with Konva using Konva.Line with the tension property.
Source: https://konvajs.org/docs/shapes/Line_-_Spline.html
To create a spline (curved line) with `Konva`, we can instantiate a `Konva.Line()` object with the `tension` property.
For full list of properties and methods, see the [Line API Reference](/api/Konva.Line.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const line = new Konva.Line({
points: [5, 70, 140, 23, 250, 60, 300, 20],
stroke: 'red',
strokeWidth: 15,
lineCap: 'round',
lineJoin: 'round',
tension: 1
});
layer.add(line);
```
```js
import { Stage, Layer, Line } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Path Tutorial
> Learn how to draw custom paths on HTML5 Canvas with Konva.js using SVG path data. Create complex shapes with the Konva.Path shape.
Source: https://konvajs.org/docs/shapes/Path.html
To create a custom path shape with `Konva`, we can instantiate a `Konva.Path()` object.
For full list of properties and methods, see the [Path API Reference](/api/Konva.Path.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const path = new Konva.Path({
x: 50,
y: 50,
data: 'M12.582,9.551C3.251,16.237,0.921,29.021,7.08,38.564l-2.36,1.689l4.893,2.262l4.893,2.262l-0.568-5.36l-0.567-5.359l-2.365,1.694c-4.657-7.375-2.83-17.185,4.352-22.33c7.451-5.338,17.817-3.625,23.156,3.824c5.337,7.449,3.625,17.813-3.821,23.152l2.857,3.988c9.617-6.893,11.827-20.277,4.935-29.896C35.591,4.87,22.204,2.658,12.582,9.551z',
fill: 'green',
scale: {
x: 2,
y: 2
}
});
layer.add(path);
```
```js
import { Stage, Layer, Path } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Regular Polygon Tutorial
> Learn how to draw regular polygons (triangles, pentagons, hexagons) on HTML5 Canvas with Konva.js using the Konva.RegularPolygon shape.
Source: https://konvajs.org/docs/shapes/RegularPolygon.html
To create a regular polygon shape with `Konva`, we can instantiate a `Konva.RegularPolygon()` object.
For full list of properties and methods, see the [RegularPolygon API Reference](/api/Konva.RegularPolygon.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const hexagon = new Konva.RegularPolygon({
x: stage.width() / 2,
y: stage.height() / 2,
sides: 6,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
layer.add(hexagon);
```
```js
import { Stage, Layer, RegularPolygon } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Ring Tutorial
> Learn how to draw rings (donuts) on HTML5 Canvas with Konva.js. Set inner and outer radius with the Konva.Ring shape.
Source: https://konvajs.org/docs/shapes/Ring.html
To create a ring with `Konva`, you can use the `Konva.Ring()` object.
For full list of properties and methods, see the [Ring API Reference](/api/Konva.Ring.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const ring = new Konva.Ring({
x: stage.width() / 2,
y: stage.height() / 2,
innerRadius: 40,
outerRadius: 70,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4
});
layer.add(ring);
```
```js
import { Stage, Layer, Ring } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Sprite Tutorial
> Learn how to use sprite sheets for animation on HTML5 Canvas with Konva.js. Play frame-based animations using the Konva.Sprite shape.
Source: https://konvajs.org/docs/shapes/Sprite.html
To create an animated sprite with `Konva`, we can instantiate a `Konva.Sprite()` object.
For full list of properties and methods, see the [Sprite API Reference](/api/Konva.Sprite.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const animations = {
idle: [
2, 2, 70, 119, // frame 1
71, 2, 74, 119, // frame 2
146, 2, 81, 119, // frame 3
226, 2, 76, 119, // frame 4
],
punch: [
2, 138, 74, 122, // frame 1
76, 138, 84, 122, // frame 2
346, 138, 120, 122, // frame 3
],
};
const imageObj = new Image();
imageObj.onload = function() {
const sprite = new Konva.Sprite({
x: 50,
y: 50,
image: imageObj,
animation: 'idle',
animations: animations,
frameRate: 7,
frameIndex: 0
});
layer.add(sprite);
sprite.start();
// Add punch button functionality
const button = document.createElement('button');
button.textContent = 'Punch';
button.style.position = 'absolute';
button.style.top = '0';
button.style.left = '0';
document.body.appendChild(button);
button.addEventListener('click', () => {
sprite.animation('punch');
sprite.on('frameIndexChange.button', function() {
if (this.frameIndex() === 2) {
setTimeout(() => {
sprite.animation('idle');
sprite.off('.button');
}, 1000 / sprite.frameRate());
}
});
});
};
imageObj.src = 'https://konvajs.org/assets/blob-sprite.png';
```
```js
import { Stage, Layer, Sprite } from 'react-konva';
import { useEffect, useRef } from 'react';
import useImage from 'use-image';
const App = () => {
const spriteRef = useRef(null);
const [image] = useImage('https://konvajs.org/assets/blob-sprite.png');
const animations = {
idle: [
2, 2, 70, 119, // frame 1
71, 2, 74, 119, // frame 2
146, 2, 81, 119, // frame 3
226, 2, 76, 119, // frame 4
],
punch: [
2, 138, 74, 122, // frame 1
76, 138, 84, 122, // frame 2
346, 138, 120, 122, // frame 3
],
};
useEffect(() => {
if (spriteRef.current) {
spriteRef.current.start();
}
}, [image]);
const handlePunch = () => {
if (spriteRef.current) {
const sprite = spriteRef.current;
sprite.animation('punch');
sprite.on('frameIndexChange.button', function() {
if (this.frameIndex() === 2) {
setTimeout(() => {
sprite.animation('idle');
sprite.off('.button');
}, 1000 / sprite.frameRate());
}
});
}
};
return (
<>
Punch
>
);
};
export default App;
```
```js
Punch
```
---
# HTML5 canvas Star Tutorial
> Learn how to draw stars on HTML5 Canvas with Konva.js. Set inner/outer radius, number of points, fill, and stroke with the Konva.Star shape.
Source: https://konvajs.org/docs/shapes/Star.html
To create a star with `Konva`, you can use the `Konva.Star()` object.
For full list of properties and methods, see the [Star API Reference](/api/Konva.Star.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const star = new Konva.Star({
x: stage.width() / 2,
y: stage.height() / 2,
numPoints: 5,
innerRadius: 30,
outerRadius: 70,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4
});
layer.add(star);
```
```js
import { Stage, Layer, Star } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Text Tutorial
> Learn how to draw text on HTML5 Canvas with Konva.js. Set font family, size, style, alignment, wrapping, padding, and decoration with the Konva.Text shape.
Source: https://konvajs.org/docs/shapes/Text.html
Canvas has no text elements. The browser gives you `fillText()`, which draws a
string at a point and forgets it — no wrapping, no alignment, no way to ask how
wide it was. `Konva.Text` is a shape, so the text stays a node you can move,
style, measure, and hit-test.
```js
const text = new Konva.Text({
x: 20,
y: 20,
text: 'Hello Konva',
fontSize: 24,
fontFamily: 'Arial',
fill: 'black',
});
```
Set `width` and the text wraps to it. Leave `width` unset and the shape sizes
itself to the content.
For the full list of properties and methods, see the
[Text API Reference](/api/Konva.Text.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
// Simple text
const simpleText = new Konva.Text({
x: stage.width() / 2,
y: 15,
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green'
});
simpleText.offsetX(simpleText.width() / 2);
// Complex text with background
const complexText = new Konva.Text({
x: 20,
y: 60,
text: "COMPLEX TEXT\n\nAll the world's a stage, and all the men and women merely players. They have their exits and their entrances.",
fontSize: 18,
fontFamily: 'Calibri',
fill: '#555',
width: 300,
padding: 20,
align: 'center'
});
const rect = new Konva.Rect({
x: 20,
y: 60,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 300,
height: complexText.height(),
shadowColor: 'black',
shadowBlur: 10,
shadowOffsetX: 10,
shadowOffsetY: 10,
shadowOpacity: 0.2,
cornerRadius: 10
});
layer.add(rect);
layer.add(simpleText);
layer.add(complexText);
```
```js
import { Stage, Layer, Text, Rect } from 'react-konva';
const text = `COMPLEX TEXT
All the world's a stage, and all the men and women merely players. They have their exits and their entrances.`;
const App = () => {
return (
);
};
export default App;
```
```js
```
## Measuring text
A `Konva.Text` measures itself as soon as it exists, so you can read its size
before it is on a layer.
```js
const text = new Konva.Text({ text: 'Hello Konva', fontSize: 24 });
text.width(); // full shape width, including padding
text.height(); // full shape height, all lines, including padding
text.getTextWidth(); // width of the widest line, excluding padding
text.fontSize(); // height of a single line
```
Use these instead of estimating. Centring by hand is the usual reason people
guess:
```js
text.offsetX(text.width() / 2); // exact
```
Two things to know:
- **`getTextHeight()` is deprecated.** It warns in the console. Use `height()`
for the whole shape and `fontSize()` for one line.
- **`measureSize(string)` measures a string you have not drawn**, using this
shape's font. It is useful for sizing something before you commit to it, and
it cannot handle multiline text.
```js
const { width } = text.measureSize('Some other string');
```
In React and Vue, read the same values through a ref to the node rather than
approximating them — or avoid measurement entirely by giving the text a `width`
and an `align`, as the demo above does.
## Wrapping and ellipsis
Text wraps only when it has a `width`.
```js
new Konva.Text({
text: 'A long line that will not fit on one row',
width: 200, // wrapping requires this
wrap: 'word', // 'word' (default), 'char', or 'none'
ellipsis: true, // needs a height too, or there is nothing to overflow
});
```
`ellipsis` truncates with `…` when the text does not fit the box. It only has an
effect when both `width` and `height` are set, since without a height the shape
grows to fit and nothing ever overflows.
```js
import Konva from 'konva';
const stage = new Konva.Stage({ container: 'container', width: 500, height: 260 });
const layer = new Konva.Layer();
stage.add(layer);
const sample = 'Konva.Text wraps to the width you give it, and can truncate when it runs out of room.';
[
{ y: 10, label: "wrap: 'word' (default)", config: { width: 220 } },
{ y: 95, label: "wrap: 'char'", config: { width: 220, wrap: 'char' } },
{ y: 180, label: 'ellipsis with a fixed height', config: { width: 220, height: 44, ellipsis: true } },
].forEach(({ y, label, config }) => {
layer.add(new Konva.Text({ x: 250, y, text: label, fontSize: 13, fill: '#666' }));
layer.add(
new Konva.Rect({ x: 20, y, width: 220, height: config.height || 70, stroke: '#ddd' })
);
layer.add(new Konva.Text({ x: 20, y, text: sample, fontSize: 14, padding: 4, ...config }));
});
```
## Text and web fonts
This is the most common text bug, and it is not a Konva bug.
A DOM element re-lays-out by itself when a web font finishes loading. Canvas
does not. If the text was created before the font arrived, it was measured with
the fallback font, and that stale measurement is what wrapping, centring, and
`width()` are still based on. The text usually looks right and sits in the
wrong place.
Wait for the font, then force a re-measure:
```js
await document.fonts.load('16px "Roboto"');
// Re-setting any measured attribute re-runs the measurement.
text.fontFamily('Roboto');
```
Konva re-measures whenever one of its text-affecting attributes changes:
`text`, `fontFamily`, `fontSize`, `fontStyle`, `fontVariant`, `lineHeight`,
`letterSpacing`, `align`, `verticalAlign`, `padding`, `width`, `height`,
`wrap`, `ellipsis`, and `direction`. Setting one of those to the value it
already has is enough.
There is a complete example, including loading the font itself, in
[Custom Font](/docs/sandbox/Custom_Font.html).
---
# HTML5 canvas TextPath Tutorial
> Learn how to draw text along a path on HTML5 Canvas with Konva.js. Render curved or circular text using the Konva.TextPath shape.
Source: https://konvajs.org/docs/shapes/TextPath.html
To create text that follows a path with `Konva`, we can instantiate a `Konva.TextPath()` object.
For full list of properties and methods, see the [TextPath API Reference](/api/Konva.TextPath.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const textPath = new Konva.TextPath({
x: 0,
y: 50,
fill: '#333',
fontSize: 16,
fontFamily: 'Arial',
text: 'All the world\'s a stage, and all the men and women merely players.',
data: 'M10,10 C0,0 10,150 100,100 S300,150 400,50',
});
layer.add(textPath);
```
```js
import { Stage, Layer, TextPath } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 canvas Wedge Tutorial
> Learn how to draw wedges (pie slices) on HTML5 Canvas with Konva.js. Set angle, radius, and rotation with the Konva.Wedge shape.
Source: https://konvajs.org/docs/shapes/Wedge.html
To create a wedge (pie piece) shape with `Konva`, we can instantiate a `Konva.Wedge()` object.
For full list of properties and methods, see the [Wedge API Reference](/api/Konva.Wedge.html).
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const wedge = new Konva.Wedge({
x: stage.width() / 2,
y: stage.height() / 2,
radius: 70,
angle: 60,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
rotation: -120
});
layer.add(wedge);
```
```js
import { Stage, Layer, Wedge } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Blend mode with globalCompositeOperation Tutorial
> Learn how to use globalCompositeOperation for blend modes like XOR on HTML5 Canvas shapes using Konva.js.
Source: https://konvajs.org/docs/styling/Blend_Mode.html
[globalCompositeOperation Documentation](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation).
With Konva framework you can set globalCompositeOperation or blending mode operations with `globalCompositeOperation` property.
**Instructions**: Drag the red rectangle over the green text to see the XOR blending effect.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
const text = new Konva.Text({
text: 'Text Shadow!',
fontFamily: 'Calibri',
fontSize: 40,
x: 20,
y: 20,
fill: 'green',
shadowColor: 'white',
shadowOffset: { x: 10, y: 10 }
});
layer.add(text);
const rect = new Konva.Rect({
x: 50,
y: 50,
width: 100,
height: 100,
fill: 'red',
draggable: true,
globalCompositeOperation: 'xor'
});
layer.add(rect);
stage.add(layer);
```
```js
import { Stage, Layer, Text, Rect } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [position, setPosition] = useState({ x: 50, y: 50 });
return (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
globalCompositeOperation="xor"
/>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Set Fill Tutorial
> Learn how to fill shapes with colors, patterns, linear gradients, and radial gradients using Konva.js on HTML5 Canvas.
Source: https://konvajs.org/docs/styling/Fill.html
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.
```js
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);
});
```
```jsx
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 (
{
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 (
{
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 (
{
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 (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
/>
);
};
const App = () => {
return (
);
};
export default App;
```
```html
```
---
# Fill and stroke order demo
> Learn how to control fill and stroke rendering order in Konva.js using the fillAfterStrokeEnabled property.
Source: https://konvajs.org/docs/styling/Fill_Stroke_Order.html
If a shape has both fill and stroke, by default, `Konva` will draw filling first then stroke on top of it. That is the best behavior for most of the applications.
## How to draw fill part on top of the stroke?
In some rare cases you may need a shape that has stroke first, then a fill on top of it. For that use case you may use [fillAfterStrokeEnabled](https://konvajs.org/api/Konva.Shape.html#fillAfterStrokeEnabled) property.
```js
shape.fillAfterStrokeEnabled(true);
```
**Instructions:** Take a look into two examples of different fill&stroke order.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const text1 = new Konva.Text({
text: 'Default shape rendering.\nfillAfterStrokeEnabled = false',
x: 50,
y: 50,
fontSize: 40,
stroke: 'green',
fill: 'yellow',
strokeWidth: 3,
});
layer.add(text1);
const text2 = new Konva.Text({
text: 'Reversed rendering order.\nfillAfterStrokeEnabled = true',
x: 50,
y: 150,
fontSize: 40,
stroke: 'green',
fill: 'yellow',
strokeWidth: 3,
fillAfterStrokeEnabled: true,
});
layer.add(text2);
```
```js
import { Stage, Layer, Text } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Hide and Show Shape Tutorial
> Learn how to toggle shape visibility on HTML5 Canvas using the Konva.js hide() and show() methods.
Source: https://konvajs.org/docs/styling/Hide_and_Show.html
To hide and show a shape with Konva, we can set the visible property when we instantiate a shape, or we can use the `hide()` and `show()` methods.
**Instructions:** Click on the buttons to show and hide the shape.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: stage.width() / 2 - 50,
y: stage.height() / 2 - 25,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
layer.add(rect);
// update button creation and styling
const buttonContainer = document.createElement('div');
buttonContainer.style.position = 'absolute';
buttonContainer.style.zIndex = 1;
buttonContainer.style.padding = '10px';
buttonContainer.style.top = '0px';
buttonContainer.style.left = '0px';
const showBtn = document.createElement('button');
showBtn.textContent = 'Show';
showBtn.onclick = () => rect.show();
buttonContainer.appendChild(showBtn);
const hideBtn = document.createElement('button');
hideBtn.textContent = 'Hide';
hideBtn.onclick = () => rect.hide();
buttonContainer.appendChild(hideBtn);
document.body.appendChild(buttonContainer);
```
```jsx
import React, { useState } from 'react';
import { Stage, Layer, Rect } from 'react-konva';
function App() {
const [visible, setVisible] = useState(true);
return (
setVisible(true)}>Show
setVisible(false)}>Hide
);
}
export default App;
```
```vue
```
---
# HTML5 Canvas Line Join Tutorial
> Learn how to set the line join style (miter, bevel, round) for shapes on HTML5 Canvas using Konva.js.
Source: https://konvajs.org/docs/styling/Line_Join.html
To set the line join for a shape with Konva, we can set the `lineJoin` property when we instantiate a shape, or we can use the `lineJoin()` method.
The `lineJoin` property can be set to `miter`, `bevel`, or `round`. Unless otherwise specified, the default line join is `miter`.
**Instructions:** Mouseover the triangle to change the line join style.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const triangle = new Konva.RegularPolygon({
x: stage.width() / 2,
y: stage.height() / 2,
sides: 3,
radius: 70,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 20,
lineJoin: 'miter'
});
layer.add(triangle);
triangle.on('mouseenter', function() {
const lineJoins = ['miter', 'bevel', 'round'];
const index = lineJoins.indexOf(triangle.lineJoin());
const nextIndex = (index + 1) % lineJoins.length;
triangle.lineJoin(lineJoins[nextIndex]);
});
```
```js
import { Stage, Layer, RegularPolygon } from 'react-konva';
import { useState } from 'react';
const App = () => {
const [lineJoin, setLineJoin] = useState('miter');
const handleMouseEnter = () => {
const lineJoins = ['miter', 'bevel', 'round'];
const index = lineJoins.indexOf(lineJoin);
const nextIndex = (index + 1) % lineJoins.length;
setLineJoin(lineJoins[nextIndex]);
};
return (
);
};
export default App;
```
```js
```
---
# Canvas Cursor Style — Change Mouse Cursor on HTML5 Canvas Shapes
> Change the mouse cursor style when hovering over HTML5 Canvas shapes. Set pointer, crosshair, grab, or custom cursors on canvas objects using Konva.js.
Source: https://konvajs.org/docs/styling/Mouse_Cursor.html
Change the mouse cursor when users hover over shapes on an HTML5 Canvas. Konva lets you listen for mouse events on individual shapes and apply any CSS cursor style to the stage container — pointer, crosshair, grab, move, or any custom cursor.
**Instructions:** Hover over each pentagon to see the cursor change.
```js
import Konva from 'konva';
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const pentagon1 = new Konva.RegularPolygon({
x: 80,
y: stage.height() / 2,
sides: 5,
radius: 30,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
pentagon1.on('mouseover', function (e) {
e.target.getStage().container().style.cursor = 'pointer';
});
pentagon1.on('mouseout', function (e) {
e.target.getStage().container().style.cursor = 'default';
});
const pentagon2 = new Konva.RegularPolygon({
x: 180,
y: stage.height() / 2,
sides: 5,
radius: 30,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
});
pentagon2.on('mouseover', function (e) {
e.target.getStage().container().style.cursor = 'crosshair';
});
pentagon2.on('mouseout', function (e) {
e.target.getStage().container().style.cursor = 'default';
});
const pentagon3 = new Konva.RegularPolygon({
x: 280,
y: stage.height() / 2,
sides: 5,
radius: 30,
fill: 'blue',
stroke: 'black',
strokeWidth: 4,
});
pentagon3.on('mouseover', function (e) {
e.target.getStage().container().style.cursor = 'move';
});
pentagon3.on('mouseout', function (e) {
e.target.getStage().container().style.cursor = 'default';
});
layer.add(pentagon1);
layer.add(pentagon2);
layer.add(pentagon3);
```
```js
import { Stage, Layer, RegularPolygon } from 'react-konva';
import { useState } from 'react';
// Separate component for polygon that changes cursor directly
const SpecialPolygon = ({ x, y }) => {
// We use e.target approach here because this component doesn't have
// access to the Stage's cursor state from the parent component
const handleMouseOver = (e) => {
e.target.getStage().container().style.cursor = 'pointer';
};
const handleMouseOut = (e) => {
e.target.getStage().container().style.cursor = 'default';
};
return (
);
};
const App = () => {
const [cursor, setCursor] = useState('default');
return (
setCursor('crosshair')}
onMouseOut={() => setCursor('default')}
/>
setCursor('move')}
onMouseOut={() => setCursor('default')}
/>
);
};
export default App;
```
```js
```
---
# HTML5 Canvas Set Shape Opacity Tutorial
> Learn how to set and change shape opacity (transparency) on HTML5 Canvas using Konva.js with values from 0 to 1.
Source: https://konvajs.org/docs/styling/Opacity.html
To set a shape opacity with Konva, we can set the `opacity` property when we instantiate the node, or we can use the `opacity()` method.
Shapes can have an opacity value between 0 and 1, where 0 is fully transparent, and 1 is fully opaque. Unless otherwise specified, all shapes are defaulted with an opacity value of 1.
If you want to apply transparency for several shapes without seen overlapping areas, take a look into [Transparent Group Demo](/docs/sandbox/Transparent_Group.html)
Instructions: Mouseover the pentagon to change its opacity.
```js
import Konva from 'konva';
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 pentagon = new Konva.RegularPolygon({
x: stage.width() / 2,
y: stage.height() / 2,
sides: 5,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
opacity: 0.5,
});
pentagon.on('mouseover', function () {
this.opacity(1);
});
pentagon.on('mouseout', function () {
this.opacity(0.5);
});
// add the shape to the layer
layer.add(pentagon);
// add the layer to the stage
stage.add(layer);
```
```jsx
import React, { useState } from 'react';
import { Stage, Layer, RegularPolygon } from 'react-konva';
const App = () => {
const [opacity, setOpacity] = useState(0.5);
const [cursor, setCursor] = useState('default');
const handleMouseEnter = () => {
setOpacity(1);
setCursor('pointer');
};
const handleMouseLeave = () => {
setOpacity(0.5);
setCursor('default');
};
return (
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Shadows Tutorial
> Learn how to add shadows to shapes on HTML5 Canvas using Konva.js with shadowColor, shadowOffset, shadowBlur, and shadowOpacity.
Source: https://konvajs.org/docs/styling/Shadow.html
To apply shadows with Konva, we can set the `shadowColor`, `shadowOffset`, `shadowBlur`, and `shadowOpacity` properties when we instantiate a shape.
We can adjust the shadow properties after instantiation by using the `shadowColor()`, `shadowOffset()`, `shadowBlur()`, and `shadowOpacity()` methods.
Instructions: Mouseover the star to change its shadow properties.
```js
import Konva from 'konva';
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 text = new Konva.Text({
text: 'Text Shadow!',
fontFamily: 'Calibri',
fontSize: 40,
x: 20,
y: 20,
stroke: 'red',
strokeWidth: 2,
shadowColor: 'black',
shadowBlur: 0,
shadowOffset: { x: 10, y: 10 },
shadowOpacity: 0.5,
});
var line = new Konva.Line({
stroke: 'green',
strokeWidth: 10,
lineJoin: 'round',
lineCap: 'round',
points: [50, 140, 250, 160],
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: { x: 10, y: 10 },
shadowOpacity: 0.5,
});
var rect = new Konva.Rect({
x: 100,
y: 120,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: { x: 10, y: 10 },
shadowOpacity: 0.5,
});
layer.add(text);
layer.add(line);
layer.add(rect);
stage.add(layer);
```
```jsx
import React from 'react';
import { Stage, Layer, Text, Line, Rect } from 'react-konva';
const App = () => {
return (
);
};
export default App;
```
```vue
```
---
# HTML5 Canvas Set Shape Stroke Color and Width Tutorial
> Learn how to set and dynamically change stroke color and width on HTML5 Canvas shapes using Konva.js.
Source: https://konvajs.org/docs/styling/Stroke.html
To set a shape stroke and stroke width with Konva, we can set the `stroke` and `strokeWidth` properties when we instantiate a shape, or we can use the `stroke()` and `strokeWidth()` methods.
Instructions: Mouseover the pentagon to change its stroke color and width.
```js
import Konva from 'konva';
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 pentagon = new Konva.RegularPolygon({
x: stage.width() / 2,
y: stage.height() / 2,
sides: 5,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
pentagon.on('mouseover', function () {
this.stroke('blue');
this.strokeWidth(20);
});
pentagon.on('mouseout', function () {
this.stroke('black');
this.strokeWidth(4);
});
// add the shape to the layer
layer.add(pentagon);
// add the layer to the stage
stage.add(layer);
```
```jsx
import React, { useState } from 'react';
import { Stage, Layer, RegularPolygon } from 'react-konva';
const App = () => {
const [stroke, setStroke] = useState('black');
const [strokeWidth, setStrokeWidth] = useState(4);
const [cursor, setCursor] = useState('default');
const handleMouseEnter = () => {
setStroke('blue');
setStrokeWidth(20);
setCursor('pointer');
};
const handleMouseLeave = () => {
setStroke('black');
setStrokeWidth(4);
setCursor('default');
};
return (
);
};
export default App;
```
```vue
```
---
# Binding the config prop
> Learn how svelte-konva keeps config props in sync with Konva node state after drag and transform events.
Source: https://konvajs.org/docs/svelte/Bindings.html
Svelte-Konva is able to keep certain props in sync with the internal state of Konva (position, rotation, scale, ...) after `dragend` and `transformend` events in case the prop is bound.
### Disabling automatic syncing
In most cases this default behavior of svelte-konva listening to the `dragend` and `transformend` events is what you want. In some cases this might not be beneficial though (mainly for performance reasons). In such cases you can opt out of this behavior by passing the `staticConfig` prop to the component, in which case svelte-konva will not listen to those events and update the bound props:
```
```
Keep in mind that svelte-konva will evaluate the `staticConfig` prop only once during component initialization. Changing the `staticConfig` prop after the component has been initialized will not have any effect.
Drag the different rings and observe the reactive changes triggered by Svelte. Note that only the bound ring (yellow) changes the coordinates on `dragend` automatically.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/bindings?file=/src/App.svelte)
---
# How to cache canvas shapes with Svelte
> Learn how to cache canvas shapes in Svelte with svelte-konva for improved rendering performance using node.cache().
Source: https://konvajs.org/docs/svelte/Cache.html
If you want to cache a node in a Svelte app, you need to have an access to Konva node and use `node.cache()` function.
To get access to a node you can use the component instance's `node` property. See [Konva Node](/docs/svelte/Konva_Node.html) for more information.
**Instruction: try to drag whole stage. Then try again with cached group.**
You should see much better performance.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/cache?file=/src/App.svelte)
---
# How to draw a custom canvas shape with Svelte?
> Learn how to draw custom canvas shapes in Svelte using the svelte-konva Shape component and canvas drawing functions.
Source: https://konvajs.org/docs/svelte/Custom_Shape.html
To create a custom shape with `svelte-konva`, you should use the `Shape` component.
When creating a custom shape, you need to define a drawing function that is passed a `Konva.Canvas` renderer.
You can then use the renderer to access the HTML5 Canvas context, and to use special methods like `context.fillStrokeShape(shape)` which automatically handles filling, stroking, and applying shadows.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/custom_shape?file=/src/App.svelte)
---
# Drag and drop canvas shapes with Svelte
> Learn how to enable drag and drop for canvas shapes in Svelte with svelte-konva using the draggable prop and bindings.
Source: https://konvajs.org/docs/svelte/Drag_And_Drop.html
To enable drag&drop for any node on canvas you just need to pass the `draggable=true` prop to the component.
svelte-konva is able to automatically keep affected props (x, y) in sync with the Konva node on `dragend`. See the [bindings](/docs/svelte/Bindings.html) doc page for more details.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/drag_and_drop?file=/src/App.svelte)
---
# How to listen to an event on a canvas shape with Svelte and Konva?
> Learn how to handle click, mouse, touch, and drag events on canvas shapes with Svelte and Konva.
Source: https://konvajs.org/docs/svelte/Events.html
With `svelte-konva` you can easily listen to user input events (`click`, `dblclick`, `mouseover`, `tap`, `dbltap`, `touchstart`, etc...) and drag&drop events (`dragstart`, `dragmove`, `dragend`). For this you can pass a callback function to the prop named `on` which is then called by svelte-konva every time the event is fired. You can also access the Konva event payload object inside the callback function as its argument.
```js
```
For the full list of events take a look into [on() method documentation](/api/Konva.Node.html).
## Bubbling
Konva events bubble up by default. To prevent this you can set the `cancelBubble` property of the Konva event to `true`:
```js
function handleClick(e) {
// Cancel bubbling
e.cancelBubble = true;
}
```
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/events?file=/src/App.svelte)
---
# How to apply canvas filters with Svelte and Konva?
> Learn how to apply canvas filters to shapes in Svelte with Konva using manual caching in onMount and afterUpdate.
Source: https://konvajs.org/docs/svelte/Filters.html
To apply filters you need to cache `Konva.Node` manually. You can do this initially in the `onMount()` method.
In case you dynamically change the style of the nodes you need to recache them manually for the changes to take effect on the canvas. This can be done by calling the `cache()` method on the affected nodes directly after a change (like in the demo) or in the `afterUpdate()` method to automatically recache the node on each state change in the component.
Instructions: hover over the rectangle to see the changes
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/filters?file=/src/App.svelte)
---
# How to draw an image on canvas with Svelte?
> Learn how to load and display images on an HTML5 Canvas with Svelte using the svelte-konva Image component.
Source: https://konvajs.org/docs/svelte/Images.html
For images you need to manually create a native window.Image instance or `canvas` element and use it as image attribute of `Image` component.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/images?file=/src/App.svelte)
---
# Accessing the Konva node
> Learn how to access the underlying Konva node from svelte-konva components via the node property or event payloads.
Source: https://konvajs.org/docs/svelte/Konva_Node.html
In some cases you might need to access the underlying Konva node of the svelte-konva component directly. You can do this by accessing the `node` property of the corresponding component instance or by accessing it in the payload of a Konva event.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/konva_node?file=/src/App.svelte)
---
# Using labels with Svelte
> Learn how to create labels and tooltips on canvas with Svelte using svelte-konva Label, Tag, and Text components.
Source: https://konvajs.org/docs/svelte/Labels.html
Creating a label is a multi-step process in Konva, as a Label instance needs to contain a Tag and Text instance to function. In svelte-konva the Tag and Text components can be easily nested inside the Label component to automatically create a correct Label without having to wire things up manually.
Hover over the circles to show the tooltips:
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/labels?file=/src/App.svelte)
---
# Saving and loading canvas with Svelte and Konva
> Learn how to save and load canvas state in Svelte with Konva by serializing your app state instead of Konva internals.
Source: https://konvajs.org/docs/svelte/Save_Load.html
Native Konva can serialize a node tree and its serializable attributes with `node.toJSON()`. It can restore them with `Konva.Node.create(json)` [(see demo)](/docs/data_and_serialization/Simple_Load.html). Restore images, event handlers, and custom drawing functions separately.
With svelte-konva, save the application state instead. The state must contain the data that the stage needs. Do not save Konva internals and nodes.
The demo saves and retrieves JSON data from `localStorage`. You can use a different storage method.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/save_load?file=/src/App.svelte)
---
# Drawing canvas shapes with Svelte
> Learn how to draw canvas shapes like Rect, Circle, Line, Star, and more in Svelte using svelte-konva components.
Source: https://konvajs.org/docs/svelte/Shapes.html
All `svelte-konva` components correspond to `Konva` components of the same name. All the parameters available for `Konva` objects are valid props for corresponding `svelte-konva` components, unless noted otherwise.
Core shapes are: Rect, Circle, Ellipse, Line, Image, Text, TextPath, Star, Label, SVG Path, RegularPolygon. You can also create custom shapes.
To get more info about Konva you can read the [Konva Overview](/docs/overview.html).
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/shapes?file=/src/App.svelte)
---
# How to apply canvas animations with Svelte and Konva?
> Learn how to animate canvas shapes in Svelte using Konva Tweens, node.to() method, and Konva.Animation.
Source: https://konvajs.org/docs/svelte/Simple_Animations.html
Konva itself has two methods for animations [Tween](/docs/tweens/Linear_Easing.html) and [Animation](/docs/animations/Rotation.html). You can apply both of them to nodes manually.
For simple use cases we recommend to use `node.to()` method.
Instructions: Try to move a rectangle.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/simple_animations?file=/src/App.svelte)
---
# How to use svelte-konva with SvelteKit?
> Learn how to use svelte-konva with SvelteKit SSR and prerendering using browser checks or dynamic imports.
Source: https://konvajs.org/docs/svelte/SvelteKit.html
Generally, svelte-konva is a client-side only library. When using SvelteKit, special care needs to be taken if svelte-konva/Konva functionality is used on prerendered and server side rendered (SSR) components. Prerendering and SSR happens in a Node.js environment. In case you use any svelte-konva functionality in such a context it will throw an error on the server:
> Error: svelte-konva: Library can only be used in a browser context but is currently used in a server environment.
There are multiple solutions to this problem:
### Wrap your svelte-konva Components into browser checks
A rudimental solution is to wrap all your svelte-konva code into SvelteKit browser checks. This is only recommended in case your project is small as all the if-blocks can get messy quickly. For larger projects use dynamic imports outlined below.
```html
{#if browser}
{/if}
```
### Dynamically import your svelte-konva stage:
A better approach is to dynamically import your svelte-konva canvas on the client-side only. Suppose you have a Svelte component containing your stage with various svelte-konva components:
_MyCanvas.svelte_
```html
```
To use this component inside a SvelteKit prerendered/SSR page you can dynamically import it inside `onMount()` and render it once it becomes defined:
_+page.svelte_
```html
This is my fancy server side rendered (or prerendered) page.
{#await MyCanvas}
Loading...
{:then Component}
{:catch error}
Something went wrong: {error.message}
{/await}
```
Instructions: Each page available in this SvelteKit App is rendered differently containing a `svelte-konva` canvas. Both dynamic import approaches are shown. Client-side only use of the canvas using SvelteKit browser checks on the prerendered page and dynamic importing of the svelte-konva canvas on the SSR page. Try to inspect the network requests made on each navigation to understand the different approaches of rendering in SvelteKit.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/sveltekit?file=/src/routes/%2Bpage.svelte)
---
# How to resize and rotate canvas shapes with Svelte and Konva?
> Learn how to resize and rotate canvas shapes in Svelte using the svelte-konva Transformer component with select support.
Source: https://konvajs.org/docs/svelte/Transformer.html
You can use the transformer tool by using the svelte-konva transformer component. Generally this approach requires some interfacing with the native Konva API. You can attach shapes to the transformer by manually attaching their handles to the transformer using the `nodes()` function. svelte-konva also automatically keeps the relevant component props in sync with the Konva node on `transformend` if bound. See the [bindings](/docs/svelte/Bindings.html) doc page for more details.
For a more detailed example with select & transform functionality see the [example](https://github.com/konvajs/svelte-konva/blob/master/src/routes/examples/transform/Transform.svelte) in the svelte-konva repo.
Instructions: click on shape to select it.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/transformer?file=/src/App.svelte)
---
# How to change the zIndex of nodes with svelte-konva?
> Learn how to manage zIndex and reorder canvas shapes in svelte-konva using native Konva methods like moveToTop().
Source: https://konvajs.org/docs/svelte/zIndex.html
When working with other Konva-Wrappers like `vue-konva` or `react-konva` you might be used to the data order representing the drawing order of the components on the canvas. In svelte-konva such a functionality is currently not implemented.
Instead you should use the Konva native functions to perform dynamic reordering of components on the canvas like `node.zIndex(5)`, `node.moveToTop()`, etc. [Tutorial](/docs/groups_and_layers/Layering.html).
### Using if-blocks
svelte-konva will follow the initial ordering of the components to draw the shapes on the canvas. This works fine in cases where you do not need to change the ordering dynamically during runtime. When using Svelte if-blocks to show/hide certain components you should know the following caveat. Consider the following example:
```
{#if showRing}
{/if}
```
Based on the ordering one would expect to see the circle drawn on the top of the canvas, followed by the ring and then the rect shapes. However, due to the if-block the ring might end up at the top of the canvas depending on the initial value and changes of `showRing`. This is caused by Svelte mounting/unmounting components inside if-blocks and svelte-konva drawing the shapes during mounting at the top of the canvas. If you want to avoid this behavior you should avoid Svelte if-blocks and use the `visible` prop to control whether a shape is visible or not. This way the component is not mounted/unmounted and maintains its initial drawing order on the canvas.
Instructions: Try to drag a circle. See how it goes to the top. This is done by calling `moveToTop()` on the dragged shape handle.
[Open the interactive demo](https://codesandbox.io/p/sandbox/github/konvajs/site/tree/master/svelte-demos/zIndex?file=/src/App.svelte)
---
# Konva.js Tools and Plugins
> Explore the official Konva integrations for React, Vue, Svelte, and Angular.
Source: https://konvajs.org/docs/tools.html
- [Konva + React](https://github.com/konvajs/react-konva/)
- [Konva + Vue](https://github.com/konvajs/vue-konva)
- [Konva + Svelte](https://github.com/konvajs/svelte-konva)
- [Konva + Angular](https://github.com/konvajs/ng2-konva)
## Debugging
- [konva-devtool](https://github.com/konvajs/konva-devtool) — official browser extension that shows the Konva scene graph and node attributes, instead of the single `` element the built-in inspector sees
- [konva-inspector](https://github.com/maitrungduc1410/konva-inspector) — community extension with a profiler and a `$konva` console handle; supports Konva 9 and 10
---
# All Tween Controls Tutorial
> Learn how to control Konva.js tweens with play, pause, reverse, reset, finish, and seek methods.
Source: https://konvajs.org/docs/tweens/All_Controls.html
To control tweens with Konva, we can use the following methods:
- `play()` - Start or resume the tween
- `pause()` - Pause the tween
- `reverse()` - Reverse the tween direction
- `reset()` - Reset to initial state
- `finish()` - Jump to final state
- `seek()` - Jump to specific position
**Instructions: Use the buttons to control the tween animation of the circle.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
const circle = new Konva.Circle({
x: 100,
y: height / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
layer.add(circle);
stage.add(layer);
const tween = new Konva.Tween({
node: circle,
duration: 2,
x: width - 100,
easing: Konva.Easings.EaseInOut,
});
// create buttons
const controls = ['play', 'pause', 'reverse', 'reset', 'finish'];
controls.forEach(control => {
const button = document.createElement('button');
button.textContent = control;
button.addEventListener('click', () => {
tween[control]();
});
document.body.appendChild(button);
});
// seek control
const seekBtn = document.createElement('button');
seekBtn.textContent = 'Seek to 50%';
seekBtn.addEventListener('click', () => {
tween.seek(1); // seek to 1 second
});
document.body.appendChild(seekBtn);
````
```js
import Konva from 'konva';
import { Stage, Layer, Circle } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const circleRef = useRef();
const tweenRef = useRef();
useEffect(() => {
if (!circleRef.current) return;
const tween = new Konva.Tween({
node: circleRef.current,
duration: 2,
x: window.innerWidth - 100,
easing: Konva.Easings.EaseInOut,
});
tweenRef.current = tween;
return () => tween.destroy();
}, []);
const controls = ['play', 'pause', 'reverse', 'reset', 'finish'];
return (
<>
{controls.map(control => (
tweenRef.current?.[control]()}
>
{control}
))}
tweenRef.current?.seek(1)}>
Seek to 50%
>
);
};
export default App;
````
```js
{{ control }}
Seek to 50%
```
---
# More Easing Functions Tutorial
> See all Konva.js easing functions in action: Linear, Ease, Back, Elastic, Bounce, and Strong with interactive demos.
Source: https://konvajs.org/docs/tweens/All_Easings.html
This tutorial demonstrates all of the easing function sets provided by Konva, including:
- `Linear`
- `Ease`
- `Back`
- `Elastic`
- `Bounce`
- `Strong`
For all available easings go to [Easings Documentation](/api/Konva.Easings.html).
**Instructions: Press "Play" to transition all of the text nodes with different easing functions.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
const easings = [
'Linear',
'EaseIn',
'EaseOut',
'EaseInOut',
'BackEaseIn',
'BackEaseOut',
'BackEaseInOut',
'ElasticEaseIn',
'ElasticEaseOut',
'ElasticEaseInOut',
'BounceEaseIn',
'BounceEaseOut',
'BounceEaseInOut',
'StrongEaseIn',
'StrongEaseOut',
'StrongEaseInOut',
];
const tweens = [];
easings.forEach((easing, i) => {
const text = new Konva.Text({
x: 50,
y: 30 + i * 25,
text: easing,
fontSize: 16,
fontFamily: 'Calibri',
fill: 'black',
});
layer.add(text);
tweens.push(
new Konva.Tween({
node: text,
duration: 2,
x: width - 200,
easing: Konva.Easings[easing],
})
);
});
stage.add(layer);
// create button
const button = document.createElement('button');
button.textContent = 'Play';
button.style.position = 'absolute';
button.style.top = '0px';
button.style.left = '0px';
button.addEventListener('click', () => {
tweens.forEach((tween) => {
tween.reset();
tween.play();
});
});
document.body.appendChild(button);
````
```js
import Konva from 'konva';
import { Stage, Layer, Text } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const tweensRef = useRef([]);
const textsRef = useRef([]);
const easings = [
'Linear',
'EaseIn',
'EaseOut',
'EaseInOut',
'BackEaseIn',
'BackEaseOut',
'BackEaseInOut',
'ElasticEaseIn',
'ElasticEaseOut',
'ElasticEaseInOut',
'BounceEaseIn',
'BounceEaseOut',
'BounceEaseInOut',
'StrongEaseIn',
'StrongEaseOut',
'StrongEaseInOut',
];
useEffect(() => {
tweensRef.current = textsRef.current.map((text, i) => {
return new Konva.Tween({
node: text,
duration: 2,
x: window.innerWidth - 200,
easing: Konva.Easings[easings[i]],
});
});
return () => {
tweensRef.current.forEach((tween) => tween.destroy());
};
}, []);
const handlePlay = () => {
tweensRef.current.forEach((tween) => {
tween.reset();
tween.play();
});
};
return (
<>
Play
{easings.map((easing, i) => (
{
textsRef.current[i] = node;
}}
x={50}
y={30 + i * 25}
text={easing}
fontSize={16}
fontFamily="Calibri"
fill="black"
/>
))}
>
);
};
export default App;
````
```js
Play
```
---
# Simple Easings Tutorial
> Learn how to use common easing functions like EaseIn, EaseOut, and EaseInOut for smooth Konva.js tween animations.
Source: https://konvajs.org/docs/tweens/Common_Easings.html
To create a non-linear easing tween with Konva, we can set the `easing` property to an easing function. Other than `Konva.Easings.Linear`, the other most common easings are:
- `Konva.Easings.EaseIn`
- `Konva.Easings.EaseInOut`
- `Konva.Easings.EaseOut`
For all available easings go to [Easings Documentation](/api/Konva.Easings.html).
**Instructions: Mouseover or touchstart the boxes to tween them with different easing functions.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
const easings = ['Linear', 'EaseIn', 'EaseOut', 'EaseInOut'];
const boxes = [];
easings.forEach((easing, i) => {
const box = new Konva.Rect({
x: 50,
y: 50 + i * 80,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
});
layer.add(box);
boxes.push(box);
const text = new Konva.Text({
x: 160,
y: 65 + i * 80,
text: easing,
fontSize: 16,
fontFamily: 'Calibri',
fill: 'black',
});
layer.add(text);
box.on('mouseenter touchstart', () => {
const tween = new Konva.Tween({
node: box,
duration: 1,
x: width - 150,
easing: Konva.Easings[easing],
}).play();
});
box.on('mouseleave touchend', () => {
const tween = new Konva.Tween({
node: box,
duration: 1,
x: 50,
easing: Konva.Easings[easing],
}).play();
});
});
stage.add(layer);
````
```js
import Konva from 'konva';
import { Stage, Layer, Rect, Text } from 'react-konva';
import { useRef } from 'react';
const Box = ({ easing, y }) => {
const boxRef = useRef();
const handleMouseEnter = () => {
boxRef.current.to({
duration: 1,
x: window.innerWidth - 150,
easing: Konva.Easings[easing],
});
};
const handleMouseLeave = () => {
boxRef.current.to({
duration: 1,
x: 50,
easing: Konva.Easings[easing],
});
};
return (
<>
>
);
};
const App = () => {
const easings = ['Linear', 'EaseIn', 'EaseOut', 'EaseInOut'];
return (
{easings.map((easing, i) => (
))}
);
};
export default App;
````
```js
```
---
# Complex Tweening Tutorial
> Learn how to create complex tween animations in Konva.js including gradient transitions and chained tweens.
Source: https://konvajs.org/docs/tweens/Complex_Tweening.html
This demo combines chained `Konva.Tween` instances with a `Konva.Animation`.
The tweens change the circle scale. The animation changes the
`fillLinearGradientColorStops` property.
**Instructions: Click the shape to start the complex animation with gradient changes.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
const circle = new Konva.Circle({
x: width / 2,
y: height / 2,
radius: 70,
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
stroke: 'black',
strokeWidth: 4,
draggable: true,
});
layer.add(circle);
stage.add(layer);
let scaleUpTween;
let scaleDownTween;
let gradientAnimation;
let gradientTimer;
const stopAnimation = () => {
scaleUpTween?.destroy();
scaleDownTween?.destroy();
gradientAnimation?.stop();
clearTimeout(gradientTimer);
scaleUpTween = undefined;
scaleDownTween = undefined;
gradientAnimation = undefined;
gradientTimer = undefined;
};
circle.on('click tap', () => {
stopAnimation();
// using regular Konva tween
scaleUpTween = new Konva.Tween({
node: circle,
duration: 1,
scaleX: 1.5,
scaleY: 1.5,
easing: Konva.Easings.EaseInOut,
onFinish: () => {
scaleUpTween.destroy();
scaleUpTween = undefined;
// scale back with another tween
scaleDownTween = new Konva.Tween({
node: circle,
duration: 1,
scaleX: 1,
scaleY: 1,
easing: Konva.Easings.BounceEaseOut,
onFinish: () => {
scaleDownTween.destroy();
scaleDownTween = undefined;
},
});
scaleDownTween.play();
},
});
scaleUpTween.play();
// manually update gradient
let ratio = 0;
gradientAnimation = new Konva.Animation((frame) => {
ratio += frame.timeDiff / 1000;
if (ratio > 1) {
ratio = 0;
}
circle.fillLinearGradientColorStops([
0,
'red',
ratio,
'yellow',
1,
'blue',
]);
}, layer);
gradientAnimation.start();
gradientTimer = setTimeout(() => {
gradientAnimation.stop();
gradientAnimation = undefined;
gradientTimer = undefined;
}, 2000);
});
````
```js
import Konva from 'konva';
import { Stage, Layer, Circle } from 'react-konva';
import { useEffect, useRef, useState } from 'react';
const App = () => {
const circleRef = useRef();
const scaleUpTweenRef = useRef();
const scaleDownTweenRef = useRef();
const gradientAnimationRef = useRef();
const gradientTimerRef = useRef();
const [position, setPosition] = useState({
x: window.innerWidth / 2,
y: window.innerHeight / 2,
});
const stopAnimation = () => {
scaleUpTweenRef.current?.destroy();
scaleDownTweenRef.current?.destroy();
gradientAnimationRef.current?.stop();
clearTimeout(gradientTimerRef.current);
scaleUpTweenRef.current = undefined;
scaleDownTweenRef.current = undefined;
gradientAnimationRef.current = undefined;
gradientTimerRef.current = undefined;
};
useEffect(() => stopAnimation, []);
const handleClick = () => {
const circle = circleRef.current;
stopAnimation();
// using regular Konva tween
scaleUpTweenRef.current = new Konva.Tween({
node: circle,
duration: 1,
scaleX: 1.5,
scaleY: 1.5,
easing: Konva.Easings.EaseInOut,
onFinish: () => {
scaleUpTweenRef.current.destroy();
scaleUpTweenRef.current = undefined;
// scale back with another tween
scaleDownTweenRef.current = new Konva.Tween({
node: circle,
duration: 1,
scaleX: 1,
scaleY: 1,
easing: Konva.Easings.BounceEaseOut,
onFinish: () => {
scaleDownTweenRef.current.destroy();
scaleDownTweenRef.current = undefined;
},
});
scaleDownTweenRef.current.play();
},
});
scaleUpTweenRef.current.play();
// manually update gradient
let ratio = 0;
gradientAnimationRef.current = new Konva.Animation((frame) => {
ratio += frame.timeDiff / 1000;
if (ratio > 1) {
ratio = 0;
}
circle.fillLinearGradientColorStops([
0,
'red',
ratio,
'yellow',
1,
'blue',
]);
}, circle.getLayer());
gradientAnimationRef.current.start();
gradientTimerRef.current = setTimeout(() => {
gradientAnimationRef.current.stop();
gradientAnimationRef.current = undefined;
gradientTimerRef.current = undefined;
}, 2000);
};
return (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
onClick={handleClick}
onTap={handleClick}
/>
);
};
export default App;
````
```js
```
---
# HTML5 Canvas Tween Finish Event Tutorial
> Learn how to trigger a callback function when a Konva.js tween animation finishes using the onFinish property.
Source: https://konvajs.org/docs/tweens/Finish_Event/index.html
To trigger a user defined function when the tween finishes with Konva, we can set the `onFinish` property.
---
# Basic Tweening Tutorial
> Learn how to create basic linear tween animations in Konva.js to transition shape properties like position, scale, and opacity.
Source: https://konvajs.org/docs/tweens/Linear_Easing.html
To tween properties with Konva, we can instantiate a `Konva.Tween` object and then start the tween by calling `play()`.
Any numeric property of a `Shape`, `Group`, `Layer`, or `Stage` can be transitioned, such as:
- `x`, `y` (position)
- `rotation`
- `width`, `height`, `radius`
- `strokeWidth`
- `opacity`
- `scaleX`, `scaleY`
- `offsetX`, `offsetY`
For a full list of attributes and methods, check out the [Konva.Tween documentation](/api/Konva.Tween.html).
**Instructions: Click the circle to start a simple linear animation.**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
const circle = new Konva.Circle({
x: 100,
y: height / 2,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
});
layer.add(circle);
stage.add(layer);
circle.on('click tap', () => {
// simple tween
const tween = new Konva.Tween({
node: circle,
duration: 1,
x: width - 100,
easing: Konva.Easings.Linear,
});
tween.play();
});
````
```js
import Konva from 'konva';
import { Stage, Layer, Circle } from 'react-konva';
import { useEffect, useRef } from 'react';
const App = () => {
const circleRef = useRef();
const tweenRef = useRef();
useEffect(() => {
return () => tweenRef.current?.destroy();
}, []);
const handleClick = () => {
tweenRef.current?.destroy();
tweenRef.current = new Konva.Tween({
node: circleRef.current,
duration: 1,
x: window.innerWidth - 100,
easing: Konva.Easings.Linear,
});
tweenRef.current.play();
};
return (
);
};
export default App;
````
```js
```
---
# Tween Blur Filter Tutorial
> Learn how to animate filter properties like blurRadius using Konva.js tweens for smooth filter transitions on images.
Source: https://konvajs.org/docs/tweens/Tween_Filter.html
To tween a filter using Konva, we can simply tween the properties associated with the filter.
In this tutorial, we'll tween the `blurRadius` property, which controls the amount of blur applied to the image.
**Instructions: Mouseover or touch the image to focus it (reduce blur).**
```js
import Konva from 'konva';
const width = window.innerWidth;
const height = window.innerHeight;
const stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
const layer = new Konva.Layer();
// create image
const imageObj = new Image();
imageObj.onload = () => {
const lion = new Konva.Image({
x: 50,
y: 50,
image: imageObj,
draggable: true,
});
layer.add(lion);
// add blur filter
lion.cache();
lion.filters([Konva.Filters.Blur]);
lion.blurRadius(10);
// create blur tween
const tween = new Konva.Tween({
node: lion,
duration: 0.5,
blurRadius: 0,
easing: Konva.Easings.EaseInOut,
});
// bind events
lion.on('mouseenter touchstart', () => {
tween.play();
});
lion.on('mouseleave touchend', () => {
tween.reverse();
});
};
imageObj.src = '/assets/lion.png';
imageObj.crossOrigin = 'anonymous';
stage.add(layer);
````
```js
import Konva from 'konva';
import { Stage, Layer, Image } from 'react-konva';
import { useEffect, useRef, useState } from 'react';
import useImage from 'use-image';
const App = () => {
const imageRef = useRef();
const [image] = useImage('/assets/lion.png');
const tweenRef = useRef();
const [position, setPosition] = useState({ x: 50, y: 50 });
useEffect(() => {
if (!image || !imageRef.current) return;
const node = imageRef.current;
node.cache();
node.filters([Konva.Filters.Blur]);
node.blurRadius(10);
const tween = new Konva.Tween({
node: node,
duration: 0.5,
blurRadius: 0,
easing: Konva.Easings.EaseInOut,
});
tweenRef.current = tween;
return () => {
tween.destroy();
if (tweenRef.current === tween) tweenRef.current = null;
};
}, [image]);
const handleMouseEnter = () => {
tweenRef.current?.play();
};
const handleMouseLeave = () => {
tweenRef.current?.reverse();
};
return (
{
setPosition({ x: e.target.x(), y: e.target.y() });
}}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onTouchStart={handleMouseEnter}
onTouchEnd={handleMouseLeave}
/>
);
};
export default App;
````
```js
```
---
# How to cache canvas shapes with Vue?
> Learn how to cache canvas shapes in Vue with vue-konva for improved rendering performance using node.cache().
Source: https://konvajs.org/docs/vue/Cache.html
If you want to cache a node in a Vue app, you need to have access to the Konva node and use the `node.cache()` function.
To get access to a node you can use references and the `component.getNode()` method:
```javascript
// in template:
// later in the code:
this.$refs.group.getNode().cache();
```
Instructions: Try to drag the whole stage. Then enable caching and try again. You should see much better performance with caching enabled.
```js
```
---
# How to draw custom canvas shape with Vue?
> Learn how to draw custom canvas shapes in Vue using the v-shape component and HTML5 Canvas drawing functions.
Source: https://konvajs.org/docs/vue/Custom_Shape.html
To create a custom shape with `vue-konva`, we should use the `v-shape` component.
When creating a custom shape, we need to define a drawing function that is passed a Konva.Canvas renderer.
We can use the renderer to access the HTML5 Canvas context, and to use special methods like `context.fillStrokeShape(shape)` which automatically handles filling, stroking, and applying shadows.
Instructions: The demo shows a custom shape drawn using canvas drawing commands.
```js
```
---
# How to implement drag and drop for canvas shapes with Vue?
> Learn how to add drag and drop to canvas shapes in Vue with vue-konva using the draggable property and drag events.
Source: https://konvajs.org/docs/vue/Drag_And_Drop.html
To enable drag&drop for any node on canvas you just need to pass `draggable: true` property into the component.
When you drag&drop a shape, it is recommended to save its position in your app store. You can use `dragstart` and `dragend` events for that purpose.
Instructions: Try to drag the text. Notice how it changes color while being dragged.
```js
```
---
# How to listen to events on canvas shapes with Vue and Konva?
> Learn how to listen to mouse, touch, and drag events on canvas shapes with Vue and Konva.
Source: https://konvajs.org/docs/vue/Events.html
With `vue-konva` you can easily listen to user input events (`click`, `dblclick`, `mouseover`, `tap`, `dbltap`, `touchstart`, etc...) and drag&drop events (`dragstart`, `dragmove`, `dragend`).
For the full list of events take a look at the [on() method documentation](/api/Konva.Node.html).
Instructions: Move your mouse over the triangle to see coordinates. Move the mouse out to see the mouseout event.
```js
```
---
# How to apply canvas filters with Vue and Konva?
> Learn how to apply canvas filters like noise and blur to shapes in Vue with Konva using caching and recaching.
Source: https://konvajs.org/docs/vue/Filters.html
To apply filters you need to cache `Konva.Node` manually. You can do it in the `mounted()` hook.
You will need to recache nodes every time you update their styles.
Instructions: Move your mouse over the rectangle to see color changes with noise filter applied.
```js
```
---
# How to draw images on canvas with Vue?
> Learn how to load and display images on an HTML5 Canvas with Vue using the vue-konva useImage hook.
Source: https://konvajs.org/docs/vue/Images.html
For images, you can use the `useImage` hook from `vue-konva` to easily load and handle images in your components.
Instructions: The demo shows how to load and display multiple images on the canvas.
```js
```
---
# How to save and load canvas with Vue and Konva?
> Learn how to save and load canvas state in Vue with Konva by serializing your app state to localStorage.
Source: https://konvajs.org/docs/vue/Save-Load.html
## How to serialize and deserialize Konva stage with Vue?
Pure Konva can serialize a node tree and its serializable attributes with `node.toJSON()`. It can restore them with `Konva.Node.create(json)`. Restore images, event handlers, and custom drawing functions separately.
[See demo](/docs/data_and_serialization/Simple_Load.html).
With `vue-konva`, define the application state in your Vue components. The state maps to nodes through templates. Save and load the application state instead of Konva internals and nodes.
Instructions: Click on the canvas to create circles. Reload the page - the circles should persist.
```js
Click on canvas to create a circle.
Reload the page . Circles should stay here.
```
---
# Drawing canvas shapes with Vue
> Learn how to draw canvas shapes like rectangles, circles, lines, and more in Vue using vue-konva components.
Source: https://konvajs.org/docs/vue/Shapes.html
All `vue-konva` components correspond to Konva components of the same name with the prefix 'v'. All the parameters available for Konva objects can be added as config in the prop for corresponding `vue-konva` components.
Core shapes are: `v-rect`, `v-circle`, `v-ellipse`, `v-line`, `v-image`, `v-text`, `v-text-path`, `v-star`, `v-label`, `v-path`, `v-regular-polygon`. You can also create a [custom shape](/docs/vue/Custom_Shape.html).
To get more info about Konva you can read [Konva Overview](/docs/overview.html).
Instructions: The demo shows various shapes with different styles and configurations.
```js
```
---
# How to apply canvas animations with Vue and Konva?
> Learn how to animate canvas shapes in Vue using Konva Tweens, node.to() method, and Konva.Animation for continuous motion.
Source: https://konvajs.org/docs/vue/Simple_Animations.html
Konva provides two methods for animations: [Tween](/docs/tweens/Linear_Easing.html) and [Animation](/docs/animations/Rotation.html). For simple use cases, we recommend using the `node.to()` method, which is a simplified version of Tween.
Instructions: Try to drag the green rectangle to see it scale randomly, and observe the red hexagon moving in a sine wave pattern.
```js
```
The demo above shows two types of animations:
1. Using `node.to()` method (Tween) to animate the green rectangle's scale when dragged
2. Using `Konva.Animation` to create a continuous sine wave movement for the red hexagon
The `node.to()` method is perfect for simple transitions, while `Konva.Animation` is better for complex, continuous animations that need to run on each frame.
---
# How to resize and rotate canvas shapes with Vue and Konva?
> Learn how to resize and rotate canvas shapes in Vue using the Konva Transformer component with click-to-select.
Source: https://konvajs.org/docs/vue/Transformer.html
Currently, there is no pure declarative "Vue-way" to use the Transformer tool.
However, you can still use it effectively by manually attaching it to Konva nodes.
The idea is to create a `v-transformer` component and manually attach it to the required node when selected.
Instructions: Click on a shape to select it. You can then resize and rotate it using the transformer handles. The shape will change color after transformation.
```js
```
## What Transformer does not do
`Transformer` draws the handles and applies the scale. Snapping to other
objects, alignment guides, a shared bounding box for a multi-selection, and
per-shape aspect rules are all yours to build — see
[objects snapping](/docs/sandbox/Objects_Snapping.html) for one approach.
A production editor also needs text editing, templates, and export around the
Transformer. If you would rather not build those, [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=docs&utm_content=vue-transformer) is a commercial
design editor SDK built on Konva by the Konva maintainers that ships them.
---
# How to implement undo/redo on canvas with Vue?
> Learn how to implement undo and redo for canvas shapes in Vue by tracking state history with Vue reactivity.
Source: https://konvajs.org/docs/vue/Undo-Redo.html
To implement undo/redo functionality with Vue, you don't need to use Konva's serialization and deserialization methods.
You just need to save a history of all the state changes within your app. There are many ways to do this. It may be simpler to do that if you use immutable structures.
Instructions: Try to move the square by dragging it. Then use the "undo" and "redo" buttons to revert or replay your actions.
```js
```
The demo shows how to:
1. Keep track of position history using Vue's reactivity system
2. Implement undo/redo functionality by navigating through the history
3. Update the history when dragging ends
4. Use `reactive` for the current position and `ref` for the history to maintain reactivity
Note that we're using Vue's reactivity system to manage the state, but we're careful to avoid unnecessary re-renders by keeping the history in a `ref`.
## Where a hand-built history stops
The history above records one value per step. A production editor has to record
grouped operations, so that a multi-select drag undoes as a single step, plus
transforms and images that finish loading after the action. That state machine
usually grows larger than the drawing code, so plan the history around document
operations rather than around raw node state.
---
# How to change the zIndex of nodes with Vue?
> Learn how to change zIndex and reorder canvas shapes in Vue by manipulating data array order instead of using zIndex.
Source: https://konvajs.org/docs/vue/zIndex.html
## How to change the zIndex and reorder components in `vue-konva`?
When you are working with `Konva` directly, you have many methods to change the order of nodes like `node.zIndex(5)`, `node.moveToTop()`, etc. See the [Layering Tutorial](/docs/groups_and_layers/Layering.html) for more details.
However, when working with Vue, it's recommended to follow Vue's declarative approach instead of using these imperative methods.
`vue-konva` follows the order of the nodes exactly as you describe them in your ``. Instead of changing the `zIndex` manually, you should update your app's data so that the components in your `` maintain the correct order.
The demo shows how to:
1. Create an array of circle shapes with random positions and colors
2. Handle drag events to update the visual order of shapes
3. Maintain the correct stacking order by manipulating the array order
4. Follow Vue's reactivity system for state management
Remember: Don't use the `zIndex` property for your canvas components. Instead, rely on the order of elements in your template and data structures.
Instructions: Try to drag a circle. When you start dragging, it will automatically move to the top of the stack. This is achieved by manipulating the array of circles in our data, not by manually changing zIndex.
```js
```