Animated Beam
An animated beam of light which travels along a path. Useful for showcasing the "integration" features of a website.
Installation
Copy and paste the following code into your project:
vue
<script setup lang="ts">
import { onMounted, onUnmounted, ref, useId, watch } from 'vue'
const props = withDefaults(defineProps<{
containerRef: any
fromRef: any
toRef: any
className?: string
curvature?: number
reverse?: boolean
pathColor?: string
pathWidth?: number
pathOpacity?: number
gradientStartColor?: string
gradientStopColor?: string
delay?: number
duration?: number
startXOffset?: number
startYOffset?: number
endXOffset?: number
endYOffset?: number
width?: number
height?: number
}>(), {
curvature: 0,
reverse: false,
duration: Math.random() * 3 + 4,
delay: 3,
pathColor: 'gray',
pathWidth: 2,
pathOpacity: 0.2,
gradientStartColor: '#ffaa40',
gradientStopColor: '#9c40ff',
startXOffset: 0,
startYOffset: 0,
endXOffset: 0,
endYOffset: 0,
})
const id = `pattern-${useId()}`
const initial = {
x1: '10%',
x2: '0%',
y1: '0%',
y2: '0%',
}
const svgDimensions = ref({ width: 0, height: 0 })
const pathD = ref('')
function updatePath() {
if (
props.containerRef
&& props.fromRef?.circleRef
&& props.toRef?.circleRef
) {
const containerRect = props.containerRef?.getBoundingClientRect()
const rectA = props.fromRef?.circleRef?.getBoundingClientRect()
const rectB = props.toRef?.circleRef?.getBoundingClientRect()
const svgWidth = containerRect?.width
const svgHeight = containerRect?.height
svgDimensions.value.width = svgWidth
svgDimensions.value.height = svgHeight
const startX = rectA.left - containerRect.left + rectA.width / 2 + props.startXOffset
const startY = rectA.top - containerRect.top + rectA.height / 2 + props.startYOffset
const endX = rectB.left - containerRect.left + rectB.width / 2 + props.endXOffset
const endY = rectB.top - containerRect.top + rectB.height / 2 + props.endYOffset
const controlY = startY - props.curvature
const d = `M ${startX},${startY} Q ${(startX + endX) / 2
},${controlY} ${endX},${endY}`
pathD.value = d
}
}
const controller = new AbortController()
onMounted(() => {
window.addEventListener('resize', updatePath, {
signal: controller.signal,
})
})
onUnmounted(() => {
controller.abort()
})
watch(props, (_) => {
updatePath()
}, { deep: true })
</script>
<template>
<svg
:width="svgDimensions?.width" :height="svgDimensions?.height" xmlns="http://www.w3.org/2000/svg" class="pointer-events-none absolute left-0 top-0 transform-gpu stroke-2" :class="[
props.className,
]" :viewBox="`0 0 ${svgDimensions?.width} ${svgDimensions?.height}`"
>
<path
:d="pathD" :stroke="pathColor" fill="none" :stroke-width="pathWidth" :stroke-opacity="pathOpacity"
stroke-linecap="round"
/>
<path :d="pathD" :stroke="`url(#${id}`" fill="none" stroke-linecap="round" />
<defs>
<linearGradient
:id="id" v-motion :initial="{
opacity: 0,
x: props.reverse ? ['100%', '0%'] : ['0%', '100%'],
}" :enter="{
opacity: 1,
x: props.reverse ? ['100%', '0%'] : ['0%', '100%'],
transition: {
duration: 1600,
type: 'keyframes',
easings: [0.16, 1, 0.3, 1],
repeat: Infinity,
},
}" gradientUnits="userSpaceOnUse" :x1="initial.x1" :x2="initial.x2" :y1="initial.y1"
:y2="initial.y2"
>
<stop :stop-color="props.gradientStartColor" stop-opacity="0" />
<stop :stop-color="props.gradientStartColor" />
<stop offset="32.5%" :stop-color="props.gradientStopColor" />
<stop offset="100%" :stop-color="props.gradientStopColor" stop-opacity="0" />
</linearGradient>
</defs>
</svg>
</template>
vue
<script setup lang='ts'>
import { ref } from 'vue'
const circleRef = ref()
defineExpose({
circleRef,
})
</script>
<template>
<div
ref="circleRef"
class="z-10 flex h-12 w-12 items-center justify-center rounded-full border-2 bg-white p-3 shadow-[0_0_20px_-12px_rgba(0,0,0,0.8)]"
>
<slot />
</div>
</template>
Examples
Animated Beam Uni-Directional
Animated Beam Bi-Directional
Animated Beam Multiple Inputs
Animated Beam Multiple Outputs
Props
Animated Beam
Prop | Type | Description | Default |
---|---|---|---|
class | string | The class for the component. | - |
containerRef | ref | The container ref. | - |
fromRef | ref | The ref of the element from which the beam should start. | - |
toRef | ref | The ref of the element to which the beam should end. | - |
curvature | number | The curvature of the beam. | 0 |
reverse | boolean | Whether the beam should be reversed. | false |
duration | number | The duration of the beam. | 5 |
delay | number | The delay of the beam. | 0 |
pathColor | string | The color of the beam. | "gray" |
pathWidth | number | The width of the beam. | 2 |
pathOpacity | number | The opacity of the beam. | 0.2 |
gradientStartColor | string | The start color of the gradient. | "#ffaa40" |
gradientStopColor | string | The stop color of the gradient. | "#9c40ff" |
startXOffset | number | The start x offset of the beam. | 0 |
startYOffset | number | The start y offset of the beam. | 0 |
endXOffset | number | The end x offset of the beam. | 0 |
endYOffset | number | The end y offset of the beam. | 0 |