11<script setup>
2- import { computed , nextTick , ref , toRef , watch } from ' vue'
2+ import { computed , ref , toRef , watch } from ' vue'
33import { BaseEdge , EdgeLabelRenderer , Position , getSmoothStepPath , useNodesData , useVueFlow } from ' @vue-flow/core'
4- import { TransitionPresets , executeTransition } from ' @vueuse/core'
54
65const props = defineProps ({
76 id: {
@@ -42,29 +41,29 @@ const props = defineProps({
4241 },
4342})
4443
45- const { findEdge } = useVueFlow ()
44+ const { updateEdgeData } = useVueFlow ()
4645
4746const nodesData = useNodesData ([props .target , props .source ])
4847
49- const edgePoint = ref (0 )
48+ const labelRef = ref ()
5049
5150const edgeRef = ref ()
5251
53- const labelPosition = ref ({ x : 0 , y : 0 } )
52+ const targetNodeData = computed (() => nodesData . value [ 0 ]. data )
5453
55- const currentLength = ref (0 )
56-
57- const targetNodeData = toRef (() => nodesData .value [0 ].data )
58-
59- const sourceNodeData = toRef (() => nodesData .value [1 ].data )
54+ const sourceNodeData = computed (() => nodesData .value [1 ].data )
6055
6156const isFinished = toRef (() => sourceNodeData .value .isFinished )
6257
6358const isCancelled = toRef (() => targetNodeData .value .isCancelled )
6459
6560const isAnimating = ref (false )
6661
67- const edgeColor = toRef (() => {
62+ let animation = null
63+
64+ const path = computed (() => getSmoothStepPath (props))
65+
66+ const edgeColor = computed (() => {
6867 if (targetNodeData .value .hasError ) {
6968 return ' #f87171'
7069 }
@@ -84,42 +83,14 @@ const edgeColor = toRef(() => {
8483 return ' #6b7280'
8584})
8685
87- const path = computed (() => getSmoothStepPath (props))
88-
8986watch (isCancelled, (isCancelled ) => {
9087 if (isCancelled) {
91- reset ()
88+ animation ? . cancel ()
9289 }
9390})
9491
9592watch (isAnimating, (isAnimating ) => {
96- const edge = findEdge (props .id )
97-
98- if (edge) {
99- // we set the `isAnimating` flag, so we can wait until the next node gets executed
100- edge .data = {
101- ... edge .data ,
102- isAnimating,
103- }
104- }
105- })
106-
107- watch (edgePoint, (point ) => {
108- const pathEl = edgeRef .value ? .pathEl
109-
110- if (! pathEl || point === 0 || ! isAnimating .value ) {
111- return
112- }
113-
114- const nextLength = pathEl .getTotalLength ()
115-
116- // if length changed, restart animation
117- if (currentLength .value !== nextLength) {
118- runAnimation ()
119- return
120- }
121-
122- labelPosition .value = pathEl .getPointAtLength (point)
93+ updateEdgeData (props .id , { isAnimating })
12394})
12495
12596watch (isFinished, (isFinished ) => {
@@ -128,7 +99,7 @@ watch(isFinished, (isFinished) => {
12899 }
129100})
130101
131- async function runAnimation () {
102+ function runAnimation () {
132103 const pathEl = edgeRef .value ? .pathEl
133104
134105 if (! pathEl) {
@@ -137,35 +108,26 @@ async function runAnimation() {
137108
138109 const totalLength = pathEl .getTotalLength ()
139110
140- // if animation restarted, use last edgePoint value to continue from
141- const from = edgePoint .value || 0
142-
143- // update initial label position
144- labelPosition .value = pathEl .getPointAtLength (from)
145-
146111 isAnimating .value = true
147112
148- // update currentLength value, so we can check if the path length changed during animation
149- if (currentLength .value !== totalLength) {
150- currentLength .value = totalLength
151- }
113+ const keyframes = [{ offsetDistance: ' 0%' }, { offsetDistance: ' 100%' }]
152114
153- await executeTransition (edgePoint, from, totalLength, {
154- transition: TransitionPresets .easeInOutCubic ,
155- duration: Math .max (1500 , totalLength / 2 ),
156- abort : () => ! isAnimating .value ,
115+ // use path length as a possible measure for the animation duration
116+ const pathLengthDuration = totalLength * 10
117+
118+ animation = labelRef .value .animate (keyframes, {
119+ duration: Math .min (Math .max (pathLengthDuration, 1500 ), 3000 ), // clamp duration between 1.5s and 3s
120+ direction: ' normal' ,
121+ easing: ' ease-in-out' ,
122+ iterations: 1 ,
157123 })
158124
159- reset ()
125+ animation .onfinish = handleAnimationEnd
126+ animation .oncancel = handleAnimationEnd
160127}
161128
162- function reset () {
163- nextTick (() => {
164- edgePoint .value = 0
165- currentLength .value = 0
166- labelPosition .value = { x: 0 , y: 0 }
167- isAnimating .value = false
168- })
129+ function handleAnimationEnd () {
130+ isAnimating .value = false
169131}
170132< / script>
171133
@@ -179,10 +141,18 @@ export default {
179141< template>
180142 < BaseEdge : id= " id" ref= " edgeRef" : path= " path[0]" : style= " { stroke: edgeColor }" / >
181143
182- < EdgeLabelRenderer v - if = " isAnimating " >
144+ < EdgeLabelRenderer>
183145 < div
184- : style= " { transform: `translate(-50%, -50%) translate(${labelPosition.x}px,${labelPosition.y}px)` }"
185- class = " nodrag nopan animated-edge-label"
146+ ref= " labelRef"
147+ : style= " {
148+ visibility: isAnimating ? 'visible' : 'hidden',
149+ position: 'absolute',
150+ zIndex: 1,
151+ offsetPath: `path('${path[0]}')`,
152+ offsetRotate: '0deg',
153+ offsetAnchor: 'center',
154+ }"
155+ class = " animated-edge-label"
186156 >
187157 < span class = " truck" >
188158 < span class = " box" > 📦< / span>
@@ -192,12 +162,7 @@ export default {
192162 < / EdgeLabelRenderer>
193163< / template>
194164
195- < style>
196- .animated - edge- label {
197- position: absolute;
198- z- index: 100 ;
199- }
200-
165+ < style scoped>
201166.truck {
202167 position: relative;
203168 display: inline- block;
0 commit comments