chore(docs): simplify animated layout example (#1747)
* chore(examples): update ts example of layouting Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> * chore(docs): cleanup animated layout example Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com> --------- Signed-off-by: braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { computed, ref, toRef, watch } from 'vue'
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { BaseEdge, EdgeLabelRenderer, Position, getSmoothStepPath, useNodesData, useVueFlow } from '@vue-flow/core'
|
||||
import { ProcessStatus } from './useRunProcess'
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
@@ -39,70 +40,85 @@ const props = defineProps({
|
||||
type: String,
|
||||
default: Position.Left,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
|
||||
const { updateEdgeData } = useVueFlow()
|
||||
|
||||
const nodesData = useNodesData([props.target, props.source])
|
||||
/**
|
||||
* We call `useNodesData` to get the data of the source and target nodes, which
|
||||
* contain the information about the status of each nodes' process.
|
||||
*/
|
||||
const nodesData = useNodesData(() => [props.target, props.source])
|
||||
|
||||
const labelRef = ref()
|
||||
|
||||
const edgeRef = ref()
|
||||
|
||||
/**
|
||||
* We extract the source and target node data from the nodes data.
|
||||
* We only need the first element of the array since we are only connecting two nodes.
|
||||
*/
|
||||
const targetNodeData = computed(() => nodesData.value[0].data)
|
||||
|
||||
const sourceNodeData = computed(() => nodesData.value[1].data)
|
||||
|
||||
const isFinished = toRef(() => sourceNodeData.value.isFinished)
|
||||
|
||||
const isCancelled = toRef(() => targetNodeData.value.isCancelled)
|
||||
|
||||
const isAnimating = ref(false)
|
||||
const isAnimating = computed({
|
||||
get: () => props.data.isAnimating || false,
|
||||
set: (value) => {
|
||||
updateEdgeData(props.id, { isAnimating: value })
|
||||
},
|
||||
})
|
||||
|
||||
let animation = null
|
||||
|
||||
const path = computed(() => getSmoothStepPath(props))
|
||||
|
||||
const edgeColor = computed(() => {
|
||||
if (targetNodeData.value.hasError) {
|
||||
return '#f87171'
|
||||
}
|
||||
|
||||
if (targetNodeData.value.isFinished) {
|
||||
return '#42B983'
|
||||
}
|
||||
|
||||
if (targetNodeData.value.isCancelled || targetNodeData.value.isSkipped) {
|
||||
return '#fbbf24'
|
||||
}
|
||||
|
||||
if (targetNodeData.value.isRunning || isAnimating.value) {
|
||||
return '#2563eb'
|
||||
}
|
||||
|
||||
return '#6b7280'
|
||||
})
|
||||
|
||||
watch(isCancelled, (isCancelled) => {
|
||||
if (isCancelled) {
|
||||
animation?.cancel()
|
||||
switch (targetNodeData.value.status) {
|
||||
case ProcessStatus.ERROR:
|
||||
return '#f87171'
|
||||
case ProcessStatus.FINISHED:
|
||||
return '#42B983'
|
||||
case ProcessStatus.CANCELLED:
|
||||
case ProcessStatus.SKIPPED:
|
||||
return '#fbbf24'
|
||||
case ProcessStatus.RUNNING:
|
||||
return '#2563eb'
|
||||
default:
|
||||
return '#6b7280'
|
||||
}
|
||||
})
|
||||
|
||||
watch(isAnimating, (isAnimating) => {
|
||||
updateEdgeData(props.id, { isAnimating })
|
||||
})
|
||||
// Cancel the animation if the target nodes' process was cancelled
|
||||
watch(
|
||||
() => targetNodeData.value.status === ProcessStatus.CANCELLED,
|
||||
(isCancelled) => {
|
||||
if (isCancelled) {
|
||||
animation?.cancel()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
watch(isFinished, (isFinished) => {
|
||||
if (isFinished) {
|
||||
runAnimation()
|
||||
}
|
||||
})
|
||||
// Run the animation when the source nodes' process is finished
|
||||
watch(
|
||||
() => sourceNodeData.value.status === ProcessStatus.FINISHED,
|
||||
(isFinished) => {
|
||||
if (isFinished) {
|
||||
runAnimation()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
function runAnimation() {
|
||||
const pathEl = edgeRef.value?.pathEl
|
||||
const labelEl = labelRef.value
|
||||
|
||||
if (!pathEl) {
|
||||
if (!pathEl || !labelEl) {
|
||||
console.warn('Path or label element not found')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -110,24 +126,38 @@ function runAnimation() {
|
||||
|
||||
isAnimating.value = true
|
||||
|
||||
const keyframes = [{ offsetDistance: '0%' }, { offsetDistance: '100%' }]
|
||||
// We need to wait for the next tick to ensure that the label element is rendered
|
||||
nextTick(() => {
|
||||
const keyframes = [{ offsetDistance: '0%' }, { offsetDistance: '100%' }]
|
||||
|
||||
// use path length as a possible measure for the animation duration
|
||||
const pathLengthDuration = totalLength * 10
|
||||
// use path length as a possible measure for the animation duration
|
||||
const pathLengthDuration = totalLength * 10
|
||||
|
||||
animation = labelRef.value.animate(keyframes, {
|
||||
duration: Math.min(Math.max(pathLengthDuration, 1500), 3000), // clamp duration between 1.5s and 3s
|
||||
direction: 'normal',
|
||||
easing: 'ease-in-out',
|
||||
iterations: 1,
|
||||
/**
|
||||
* We animate the label element along the path of the edge using the `offsetDistance` property and
|
||||
* the Web Animations API.
|
||||
*
|
||||
* The `animate` method returns an `Animation` object that we can use to listen to events like `finish` or `cancel`.
|
||||
*
|
||||
* The animation duration is calculated based on the total length of the path and clamped between 1.5s and 3s.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
|
||||
*/
|
||||
const labelAnimation = labelEl.animate(keyframes, {
|
||||
duration: Math.min(Math.max(pathLengthDuration, 1500), 3000), // clamp duration between 1.5s and 3s
|
||||
direction: 'normal',
|
||||
easing: 'ease-in-out',
|
||||
iterations: 1,
|
||||
})
|
||||
|
||||
const handleAnimationEnd = () => {
|
||||
isAnimating.value = false
|
||||
}
|
||||
|
||||
labelAnimation.onfinish = handleAnimationEnd
|
||||
labelAnimation.oncancel = handleAnimationEnd
|
||||
animation = labelAnimation
|
||||
})
|
||||
|
||||
animation.onfinish = handleAnimationEnd
|
||||
animation.oncancel = handleAnimationEnd
|
||||
}
|
||||
|
||||
function handleAnimationEnd() {
|
||||
isAnimating.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -152,7 +182,6 @@ export default {
|
||||
offsetRotate: '0deg',
|
||||
offsetAnchor: 'center',
|
||||
}"
|
||||
class="animated-edge-label"
|
||||
>
|
||||
<span class="truck">
|
||||
<span class="box">📦</span>
|
||||
|
||||
Reference in New Issue
Block a user