|
|
@@ -0,0 +1,107 @@
|
|
|
+<script lang="ts" setup>
|
|
|
+import {computed} from 'vue'
|
|
|
+
|
|
|
+interface WaveConfig {
|
|
|
+ color: string
|
|
|
+ amplitude: number
|
|
|
+ period: number
|
|
|
+ speed: number
|
|
|
+ opacity: number
|
|
|
+ strokeWidth: number
|
|
|
+ phaseOffset: number
|
|
|
+}
|
|
|
+
|
|
|
+const waves: WaveConfig[] = [
|
|
|
+ {color: '#722ed1', amplitude: 18, period: 200, speed: 4, opacity: 0.7, strokeWidth: 1.2, phaseOffset: 0},
|
|
|
+ {color: '#9254de', amplitude: 14, period: 160, speed: 5.5, opacity: 0.6, strokeWidth: 1, phaseOffset: 40},
|
|
|
+ {color: '#b37feb', amplitude: 10, period: 130, speed: 7, opacity: 0.45, strokeWidth: 0.8, phaseOffset: 90},
|
|
|
+ {color: '#1890ff', amplitude: 20, period: 240, speed: 3.5, opacity: 0.65, strokeWidth: 1.2, phaseOffset: 80},
|
|
|
+ {color: '#40a9ff', amplitude: 12, period: 170, speed: 6, opacity: 0.5, strokeWidth: 0.8, phaseOffset: 150},
|
|
|
+ {color: '#13c2c2', amplitude: 12, period: 180, speed: 6, opacity: 0.55, strokeWidth: 1, phaseOffset: 120},
|
|
|
+ {color: '#36cfc9', amplitude: 8, period: 140, speed: 7.5, opacity: 0.4, strokeWidth: 0.8, phaseOffset: 200},
|
|
|
+ {color: '#eb2f96', amplitude: 16, period: 220, speed: 4.5, opacity: 0.5, strokeWidth: 1, phaseOffset: 60},
|
|
|
+ {color: '#f759ab', amplitude: 9, period: 150, speed: 6.5, opacity: 0.4, strokeWidth: 0.8, phaseOffset: 170},
|
|
|
+ {color: '#597ef7', amplitude: 15, period: 190, speed: 5, opacity: 0.5, strokeWidth: 1, phaseOffset: 110},
|
|
|
+]
|
|
|
+
|
|
|
+const viewBoxWidth = 600
|
|
|
+const viewBoxHeight = 100
|
|
|
+const centerY = viewBoxHeight / 2
|
|
|
+
|
|
|
+function generateWavePath(wave: WaveConfig): string {
|
|
|
+ const totalWidth = viewBoxWidth * 2
|
|
|
+ const step = 4
|
|
|
+ const points: string[] = []
|
|
|
+
|
|
|
+ for (let x = 0; x <= totalWidth; x += step) {
|
|
|
+ const y = centerY + wave.amplitude * Math.sin((2 * Math.PI * (x + wave.phaseOffset)) / wave.period)
|
|
|
+ points.push(`${x},${y.toFixed(2)}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ return `M ${points[0]} ` + points.slice(1).map(p => `L ${p}`).join(' ')
|
|
|
+}
|
|
|
+
|
|
|
+const wavePaths = computed(() =>
|
|
|
+ waves.map(w => ({
|
|
|
+ ...w,
|
|
|
+ path: generateWavePath(w),
|
|
|
+ }))
|
|
|
+)
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="reasoning-card">
|
|
|
+ <svg
|
|
|
+ :viewBox="`0 0 ${viewBoxWidth} ${viewBoxHeight}`"
|
|
|
+ class="wave-svg"
|
|
|
+ preserveAspectRatio="none"
|
|
|
+ >
|
|
|
+ <g v-for="(wave, i) in wavePaths" :key="i">
|
|
|
+ <path
|
|
|
+ :d="wave.path"
|
|
|
+ :stroke="wave.color"
|
|
|
+ :stroke-opacity="wave.opacity"
|
|
|
+ :stroke-width="wave.strokeWidth"
|
|
|
+ :style="{ animationDuration: `${wave.speed}s` }"
|
|
|
+ class="wave-path"
|
|
|
+ fill="none"
|
|
|
+ stroke-linecap="round"
|
|
|
+ />
|
|
|
+ </g>
|
|
|
+ </svg>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.reasoning-card {
|
|
|
+ position: relative;
|
|
|
+ aspect-ratio: 3 / 1;
|
|
|
+ background: #1a1a2e;
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ border: 1px solid rgba(114, 46, 209, 0.3);
|
|
|
+ box-shadow: 0 0 12px rgba(114, 46, 209, 0.15),
|
|
|
+ inset 0 0 20px rgba(0, 0, 0, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.wave-svg {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 200%;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.wave-path {
|
|
|
+ animation: wave-scroll linear infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes wave-scroll {
|
|
|
+ from {
|
|
|
+ transform: translateX(0);
|
|
|
+ }
|
|
|
+ to {
|
|
|
+ transform: translateX(-50%);
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|