From 6f7b18429b6af1037d1cffdee48e77506c9f77ec Mon Sep 17 00:00:00 2001 From: David Haz Date: Mon, 21 Jul 2025 08:55:13 +0300 Subject: [PATCH] Add copy button styles and fix CurvedLoop for Safari --- .../TextAnimations/CurvedLoop/CurvedLoop.vue | 104 +++++++----------- src/css/base.css | 8 +- 2 files changed, 43 insertions(+), 69 deletions(-) diff --git a/src/content/TextAnimations/CurvedLoop/CurvedLoop.vue b/src/content/TextAnimations/CurvedLoop/CurvedLoop.vue index 0a05995..b75ed13 100644 --- a/src/content/TextAnimations/CurvedLoop/CurvedLoop.vue +++ b/src/content/TextAnimations/CurvedLoop/CurvedLoop.vue @@ -25,10 +25,10 @@ const text = computed(() => { }); const measureRef = ref(null); -const tspansRef = ref([]); +const textPathRef = ref(null); const pathRef = ref(null); -const pathLength = ref(0); const spacing = ref(0); +const offset = ref(0); const uid = Math.random().toString(36).substr(2, 9); const pathId = `curve-${uid}`; @@ -41,34 +41,38 @@ const velRef = ref(0); let animationFrame: number | null = null; +const textLength = computed(() => spacing.value); +const totalText = computed(() => { + return textLength.value + ? Array(Math.ceil(1800 / textLength.value) + 2) + .fill(text.value) + .join('') + : text.value; +}); +const ready = computed(() => spacing.value > 0); + const updateSpacing = () => { if (measureRef.value) { spacing.value = measureRef.value.getComputedTextLength(); } }; -const updatePathLength = () => { - if (pathRef.value) { - pathLength.value = pathRef.value.getTotalLength(); - } -}; - const animate = () => { - if (!spacing.value) return; + if (!spacing.value || !ready.value) return; const step = () => { - tspansRef.value.forEach(t => { - if (!t) return; - let x = parseFloat(t.getAttribute('x') || '0'); - if (!dragRef.value) { - const delta = dirRef.value === 'right' ? Math.abs(props.speed) : -Math.abs(props.speed); - x += delta; - } - const maxX = (tspansRef.value.length - 1) * spacing.value; - if (x < -spacing.value) x = maxX; - if (x > maxX) x = -spacing.value; - t.setAttribute('x', x.toString()); - }); + if (!dragRef.value && textPathRef.value) { + const delta = dirRef.value === 'right' ? props.speed : -props.speed; + const currentOffset = parseFloat(textPathRef.value.getAttribute('startOffset') || '0'); + let newOffset = currentOffset + delta; + + const wrapPoint = spacing.value; + if (newOffset <= -wrapPoint) newOffset += wrapPoint; + if (newOffset >= wrapPoint) newOffset -= wrapPoint; + + textPathRef.value.setAttribute('startOffset', newOffset + 'px'); + offset.value = newOffset; + } animationFrame = requestAnimationFrame(step); }; step(); @@ -81,12 +85,6 @@ const stopAnimation = () => { } }; -const repeats = computed(() => { - return pathLength.value && spacing.value ? Math.ceil(pathLength.value / spacing.value) + 2 : 0; -}); - -const ready = computed(() => pathLength.value > 0 && spacing.value > 0); - const onPointerDown = (e: PointerEvent) => { if (!props.interactive) return; dragRef.value = true; @@ -96,19 +94,20 @@ const onPointerDown = (e: PointerEvent) => { }; const onPointerMove = (e: PointerEvent) => { - if (!props.interactive || !dragRef.value) return; + if (!props.interactive || !dragRef.value || !textPathRef.value) return; const dx = e.clientX - lastXRef.value; lastXRef.value = e.clientX; velRef.value = dx; - tspansRef.value.forEach(t => { - if (!t) return; - let x = parseFloat(t.getAttribute('x') || '0'); - x += dx; - const maxX = (tspansRef.value.length - 1) * spacing.value; - if (x < -spacing.value) x = maxX; - if (x > maxX) x = -spacing.value; - t.setAttribute('x', x.toString()); - }); + + const currentOffset = parseFloat(textPathRef.value.getAttribute('startOffset') || '0'); + let newOffset = currentOffset + dx; + + const wrapPoint = spacing.value; + if (newOffset <= -wrapPoint) newOffset += wrapPoint; + if (newOffset >= wrapPoint) newOffset -= wrapPoint; + + textPathRef.value.setAttribute('startOffset', newOffset + 'px'); + offset.value = newOffset; }; const endDrag = () => { @@ -124,7 +123,6 @@ const cursorStyle = computed(() => { onMounted(() => { nextTick(() => { updateSpacing(); - updatePathLength(); animate(); }); }); @@ -139,25 +137,12 @@ watch([text, () => props.className], () => { }); }); -watch( - () => props.curveAmount, - () => { - nextTick(() => { - updatePathLength(); - }); - } -); - watch([spacing, () => props.speed], () => { stopAnimation(); if (spacing.value) { animate(); } }); - -watch(repeats, () => { - tspansRef.value = []; -});