HTML5 Canvas Optimize Animation Performance Tip
When creating animations with Konva, it's important to optimize them for better performance. Here are some key tips:
- Use
Konva.Animationinstead ofrequestAnimationFramedirectly - Only animate properties that need to change
- Consider using shape caching for complex shapes
- Minimize the number of nodes being animated
Below is a demo showing optimized animation techniques:
- Vanilla
- React
- Vue
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();
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 (
<div>
<button
onClick={toggleAnimation}
style={{ position: 'absolute', top: '10px', left: '10px' }}
>
{isPlaying ? 'Stop Animation' : 'Start Animation'}
</button>
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Star
ref={starRef}
x={window.innerWidth / 2}
y={window.innerHeight / 2}
numPoints={6}
innerRadius={40}
outerRadius={70}
fill="yellow"
stroke="black"
strokeWidth={4}
/>
<Circle
ref={circleRef}
x={100}
y={100}
radius={20}
fill="red"
/>
</Layer>
</Stage>
</div>
);
};
export default App;
<template>
<div>
<button
@click="toggleAnimation"
style="position: absolute; top: 10px; left: 10px"
>
{{ isPlaying ? 'Stop Animation' : 'Start Animation' }}
</button>
<v-stage :config="stageSize">
<v-layer ref="layerRef">
<v-star
ref="starRef"
:config="starConfig"
/>
<v-circle
ref="circleRef"
:config="circleConfig"
/>
</v-layer>
</v-stage>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import Konva from 'konva';
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const starConfig = {
x: window.innerWidth / 2,
y: window.innerHeight / 2,
numPoints: 6,
innerRadius: 40,
outerRadius: 70,
fill: 'yellow',
stroke: 'black',
strokeWidth: 4
};
const circleConfig = ref({
x: 100,
y: 100,
radius: 20,
fill: 'red'
});
const layerRef = ref(null);
const starRef = ref(null);
const circleRef = ref(null);
const isPlaying = ref(true);
let anim = null;
onMounted(() => {
// Cache the star shape for better performance
starRef.value.getNode().cache();
// Create animation
anim = new Konva.Animation((frame) => {
// Rotate star (cached shape)
starRef.value.getNode().rotation(frame.time * 0.1);
// Move circle in a circle pattern
const circle = circleRef.value.getNode();
circle.x(100 + Math.cos(frame.time * 0.002) * 50);
circle.y(100 + Math.sin(frame.time * 0.002) * 50);
}, layerRef.value.getNode());
anim.start();
});
onUnmounted(() => {
if (anim) {
anim.stop();
}
});
const toggleAnimation = () => {
if (isPlaying.value) {
anim.stop();
} else {
anim.start();
}
isPlaying.value = !isPlaying.value;
};
</script>