HTML5 Canvas 动画性能优化技巧
在使用 Konva 创建动画时,优化它们以获得更好的性能非常重要。 以下是一些关键提示:
- 使用
Konva.Animation
而不是直接使用requestAnimationFrame
- 仅动画需要变化的属性
- 考虑对复杂形状使用形状缓存
- 最小化正在动画处理的节点数量
下面是一个显示优化动画技术的演示:
- 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); // 创建复杂的星形 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, }); // 为了更好的性能缓存形状 star.cache(); layer.add(star); // 创建不需要缓存的简单圆形 const circle = new Konva.Circle({ x: 100, y: 100, radius: 20, fill: 'red', }); layer.add(circle); // 创建优化的动画 const anim = new Konva.Animation((frame) => { // 旋转星形(缓存形状) star.rotation(frame.time * 0.1); // 圆形按圆形轨迹移动 circle.x(100 + Math.cos(frame.time * 0.002) * 50); circle.y(100 + Math.sin(frame.time * 0.002) * 50); }, layer); // 添加开始/停止按钮 const button = document.createElement('button'); button.textContent = '切换动画'; 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 = '开始动画'; } else { anim.start(); button.textContent = '停止动画'; } 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(() => { // 缓存星形以获得更好的性能 if (starRef.current) { starRef.current.cache(); } // 创建动画 const anim = new Konva.Animation((frame) => { // 旋转星形(缓存形状) starRef.current.rotation(frame.time * 0.1); // 圆形按圆形轨迹移动 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 ? '停止动画' : '开始动画'} </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 ? '停止动画' : '开始动画' }} </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(() => { // 缓存星形以获得更好的性能 starRef.value.getNode().cache(); // 创建动画 anim = new Konva.Animation((frame) => { // 旋转星形(缓存形状) starRef.value.getNode().rotation(frame.time * 0.1); // 圆形按圆形轨迹移动 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>