219 lines
9.7 KiB
Vue

<template>
<div style="height: fit-content; width: fit-content; position: relative;" ref="container">
<svg class="VPImage image-src" viewBox="0 0 612 612" fill="none" xmlns="http://www.w3.org/2000/svg" ref="svgElement">
<defs>
<linearGradient id="gradient_1" gradientUnits="userSpaceOnUse" x1="300" y1="0" x2="300" y2="600">
<stop offset="0" stop-color="#A1A7F6" />
<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.2" />
</linearGradient>
<linearGradient id="gradient_2" gradientUnits="userSpaceOnUse" x1="110.5" y1="0" x2="110.5" y2="221">
<stop offset="0.468" stop-color="#BFBAF6" />
<stop offset="1" stop-color="#FFFFFF" />
</linearGradient>
<filter color-interpolation-filters="sRGB" x="-219" y="-219" width="221" height="221" id="filter_3">
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
<feOffset dx="0" dy="4" />
<feGaussianBlur stdDeviation="2" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
</filter>
<linearGradient id="gradient_4" gradientUnits="userSpaceOnUse" x1="55.5" y1="0" x2="55.5" y2="111">
<stop offset="0" stop-color="#FFFFFF" />
<stop offset="1" stop-color="#A8A7F3" />
</linearGradient>
<filter color-interpolation-filters="sRGB" x="-109" y="-109" width="111" height="111" id="filter_5">
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
<feOffset dx="0" dy="4" />
<feGaussianBlur stdDeviation="2" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
</filter>
<linearGradient id="gradient_6" gradientUnits="userSpaceOnUse" x1="174" y1="0" x2="174" y2="348">
<stop offset="0.182" stop-color="#A594F6" />
<stop offset="1" stop-color="#F4E5FF" />
</linearGradient>
<filter color-interpolation-filters="sRGB" x="-346" y="-346" width="348" height="348" id="filter_7">
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
<feOffset dx="0" dy="4" />
<feGaussianBlur stdDeviation="2" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
</filter>
<linearGradient id="gradient_8" gradientUnits="userSpaceOnUse" x1="57" y1="0" x2="57" y2="114">
<stop offset="0" stop-color="#FFFFFF" />
<stop offset="0.614" stop-color="#C7BAF8" />
</linearGradient>
<filter color-interpolation-filters="sRGB" x="-112" y="-112" width="114" height="114" id="filter_9">
<feFlood flood-opacity="0" result="BackgroundImageFix_1" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" in="SourceAlpha" />
<feOffset dx="0" dy="4" />
<feGaussianBlur stdDeviation="2" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.251 0" />
<feBlend mode="normal" in2="BackgroundImageFix_1" result="Shadow_2" />
<feBlend mode="normal" in="SourceGraphic" in2="Shadow_2" result="Shape_3" />
</filter>
</defs>
<g transform="translate(6 2)">
<g>
<path
d="M300 0C465.708 0 600 134.292 600 300C600 300 600 300 600 300C600 465.708 465.708 600 300 600C300 600 300 600 300 600C134.292 600 0 465.708 0 300C0 300 0 300 0 300C0 134.292 134.292 0 300 0Z"
fill="#5A00FF" fill-rule="evenodd" />
<path
d="M300 0C465.708 0 600 134.292 600 300C600 300 600 300 600 300C600 465.708 465.708 600 300 600C300 600 300 600 300 600C134.292 600 0 465.708 0 300C0 300 0 300 0 300C0 134.292 134.292 0 300 0Z"
fill="url(#gradient_1)" fill-rule="evenodd" />
</g>
<path
d="M0 110.5C0 49.4725 49.4725 0 110.5 0C171.527 0 221 49.4725 221 110.5C221 171.527 171.527 221 110.5 221C49.4725 221 0 171.527 0 110.5Z"
fill="url(#gradient_2)" fill-rule="evenodd" filter="url(#filter_3)"
transform="translate(293 324)" />
<path
d="M0 55.5C0 24.8482 24.8482 0 55.5 0C86.1518 0 111 24.8482 111 55.5C111 86.1518 86.1518 111 55.5 111C24.8482 111 0 86.1518 0 55.5Z"
fill="url(#gradient_4)" fill-rule="evenodd" filter="url(#filter_5)" transform="translate(48 269)" />
<path
d="M0 174C0 77.9024 77.9024 0 174 0C270.098 0 348 77.9024 348 174C348 270.098 270.098 348 174 348C77.9024 348 0 270.098 0 174Z"
fill="url(#gradient_6)" fill-rule="evenodd" filter="url(#filter_7)" transform="translate(188 56)" />
<path
d="M0 57C0 25.5198 25.5198 0 57 0C88.4802 0 114 25.5198 114 57C114 88.4802 88.4802 114 57 114C25.5198 114 0 88.4802 0 57Z"
fill="url(#gradient_8)" fill-rule="evenodd" filter="url(#filter_9)"
transform="translate(388 129)" />
</g>
</svg>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { gsap } from 'gsap'
const container = ref(null)
const svgElement = ref(null)
onMounted(() => {
const paths = svgElement.value.querySelectorAll('path')
// 设置初始状态
gsap.set(paths, {
strokeDasharray: (i, target) => {
const length = target.getTotalLength()
target.style.strokeDasharray = length
return length
},
strokeDashoffset: (i, target) => target.getTotalLength(),
stroke: 'var(--vp-c-brand-2)',
strokeWidth: 3,
fillOpacity: 0
})
// 创建主时间线
const master = gsap.timeline()
// 描边动画时间线
const drawTimeline = gsap.timeline({
defaults: { duration: 1.5, ease: "power2.inOut" }
})
// 为所有路径添加描边动画
paths.forEach(path => {
drawTimeline.to(path, {
strokeDashoffset: 0,
}, "<+=0.3")
})
// 反向描边消失动画时间线
const reverseTimeline = gsap.timeline({
defaults: { duration: 1.5, ease: "power2.inOut" }
})
// 为所有路径添加反向描边动画
paths.forEach(path => {
reverseTimeline.to(path, {
fillOpacity: 1,
strokeDashoffset: (i, target) => target.getTotalLength()
}, "<")
})
// 微扰动画时间线
const wiggleTimeline = gsap.timeline({
repeat: -1,
yoyo: true,
defaults: {
duration: 2,
ease: "sine.inOut"
}
})
// 为每个圆形元素创建不同的微扰动画
const wiggleElements = [
{
el: svgElement.value.querySelector('[transform="translate(293 324)"]'),
x: "+=12", y: "+=5", rotation: "+=2"
},
{
el: svgElement.value.querySelector('[transform="translate(48 269)"]'),
x: "-=5", y: "-=10", rotation: "-=1"
},
{
el: svgElement.value.querySelector('[transform="translate(188 56)"]'),
x: "-=4", y: "+=5", rotation: "+=3"
},
{
el: svgElement.value.querySelector('[transform="translate(388 129)"]'),
x: "+=9", y: "+=4", rotation: "-=2"
}
]
// 为每个元素添加独特的微扰动画
wiggleElements.forEach(item => {
wiggleTimeline.to(item.el, {
x: item.x,
y: item.y,
rotation: item.rotation,
scale: item.scale,
transformOrigin: "center center"
}, "<")
})
// 控制动画顺序
master.add(drawTimeline)
.add(reverseTimeline, "+=0.5")
.add(wiggleTimeline) // 所有动画完成后开始微扰动画
})
</script>
<style scoped>
.VPHero .VPImage {
filter: drop-shadow(-2px 4px 6px rgba(0, 0, 0, .2));
}
.image-src {
position: absolute;
top: 50%;
left: 50%;
width: 320px;
height: 320px;
object-fit: contain;
transform: translate(-50%, -50%);
}
.circle-animation {
position: absolute;
top: 50%;
left: 50%;
width: 320px;
height: 320px;
transform: translate(-50%, -50%);
color: var(--vp-c-brand-1);
}
.circle-animation circle {
stroke-width: 3;
stroke-linecap: round;
}
</style>