文本动画教程
注意:此功能仅适用于 Konva v10.0.0 及以上版本。
Konva 通过 charRenderFunc 属性提供强大的文本动画功能。此函数允许您自定义每个字符的渲染方式,从而实现逐字符动画和特效。
var text = new Konva.Text({
x: 10,
y: 10,
text: 'AB',
fontSize: 20,
charRenderFunc: function ({ context, index }) {
if (index === 1) {
// 仅偏移第二个字符
context.translate(0, 10);
}
},
});
charRenderFunc 接收一个包含以下参数的上下文对象:
char- 正在渲染的实际字符字符串index- 字符在整个文本中的从零开始的索引x- 字符将被渲染的 X 位置y- 字符将被渲染的 Y 位置lineIndex- 包含此字符的行从零开始的索引column- 当前行内从零开始的列位置isLastInLine- 布尔值,指示是否为行中的最后一个字符width- 字符的宽度context- Canvas 2D 渲染上下文,用于应用变换、透明度、颜色等
这使您可以根据字符的位置和属性,对单个字符应用变换、透明度更改或其他效果。
- 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); // 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();
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 ( <Stage width={window.innerWidth} height={window.innerHeight}> <Layer ref={layerRef}> <Text ref={textRef} x={window.innerWidth / 2 - 100} y={window.innerHeight / 2 - 20} text="ANIMATION" fontSize={40} fontFamily="Arial" fill="#333" charRenderFunc={({ context, index }) => { context.globalAlpha = charOpacitiesRef.current[index] || 0; }} /> </Layer> </Stage> ); }; export default App;
<template> <v-stage :config="stageSize"> <v-layer ref="layerRef"> <v-text ref="textRef" :config="textConfig" /> </v-layer> </v-stage> </template> <script setup> import { ref, onMounted, onUnmounted, reactive } from 'vue'; import Konva from 'konva'; const stageSize = { width: window.innerWidth, height: window.innerHeight }; const textConfig = reactive({ x: window.innerWidth / 2 - 100, y: window.innerHeight / 2 - 20, text: 'ANIMATION', fontSize: 40, fontFamily: 'Arial', fill: '#333', charRenderFunc: ({ context, index }) => { context.globalAlpha = charOpacities[index] || 0; } }); const charOpacities = reactive([]); const layerRef = ref(null); const textRef = ref(null); let anim = null; onMounted(() => { 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.value.getNode().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); } } }, layerRef.value.getNode()); anim.start(); }); onUnmounted(() => { if (anim) { anim.stop(); } }); </script>