HTML5 Canvas 优化描边性能提示
在 Konva 中绘制带有描边和阴影的形状时,会发生额外的内部绘制步骤。这是因为 Konva 需要确保描边的阴影被正确绘制。然而,这可能会影响性能,特别是处理许多形状时。
为了优化性能,您可以通过设置 shadowForStrokeEnabled(false)
来禁用描边阴影。当您不需要描边投影阴影时,这尤其有用。
下面是一个演示,展示了带和不带描边阴影的性能差异:
- 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 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, }); // 创建不带描边阴影的形状(优化) 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, }); // 添加标签 const defaultLabel = new Konva.Text({ x: 50, y: 170, text: '带描边阴影', fontSize: 16, }); const optimizedLabel = new Konva.Text({ x: 200, y: 170, text: '不带描边阴影\n(更好的性能)', fontSize: 16, }); // 添加 FPS 计数器 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); // 创建动画以演示性能 const anim = new Konva.Animation((frame) => { circleWithShadow.rotation(frame.time * 0.1); circleOptimized.rotation(frame.time * 0.1); // 更新 FPS 计数器 fpsText.text('FPS: ' + frame.frameRate.toFixed(1)); }, layer); anim.start();
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) => { // 旋转圆形 circleWithShadowRef.current.rotation(frame.time * 0.1); circleOptimizedRef.current.rotation(frame.time * 0.1); // 更新 FPS 计数器 fpsTextRef.current.text('FPS: ' + frame.frameRate.toFixed(1)); }, circleWithShadowRef.current.getLayer()); anim.start(); return () => anim.stop(); }, []); return ( <Stage width={window.innerWidth} height={window.innerHeight}> <Layer> {/* 带有描边阴影的圆形(默认) */} <Circle ref={circleWithShadowRef} x={100} y={100} radius={50} fill="red" stroke="black" strokeWidth={4} shadowColor="black" shadowBlur={10} shadowOffset={{ x: 5, y: 5 }} shadowOpacity={0.5} /> {/* 不带描边阴影的圆形(优化) */} <Circle ref={circleOptimizedRef} 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} /> {/* 标签 */} <Text x={50} y={170} text="带描边阴影" fontSize={16} /> <Text x={200} y={170} text="不带描边阴影\n(更好的性能)" fontSize={16} /> {/* FPS 计数器 */} <Text ref={fpsTextRef} x={10} y={10} text="FPS: 0" fontSize={16} /> </Layer> </Stage> ); }; export default App;
<template> <v-stage :config="stageSize"> <v-layer ref="layerRef"> <!-- 带有描边阴影的圆形(默认) --> <v-circle ref="circleWithShadowRef" :config="circleWithShadowConfig" /> <!-- 不带描边阴影的圆形(优化) --> <v-circle ref="circleOptimizedRef" :config="circleOptimizedConfig" /> <!-- 标签 --> <v-text :config="defaultLabelConfig" /> <v-text :config="optimizedLabelConfig" /> <!-- FPS 计数器 --> <v-text ref="fpsTextRef" :config="fpsConfig" /> </v-layer> </v-stage> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue'; import Konva from 'konva'; const stageSize = { width: window.innerWidth, height: window.innerHeight }; const circleWithShadowConfig = { x: 100, y: 100, radius: 50, fill: 'red', stroke: 'black', strokeWidth: 4, shadowColor: 'black', shadowBlur: 10, shadowOffset: { x: 5, y: 5 }, shadowOpacity: 0.5 }; const circleOptimizedConfig = { 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 }; const defaultLabelConfig = { x: 50, y: 170, text: '带描边阴影', fontSize: 16 }; const optimizedLabelConfig = { x: 200, y: 170, text: '不带描边阴影\n(更好的性能)', fontSize: 16 }; const fpsConfig = ref({ x: 10, y: 10, text: 'FPS: 0', fontSize: 16 }); const layerRef = ref(null); const circleWithShadowRef = ref(null); const circleOptimizedRef = ref(null); const fpsTextRef = ref(null); let anim = null; onMounted(() => { anim = new Konva.Animation((frame) => { // 旋转圆形 circleWithShadowRef.value.getNode().rotation(frame.time * 0.1); circleOptimizedRef.value.getNode().rotation(frame.time * 0.1); // 更新 FPS 计数器 fpsTextRef.value.getNode().text('FPS: ' + frame.frameRate.toFixed(1)); }, layerRef.value.getNode()); anim.start(); }); onUnmounted(() => { if (anim) { anim.stop(); } }); </script>