复杂补间教程
此演示组合使用链式 Konva.Tween 实例和一个 Konva.Animation。
补间动画更改圆的缩放比例。动画更改
fillLinearGradientColorStops 属性。
操作说明:单击图形,启动包含渐变变化的复杂动画。
- Vanilla
- React
- Vue
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);
});
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 (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Circle
ref={circleRef}
x={position.x}
y={position.y}
radius={70}
fillLinearGradientStartPoint={{ x: -50, y: -50 }}
fillLinearGradientEndPoint={{ x: 50, y: 50 }}
fillLinearGradientColorStops={[0, 'red', 1, 'yellow']}
stroke="black"
strokeWidth={4}
draggable
onDragEnd={(e) => {
setPosition({ x: e.target.x(), y: e.target.y() });
}}
onClick={handleClick}
onTap={handleClick}
/>
</Layer>
</Stage>
);
};
export default App;
<template>
<v-stage :config="stageSize">
<v-layer ref="layerRef">
<v-circle
:config="circleConfig"
@click="handleClick"
@tap="handleClick"
ref="circleRef"
/>
</v-layer>
</v-stage>
</template>
<script setup>
import { onUnmounted, ref } from 'vue';
import Konva from 'konva';
const stageSize = {
width: window.innerWidth,
height: window.innerHeight
};
const circleConfig = {
x: window.innerWidth / 2,
y: window.innerHeight / 2,
radius: 70,
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 50, y: 50 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow'],
stroke: 'black',
strokeWidth: 4,
draggable: true
};
const circleRef = ref(null);
const layerRef = ref(null);
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;
};
onUnmounted(stopAnimation);
const handleClick = () => {
const circle = circleRef.value.getNode();
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',
]);
}, layerRef.value.getNode());
gradientAnimation.start();
gradientTimer = setTimeout(() => {
gradientAnimation.stop();
gradientAnimation = undefined;
gradientTimer = undefined;
}, 2000);
};
</script>