1
1
<script setup>
2
- import { computed , nextTick , ref , toRef , watch } from ' vue'
2
+ import { computed , ref , toRef , watch } from ' vue'
3
3
import { BaseEdge , EdgeLabelRenderer , Position , getSmoothStepPath , useNodesData , useVueFlow } from ' @vue-flow/core'
4
- import { TransitionPresets , executeTransition } from ' @vueuse/core'
5
4
6
5
const props = defineProps ({
7
6
id: {
@@ -42,29 +41,29 @@ const props = defineProps({
42
41
},
43
42
})
44
43
45
- const { findEdge } = useVueFlow ()
44
+ const { updateEdgeData } = useVueFlow ()
46
45
47
46
const nodesData = useNodesData ([props .target , props .source ])
48
47
49
- const edgePoint = ref (0 )
48
+ const labelRef = ref ()
50
49
51
50
const edgeRef = ref ()
52
51
53
- const labelPosition = ref ({ x : 0 , y : 0 } )
52
+ const targetNodeData = computed (() => nodesData . value [ 0 ]. data )
54
53
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 )
60
55
61
56
const isFinished = toRef (() => sourceNodeData .value .isFinished )
62
57
63
58
const isCancelled = toRef (() => targetNodeData .value .isCancelled )
64
59
65
60
const isAnimating = ref (false )
66
61
67
- const edgeColor = toRef (() => {
62
+ let animation = null
63
+
64
+ const path = computed (() => getSmoothStepPath (props))
65
+
66
+ const edgeColor = computed (() => {
68
67
if (targetNodeData .value .hasError ) {
69
68
return ' #f87171'
70
69
}
@@ -84,42 +83,14 @@ const edgeColor = toRef(() => {
84
83
return ' #6b7280'
85
84
})
86
85
87
- const path = computed (() => getSmoothStepPath (props))
88
-
89
86
watch (isCancelled, (isCancelled ) => {
90
87
if (isCancelled) {
91
- reset ()
88
+ animation ? . cancel ()
92
89
}
93
90
})
94
91
95
92
watch (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 })
123
94
})
124
95
125
96
watch (isFinished, (isFinished ) => {
@@ -128,7 +99,7 @@ watch(isFinished, (isFinished) => {
128
99
}
129
100
})
130
101
131
- async function runAnimation () {
102
+ function runAnimation () {
132
103
const pathEl = edgeRef .value ? .pathEl
133
104
134
105
if (! pathEl) {
@@ -137,35 +108,26 @@ async function runAnimation() {
137
108
138
109
const totalLength = pathEl .getTotalLength ()
139
110
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
-
146
111
isAnimating .value = true
147
112
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%' }]
152
114
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 ,
157
123
})
158
124
159
- reset ()
125
+ animation .onfinish = handleAnimationEnd
126
+ animation .oncancel = handleAnimationEnd
160
127
}
161
128
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
169
131
}
170
132
< / script>
171
133
@@ -179,10 +141,18 @@ export default {
179
141
< template>
180
142
< BaseEdge : id= " id" ref= " edgeRef" : path= " path[0]" : style= " { stroke: edgeColor }" / >
181
143
182
- < EdgeLabelRenderer v - if = " isAnimating " >
144
+ < EdgeLabelRenderer>
183
145
< 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"
186
156
>
187
157
< span class = " truck" >
188
158
< span class = " box" > 📦< / span>
@@ -192,12 +162,7 @@ export default {
192
162
< / EdgeLabelRenderer>
193
163
< / template>
194
164
195
- < style>
196
- .animated - edge- label {
197
- position: absolute;
198
- z- index: 100 ;
199
- }
200
-
165
+ < style scoped>
201
166
.truck {
202
167
position: relative;
203
168
display: inline- block;
0 commit comments