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:
Braks
2025-01-13 00:22:13 +01:00
committed by GitHub
parent 25f9dd1dd9
commit e94611a3b0
25 changed files with 1059 additions and 684 deletions
+83 -54
View File
@@ -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>