WaveCanvas.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <template>
  2. <canvas ref="waveCanvas" class="wave-canvas"></canvas>
  3. </template>
  4. <script>
  5. export default {
  6. name: "WaveCanvas",
  7. data() {
  8. return {
  9. canvas: null,
  10. ctx: null,
  11. animationId: null,
  12. time: 0,
  13. waves: [],
  14. particles: []
  15. };
  16. },
  17. mounted() {
  18. this.initCanvas();
  19. this.initWaves();
  20. this.initParticles();
  21. this.animate();
  22. window.addEventListener("resize", this.handleResize);
  23. },
  24. beforeDestroy() {
  25. if (this.animationId) {
  26. cancelAnimationFrame(this.animationId);
  27. }
  28. window.removeEventListener("resize", this.handleResize);
  29. },
  30. methods: {
  31. initCanvas() {
  32. this.canvas = this.$refs.waveCanvas;
  33. this.ctx = this.canvas.getContext("2d");
  34. this.setCanvasSize();
  35. },
  36. setCanvasSize() {
  37. const parent = this.canvas.parentElement;
  38. this.canvas.width = parent.clientWidth;
  39. this.canvas.height = parent.clientHeight;
  40. },
  41. handleResize() {
  42. this.setCanvasSize();
  43. this.initParticles();
  44. },
  45. initWaves() {
  46. // 创建多层波浪参数
  47. this.waves = [
  48. {
  49. amplitude: 30,
  50. frequency: 0.01,
  51. speed: 0.02,
  52. yOffset: 0.7,
  53. color: "rgba(64, 156, 255, 0.4)",
  54. lineWidth: 2
  55. },
  56. {
  57. amplitude: 25,
  58. frequency: 0.015,
  59. speed: 0.025,
  60. yOffset: 0.75,
  61. color: "rgba(100, 181, 246, 0.3)",
  62. lineWidth: 1.5
  63. },
  64. {
  65. amplitude: 20,
  66. frequency: 0.008,
  67. speed: 0.015,
  68. yOffset: 0.8,
  69. color: "rgba(144, 202, 249, 0.25)",
  70. lineWidth: 1
  71. },
  72. {
  73. amplitude: 35,
  74. frequency: 0.012,
  75. speed: 0.018,
  76. yOffset: 0.65,
  77. color: "rgba(64, 156, 255, 0.2)",
  78. lineWidth: 1.5
  79. }
  80. ];
  81. },
  82. initParticles() {
  83. this.particles = [];
  84. const width = this.canvas.width;
  85. const height = this.canvas.height;
  86. // 创建点阵粒子
  87. const cols = Math.floor(width / 15);
  88. const rows = Math.floor(height / 15);
  89. for (let i = 0; i < cols; i++) {
  90. for (let j = 0; j < rows; j++) {
  91. if (Math.random() > 0.6) {
  92. this.particles.push({
  93. x: i * 15 + Math.random() * 10,
  94. y: j * 15 + Math.random() * 10,
  95. baseY: j * 15,
  96. size: Math.random() * 1.5 + 0.5,
  97. opacity: Math.random() * 0.5 + 0.2,
  98. speed: Math.random() * 0.5 + 0.2,
  99. phase: Math.random() * Math.PI * 2
  100. });
  101. }
  102. }
  103. }
  104. },
  105. drawWave(wave, index) {
  106. const ctx = this.ctx;
  107. const width = this.canvas.width;
  108. const height = this.canvas.height;
  109. const baseY = height * wave.yOffset;
  110. ctx.beginPath();
  111. ctx.strokeStyle = wave.color;
  112. ctx.lineWidth = wave.lineWidth;
  113. // 绘制波浪线
  114. for (let x = 0; x <= width; x += 2) {
  115. const y = baseY +
  116. Math.sin(x * wave.frequency + this.time * wave.speed + index) * wave.amplitude +
  117. Math.sin(x * wave.frequency * 2 + this.time * wave.speed * 1.5) * (wave.amplitude * 0.5);
  118. if (x === 0) {
  119. ctx.moveTo(x, y);
  120. } else {
  121. ctx.lineTo(x, y);
  122. }
  123. }
  124. ctx.stroke();
  125. // 填充波浪下方渐变
  126. ctx.lineTo(width, height);
  127. ctx.lineTo(0, height);
  128. ctx.closePath();
  129. const gradient = ctx.createLinearGradient(0, baseY - wave.amplitude, 0, height);
  130. gradient.addColorStop(0, wave.color.replace(/[\d.]+\)$/, "0.1)"));
  131. gradient.addColorStop(0.5, wave.color.replace(/[\d.]+\)$/, "0.05)"));
  132. gradient.addColorStop(1, "transparent");
  133. ctx.fillStyle = gradient;
  134. ctx.fill();
  135. },
  136. drawParticles() {
  137. const ctx = this.ctx;
  138. const width = this.canvas.width;
  139. const height = this.canvas.height;
  140. this.particles.forEach(particle => {
  141. // 粒子随波浪波动
  142. const waveY = Math.sin(particle.x * 0.01 + this.time * 0.02) * 20 +
  143. Math.sin(particle.x * 0.02 + this.time * 0.03) * 10;
  144. const y = particle.baseY + waveY * particle.speed;
  145. // 只在底部区域显示粒子
  146. if (y > height * 0.5) {
  147. const opacity = particle.opacity * (1 - (y - height * 0.5) / (height * 0.5));
  148. ctx.beginPath();
  149. ctx.arc(particle.x, y, particle.size, 0, Math.PI * 2);
  150. ctx.fillStyle = `rgba(100, 200, 255, ${Math.max(0, opacity)})`;
  151. ctx.fill();
  152. // 添加发光效果
  153. if (opacity > 0.3) {
  154. ctx.beginPath();
  155. ctx.arc(particle.x, y, particle.size * 3, 0, Math.PI * 2);
  156. ctx.fillStyle = `rgba(100, 200, 255, ${opacity * 0.2})`;
  157. ctx.fill();
  158. }
  159. }
  160. });
  161. },
  162. drawGrid() {
  163. const ctx = this.ctx;
  164. const width = this.canvas.width;
  165. const height = this.canvas.height;
  166. // 绘制淡淡的网格线
  167. ctx.strokeStyle = "rgba(64, 156, 255, 0.03)";
  168. ctx.lineWidth = 0.5;
  169. const gridSize = 30;
  170. for (let x = 0; x <= width; x += gridSize) {
  171. ctx.beginPath();
  172. ctx.moveTo(x, height * 0.5);
  173. ctx.lineTo(x, height);
  174. ctx.stroke();
  175. }
  176. for (let y = Math.floor(height * 0.5); y <= height; y += gridSize) {
  177. ctx.beginPath();
  178. ctx.moveTo(0, y);
  179. ctx.lineTo(width, y);
  180. ctx.stroke();
  181. }
  182. },
  183. drawGlow() {
  184. const ctx = this.ctx;
  185. const width = this.canvas.width;
  186. const height = this.canvas.height;
  187. // 底部发光效果
  188. const gradient = ctx.createLinearGradient(0, height - 150, 0, height);
  189. gradient.addColorStop(0, "transparent");
  190. gradient.addColorStop(0.5, "rgba(64, 156, 255, 0.1)");
  191. gradient.addColorStop(1, "rgba(64, 156, 255, 0.25)");
  192. ctx.fillStyle = gradient;
  193. ctx.fillRect(0, height - 150, width, 150);
  194. },
  195. animate() {
  196. const ctx = this.ctx;
  197. const width = this.canvas.width;
  198. const height = this.canvas.height;
  199. // 清空画布
  200. ctx.clearRect(0, 0, width, height);
  201. // 绘制网格
  202. this.drawGrid();
  203. // 绘制波浪
  204. this.waves.forEach((wave, index) => {
  205. this.drawWave(wave, index);
  206. });
  207. // 绘制粒子
  208. this.drawParticles();
  209. // 绘制发光效果
  210. this.drawGlow();
  211. // 更新时间
  212. this.time += 1;
  213. // 继续动画
  214. this.animationId = requestAnimationFrame(this.animate.bind(this));
  215. }
  216. }
  217. };
  218. </script>
  219. <style scoped>
  220. .wave-canvas {
  221. position: absolute;
  222. bottom: 0;
  223. left: 0;
  224. width: 100%;
  225. height: 100%;
  226. pointer-events: none;
  227. }
  228. </style>