容量 动效 仪表盘 Canvas 2D API

容量动效

html 复制代码
<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Canvas 内部圆形波浪</title>
    <style>
      canvas {
        border: 5px solid black;
      }
    </style>
  </head>
  <body>
    <canvas id="waveCanvas" width="400" height="400"></canvas>
    <label for="heightPercentageInput">Wave Height:</label>
    <input
      type="range"
      id="heightPercentageInput"
      min="0"
      max="1"
      step="0.01"
      value="0.5"
    />
    <label for="speedInput">Wave Speed:</label>
    <input
      type="range"
      id="speedInput"
      min="0.1"
      max="5"
      step="0.1"
      value="1"
    />
    <label for="opacity1Input">Wave 1 Opacity:</label>
    <input
      type="range"
      id="opacity1Input"
      min="0"
      max="1"
      step="0.01"
      value="0.5"
    />
    <label for="opacity2Input">Wave 2 Opacity:</label>
    <input
      type="range"
      id="opacity2Input"
      min="0"
      max="1"
      step="0.01"
      value="0.2"
    />
    <script>
      const canvas = document.getElementById("waveCanvas");
      const ctx = canvas.getContext("2d");

      let time = 0;

      function drawWave() {
        ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除之前的画面

        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        const radius = 100; // 圆的半径

        // 创建径向渐变
        const gradient = ctx.createRadialGradient(
          centerX,
          centerY,
          radius * 0.9,
          centerX,
          centerY,
          radius
        );
        gradient.addColorStop(0, "rgba(0, 255, 200, 0.1)");
        gradient.addColorStop(1, "rgba(0, 255, 200, 0)");

        ctx.save();
        ctx.scale(0.9, 0.9);
        ctx.translate(20, 20);

        // 设置渐变填充样式并绘制圆
        ctx.beginPath();
        ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
        ctx.fillStyle = gradient; // 使用渐变填充颜色
        ctx.fill(); // 填充圆
        ctx.clip(); // Clip to the circle

        // 获取输入值
        const heightPercentageInput = document.getElementById(
          "heightPercentageInput"
        );
        const speedInput = document.getElementById("speedInput");
        const opacity1Input = document.getElementById("opacity1Input");
        const opacity2Input = document.getElementById("opacity2Input");

        const waveHeight = radius * heightPercentageInput.value; // Control wave height
        const waveSpeed = speedInput.value; // Control wave speed
        const opacity1 = opacity1Input.value; // Control wave 1 opacity
        const opacity2 = opacity2Input.value; // Control wave 2 opacity

        // 画第一条波浪
        ctx.beginPath();
        ctx.moveTo(0, centerY + radius);
        for (let i = 0; i <= canvas.width; i++) {
          const y =
            Math.sin(i * 0.05 + time * waveSpeed) * 10 +
            waveHeight * 0.6 +
            centerY;
          ctx.lineTo(i, Math.min(y, centerY + radius)); // 确保在圆内
        }
        ctx.lineTo(canvas.width, centerY + radius);
        ctx.lineTo(0, centerY + radius);
        ctx.fillStyle = `rgba(0, 255, 200, ${opacity1})`;
        ctx.fill();

        // 画第二条波浪
        ctx.beginPath();
        ctx.moveTo(0, centerY + radius);
        for (let i = 0; i <= canvas.width; i++) {
          const y =
            Math.sin(i * 0.04 + time * (waveSpeed * 1.5) + Math.PI / 2) * 10 +
            waveHeight * 0.55 +
            centerY;
          ctx.lineTo(i, Math.min(y, centerY + radius)); // 确保在圆内
        }
        ctx.lineTo(canvas.width, centerY + radius);
        ctx.lineTo(0, centerY + radius);
        ctx.fillStyle = `rgba(0, 255, 200, ${opacity2})`; // Make it a bit more opaque
        ctx.fill();

        ctx.restore();

        // 绘制带有轨迹的旋转弧线
        const angle = (time / 5) % (Math.PI * 2); // 根据时间计算当前角度
        const trailLength = Math.PI * 0.75; // 弧线的长度
        const segments = 333; // 轨迹的段数

        // 绘制轨迹
        for (let i = 0; i < segments; i++) {
          const alpha = 1 - i / segments; // 渐变透明度
          const startAngle = angle - (i * trailLength) / segments; // 每段的起始角度
          const endAngle = startAngle + trailLength / segments; // 每段的结束角度

          ctx.beginPath();
          ctx.arc(centerX, centerY, radius, startAngle, endAngle); // 绘制每段弧线
          ctx.strokeStyle = `rgba(59, 144, 125, ${alpha})`; // 设置弧线颜色,带透明度
          ctx.lineWidth = 4 - (3 * i) / segments; // 渐变线宽
          ctx.stroke(); // 描绘弧线
        }

        // 设定一个小的偏移角度
        const offsetAngle = 2.31; // 调整这个值可以改变"往前"的距离

        // 绘制位于弧线末尾的点(移前)
        const dotX =
          centerX + radius * Math.cos(angle + trailLength - offsetAngle); // 计算点的X坐标
        const dotY =
          centerY + radius * Math.sin(angle + trailLength - offsetAngle); // 计算点的Y坐标
        ctx.beginPath();
        ctx.arc(dotX, dotY, 4, 0, Math.PI * 2); // 绘制点
        ctx.fillStyle = "#3b907d"; // 点的填充颜色
        ctx.fill(); // 填充点

        // 增加时间以实现动画
        time += 0.1;

        // 递归调用以实现动画效果
        requestAnimationFrame(drawWave);
      }

      drawWave(); // 开始绘制
    </script>
  </body>
</html>
相关推荐
前端不太难9 分钟前
RN 调试效率低,一点小改动就需要重新构建?解决手册(实战 / 脚本 / Demo)
前端·react native·重构
是谁眉眼11 分钟前
vue环境变量
前端·javascript·vue.js
3秒一个大12 分钟前
JSX 基本语法与 React 组件化思想
前端·react.js
鹏北海-RemHusband12 分钟前
Vue 组件解耦实践:用回调函数模式替代枚举类型传递
前端·javascript·vue.js
用户66006766853912 分钟前
斐波那契数列:从递归到缓存优化的极致拆解
前端·javascript·算法
海上彼尚16 分钟前
vite+vue3 ssg预渲染方案
前端·javascript·vue.js
初辰ge18 分钟前
做后台系统别再只会单体架构了,微前端才是更优解
前端·架构
风止何安啊21 分钟前
React 入门秘籍:像搭积木一样写网页,JSX 让开发爽到飞起!
前端·react.js·前端框架
whyfail23 分钟前
React原理(暴力版)
前端·react.js
shoa_top25 分钟前
一文带你掌握 JSONP:从 Script 标签到手写实现
前端·面试