| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- <template>
- <canvas ref="waveCanvas" class="wave-canvas"></canvas>
- </template>
- <script>
- export default {
- name: "WaveCanvas",
- data() {
- return {
- canvas: null,
- ctx: null,
- animationId: null,
- time: 0,
- waves: [],
- particles: []
- };
- },
- mounted() {
- this.initCanvas();
- this.initWaves();
- this.initParticles();
- this.animate();
- window.addEventListener("resize", this.handleResize);
- },
- beforeDestroy() {
- if (this.animationId) {
- cancelAnimationFrame(this.animationId);
- }
- window.removeEventListener("resize", this.handleResize);
- },
- methods: {
- initCanvas() {
- this.canvas = this.$refs.waveCanvas;
- this.ctx = this.canvas.getContext("2d");
- this.setCanvasSize();
- },
- setCanvasSize() {
- const parent = this.canvas.parentElement;
- this.canvas.width = parent.clientWidth;
- this.canvas.height = parent.clientHeight;
- },
- handleResize() {
- this.setCanvasSize();
- this.initParticles();
- },
- initWaves() {
- // 创建多层波浪参数
- this.waves = [
- {
- amplitude: 30,
- frequency: 0.01,
- speed: 0.02,
- yOffset: 0.7,
- color: "rgba(64, 156, 255, 0.4)",
- lineWidth: 2
- },
- {
- amplitude: 25,
- frequency: 0.015,
- speed: 0.025,
- yOffset: 0.75,
- color: "rgba(100, 181, 246, 0.3)",
- lineWidth: 1.5
- },
- {
- amplitude: 20,
- frequency: 0.008,
- speed: 0.015,
- yOffset: 0.8,
- color: "rgba(144, 202, 249, 0.25)",
- lineWidth: 1
- },
- {
- amplitude: 35,
- frequency: 0.012,
- speed: 0.018,
- yOffset: 0.65,
- color: "rgba(64, 156, 255, 0.2)",
- lineWidth: 1.5
- }
- ];
- },
- initParticles() {
- this.particles = [];
- const width = this.canvas.width;
- const height = this.canvas.height;
-
- // 创建点阵粒子
- const cols = Math.floor(width / 15);
- const rows = Math.floor(height / 15);
-
- for (let i = 0; i < cols; i++) {
- for (let j = 0; j < rows; j++) {
- if (Math.random() > 0.6) {
- this.particles.push({
- x: i * 15 + Math.random() * 10,
- y: j * 15 + Math.random() * 10,
- baseY: j * 15,
- size: Math.random() * 1.5 + 0.5,
- opacity: Math.random() * 0.5 + 0.2,
- speed: Math.random() * 0.5 + 0.2,
- phase: Math.random() * Math.PI * 2
- });
- }
- }
- }
- },
- drawWave(wave, index) {
- const ctx = this.ctx;
- const width = this.canvas.width;
- const height = this.canvas.height;
- const baseY = height * wave.yOffset;
-
- ctx.beginPath();
- ctx.strokeStyle = wave.color;
- ctx.lineWidth = wave.lineWidth;
-
- // 绘制波浪线
- for (let x = 0; x <= width; x += 2) {
- const y = baseY +
- Math.sin(x * wave.frequency + this.time * wave.speed + index) * wave.amplitude +
- Math.sin(x * wave.frequency * 2 + this.time * wave.speed * 1.5) * (wave.amplitude * 0.5);
-
- if (x === 0) {
- ctx.moveTo(x, y);
- } else {
- ctx.lineTo(x, y);
- }
- }
-
- ctx.stroke();
-
- // 填充波浪下方渐变
- ctx.lineTo(width, height);
- ctx.lineTo(0, height);
- ctx.closePath();
-
- const gradient = ctx.createLinearGradient(0, baseY - wave.amplitude, 0, height);
- gradient.addColorStop(0, wave.color.replace(/[\d.]+\)$/, "0.1)"));
- gradient.addColorStop(0.5, wave.color.replace(/[\d.]+\)$/, "0.05)"));
- gradient.addColorStop(1, "transparent");
-
- ctx.fillStyle = gradient;
- ctx.fill();
- },
- drawParticles() {
- const ctx = this.ctx;
- const width = this.canvas.width;
- const height = this.canvas.height;
-
- this.particles.forEach(particle => {
- // 粒子随波浪波动
- const waveY = Math.sin(particle.x * 0.01 + this.time * 0.02) * 20 +
- Math.sin(particle.x * 0.02 + this.time * 0.03) * 10;
-
- const y = particle.baseY + waveY * particle.speed;
-
- // 只在底部区域显示粒子
- if (y > height * 0.5) {
- const opacity = particle.opacity * (1 - (y - height * 0.5) / (height * 0.5));
-
- ctx.beginPath();
- ctx.arc(particle.x, y, particle.size, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(100, 200, 255, ${Math.max(0, opacity)})`;
- ctx.fill();
-
- // 添加发光效果
- if (opacity > 0.3) {
- ctx.beginPath();
- ctx.arc(particle.x, y, particle.size * 3, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(100, 200, 255, ${opacity * 0.2})`;
- ctx.fill();
- }
- }
- });
- },
- drawGrid() {
- const ctx = this.ctx;
- const width = this.canvas.width;
- const height = this.canvas.height;
-
- // 绘制淡淡的网格线
- ctx.strokeStyle = "rgba(64, 156, 255, 0.03)";
- ctx.lineWidth = 0.5;
-
- const gridSize = 30;
-
- for (let x = 0; x <= width; x += gridSize) {
- ctx.beginPath();
- ctx.moveTo(x, height * 0.5);
- ctx.lineTo(x, height);
- ctx.stroke();
- }
-
- for (let y = Math.floor(height * 0.5); y <= height; y += gridSize) {
- ctx.beginPath();
- ctx.moveTo(0, y);
- ctx.lineTo(width, y);
- ctx.stroke();
- }
- },
- drawGlow() {
- const ctx = this.ctx;
- const width = this.canvas.width;
- const height = this.canvas.height;
-
- // 底部发光效果
- const gradient = ctx.createLinearGradient(0, height - 150, 0, height);
- gradient.addColorStop(0, "transparent");
- gradient.addColorStop(0.5, "rgba(64, 156, 255, 0.1)");
- gradient.addColorStop(1, "rgba(64, 156, 255, 0.25)");
-
- ctx.fillStyle = gradient;
- ctx.fillRect(0, height - 150, width, 150);
- },
- animate() {
- const ctx = this.ctx;
- const width = this.canvas.width;
- const height = this.canvas.height;
-
- // 清空画布
- ctx.clearRect(0, 0, width, height);
-
- // 绘制网格
- this.drawGrid();
-
- // 绘制波浪
- this.waves.forEach((wave, index) => {
- this.drawWave(wave, index);
- });
-
- // 绘制粒子
- this.drawParticles();
-
- // 绘制发光效果
- this.drawGlow();
-
- // 更新时间
- this.time += 1;
-
- // 继续动画
- this.animationId = requestAnimationFrame(this.animate.bind(this));
- }
- }
- };
- </script>
- <style scoped>
- .wave-canvas {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- height: 100%;
- pointer-events: none;
- }
- </style>
|