canvas实现圆轨滑动

圆轨滑动gif:
请添加图片描述

1.初始化配置

// 基础配置
let start = 0;
let end = (5 / 3) * Math.PI;
let colorList = ["#55e3fe", "#2f87fc", "#343eeb"];
let arcWidth = "30"; //圆轨尺寸
let arcRadius = 180; // 圆轨半径
let lineRadius = 140; //虚线半径
let startTrack = false; // 开始节点是否开始运动
let endTrack = false; // 结束节点是否开始运动
let canvas = document.querySelector("#test");
let ratioW = Math.round(canvas.width / parseInt(canvas.style.width)); // 宽度占比
let ratioH = Math.round(canvas.height / parseInt(canvas.style.height)); // 高度占比
let x0 = Math.round(canvas.width / 2); //中心x轴坐标
let y0 = Math.round(canvas.height / 2); //中心y轴坐标
let lrw = Math.round(arcWidth / 2);
var ctx = canvas.getContext("2d");
var grad = null; // 边框渐变渲染只能够进行线性渲染
let startNodeX = 0, //开始节点X轴坐标
  startNodeY = 0, //开始节点Y轴坐标
  endNodeX = 0, //结束节点X轴坐标
  endNodeY = 0; //结束节点Y轴坐标

2.开始绘制
2.1 获取开始节点,结束节点的坐标

startNodeX = Math.round(x0 + arcRadius * Math.cos(start)); //开始节点X轴坐标
startNodeY = Math.round(y0 + arcRadius * Math.sin(start)); //开始节点Y轴坐标
endNodeX = Math.round(x0 + arcRadius * Math.cos(end)); //结束节点X轴坐标
endNodeY = Math.round(y0 + arcRadius * Math.sin(end)); //结束节点Y轴坐标

2.2 处理渐变颜色

if (start != end && startNodeX == endNodeX && startNodeY == endNodeY) {
   grad = ctx.createLinearGradient(
     startNodeX,
     startNodeY,
     x0 + (x0 - startNodeX),
     y0 + (y0 - startNodeX)
   );
 } else {
   grad = ctx.createLinearGradient(
     startNodeX,
     startNodeY,
     endNodeX,
     endNodeY
   );
   grad.addColorStop(0, colorList[0]);
   grad.addColorStop(0.5, colorList[1]);
   grad.addColorStop(1, colorList[2]);
 }

3.开始绘制

ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.translate(0, 0); //坐标定位至0,0处
// 1.背景颜色
ctx.fillStyle = "#040e31";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.setLineDash([1, 0]);
// 2.底部透明圆环
ctx.beginPath();
ctx.lineWidth = arcWidth;
ctx.strokeStyle = "#ccdffb55";
ctx.arc(x0, y0, arcRadius, 0, 2 * Math.PI); //绘制一个完整的圆
ctx.stroke();
// 3.渐变圆弧
ctx.beginPath();
ctx.lineWidth = arcWidth;
ctx.strokeStyle = grad;
ctx.arc(x0, y0, arcRadius, start, end);
ctx.stroke();
// 4.灰色虚细线
ctx.beginPath();
ctx.lineWidth = "20";
ctx.setLineDash([3, 8]);
ctx.strokeStyle = grad;
ctx.arc(x0, y0, lineRadius, start, end);
ctx.stroke();
// 5.渐变虚细线
ctx.beginPath();
ctx.lineWidth = "20";
ctx.strokeStyle = "#ccdffb55";
// 设置虚线排列
ctx.arc(x0, y0, lineRadius, end, start); //绘制一个完整的圆
ctx.stroke();
// 6.画圆弧两端的图标
ctx.setLineDash([1, 0]);
// 开始节点
ctx.beginPath();
ctx.lineWidth = lrw + "";
ctx.fillStyle = colorList[0];
ctx.arc(startNodeX, startNodeY, lrw, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
// 结束节点
ctx.beginPath();
ctx.fillStyle = colorList[2];
ctx.arc(endNodeX, endNodeY, lrw, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
if (startTrack) {
  ctx.setLineDash([3, 4]);
  // 开始节点
  ctx.beginPath();
  ctx.lineWidth = lrw - 8 + "";
  ctx.strokeStyle = "#ffffff";
  ctx.arc(startNodeX, startNodeY, lrw - 5, 0, 2 * Math.PI);
  ctx.stroke();
}
if (endTrack) {
  ctx.setLineDash([3, 4]);
  // 结束节点
  ctx.beginPath();
  ctx.lineWidth = lrw - 8 + "";
  ctx.strokeStyle = "#ffffff";
  ctx.arc(endNodeX, endNodeY, lrw - 5, 0, 2 * Math.PI);
  ctx.stroke();
}

4.以上是静态canvas圆环,若需要沿着圆轨滑动,则需要使用鼠标监听事件

// 画布鼠标按下事件
 canvas.addEventListener("mousedown", function (e) {
   let points = windowToCanvas(canvas, e.clientX, e.clientY);
   let startDis = distance(points.x, points.y, startNodeX, startNodeY);
   let endDis = distance(points.x, points.y, endNodeX, endNodeY);
   if (lrw > startDis) {
     startTrack = true;
     ctx.setLineDash([3, 4]);
     // 开始节点
     ctx.beginPath();
     ctx.lineWidth = lrw - 8 + "";
     ctx.strokeStyle = "#ffffff";
     ctx.arc(startNodeX, startNodeY, lrw - 5, 0, 2 * Math.PI);
     ctx.stroke();
   }
   if (lrw > endDis) {
     endTrack = true;
     ctx.setLineDash([3, 4]);
     // 结束节点
     ctx.beginPath();
     ctx.lineWidth = lrw - 8 + "";
     ctx.strokeStyle = "#ffffff";
     ctx.arc(endNodeX, endNodeY, lrw - 5, 0, 2 * Math.PI);
     ctx.stroke();
   }
 });
 // 画布鼠标松开事件
 canvas.addEventListener("mouseup", function (e) {
   if (startTrack) {
     ctx.setLineDash([1, 0]);
     // 开始节点
     ctx.beginPath();
     ctx.lineWidth = lrw + "";
     ctx.fillStyle = colorList[0];
     ctx.arc(startNodeX, startNodeY, lrw, 0, 2 * Math.PI);
     ctx.fill();
     ctx.closePath();
   }
   if (endTrack) {
     ctx.setLineDash([1, 0]);
     // 结束节点
     ctx.beginPath();
     ctx.lineWidth = lrw + "";
     ctx.fillStyle = colorList[2];
     ctx.arc(endNodeX, endNodeY, lrw, 0, 2 * Math.PI);
     ctx.fill();
     ctx.closePath();
   }
   startTrack = false;
   endTrack = false;
 });
 // 画布鼠标移动事件
 canvas.addEventListener("mousemove", function (e) {
   // 节点运动轨迹
   if (startTrack || endTrack) {
     let points = windowToCanvas(canvas, e.clientX, e.clientY);
     let angle = vectorAngle([0, 0], [x0 - points.x, y0 - points.y]);
     // 开始节点
     if (startTrack) start = angle;
     // 结束节点
     if (endTrack) end = angle;
     init();
   }
 });

5.辅助函数

// 两点之间的距离
function distance(x1, y1, x2, y2) {
  let squareSum = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
  return Math.round(Math.sqrt(squareSum));
}
// 求以下两个向量之间的角度
function vectorAngle(x, y) {
  let angle = Math.atan2(x[1] - y[1], x[0] - y[0]);
  return angle;
}

/** 获取鼠标在canvas上的坐标**/
function windowToCanvas(canvas, x, y) {
  let rect = canvas.getBoundingClientRect();
  return {
    x: Math.round((x - rect.left * (canvas.width / rect.width)) * ratioW),
    y: Math.round(
      (y - rect.top * (canvas.height / rect.height)) * ratioH
    ),
  };
}

总体代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        padding: 0;
        margin: 0;
      }
    </style>
  </head>
  <body>
    <canvas
      id="test"
      width="800"
      height="800"
      style="width: 400px; height: 400px"
    ></canvas>
    <script>
      // 基础配置
      let start = 0;
      let end = (5 / 3) * Math.PI;
      let colorList = ["#55e3fe", "#2f87fc", "#343eeb"];
      let arcWidth = "30"; //圆轨尺寸
      let arcRadius = 180; // 圆轨半径
      let lineRadius = 140; //虚线半径
      let startTrack = false; // 开始节点是否开始运动
      let endTrack = false; // 结束节点是否开始运动
      let canvas = document.querySelector("#test");
      let ratioW = Math.round(canvas.width / parseInt(canvas.style.width)); // 宽度占比
      let ratioH = Math.round(canvas.height / parseInt(canvas.style.height)); // 高度占比
      let x0 = Math.round(canvas.width / 2); //中心x轴坐标
      let y0 = Math.round(canvas.height / 2); //中心y轴坐标
      let lrw = Math.round(arcWidth / 2);
      var ctx = canvas.getContext("2d");
      var grad = null; // 边框渐变渲染只能够进行线性渲染
      let startNodeX = 0, //开始节点X轴坐标
        startNodeY = 0, //开始节点Y轴坐标
        endNodeX = 0, //结束节点X轴坐标
        endNodeY = 0; //结束节点Y轴坐标

      // 开始绘制
      init();

      // 画布鼠标按下事件
      canvas.addEventListener("mousedown", function (e) {
        let points = windowToCanvas(canvas, e.clientX, e.clientY);
        let startDis = distance(points.x, points.y, startNodeX, startNodeY);
        let endDis = distance(points.x, points.y, endNodeX, endNodeY);
        if (lrw > startDis) {
          startTrack = true;
          ctx.setLineDash([3, 4]);
          // 开始节点
          ctx.beginPath();
          ctx.lineWidth = lrw - 8 + "";
          ctx.strokeStyle = "#ffffff";
          ctx.arc(startNodeX, startNodeY, lrw - 5, 0, 2 * Math.PI);
          ctx.stroke();
        }
        if (lrw > endDis) {
          endTrack = true;
          ctx.setLineDash([3, 4]);
          // 结束节点
          ctx.beginPath();
          ctx.lineWidth = lrw - 8 + "";
          ctx.strokeStyle = "#ffffff";
          ctx.arc(endNodeX, endNodeY, lrw - 5, 0, 2 * Math.PI);
          ctx.stroke();
        }
      });
      // 画布鼠标松开事件
      canvas.addEventListener("mouseup", function (e) {
        if (startTrack) {
          ctx.setLineDash([1, 0]);
          // 开始节点
          ctx.beginPath();
          ctx.lineWidth = lrw + "";
          ctx.fillStyle = colorList[0];
          ctx.arc(startNodeX, startNodeY, lrw, 0, 2 * Math.PI);
          ctx.fill();
          ctx.closePath();
        }
        if (endTrack) {
          ctx.setLineDash([1, 0]);
          // 结束节点
          ctx.beginPath();
          ctx.lineWidth = lrw + "";
          ctx.fillStyle = colorList[2];
          ctx.arc(endNodeX, endNodeY, lrw, 0, 2 * Math.PI);
          ctx.fill();
          ctx.closePath();
        }
        startTrack = false;
        endTrack = false;
      });
      // 画布鼠标移动事件
      canvas.addEventListener("mousemove", function (e) {
        // 节点运动轨迹
        if (startTrack || endTrack) {
          let points = windowToCanvas(canvas, e.clientX, e.clientY);
          let angle = vectorAngle([0, 0], [x0 - points.x, y0 - points.y]);
          // 开始节点
          if (startTrack) start = angle;
          // 结束节点
          if (endTrack) end = angle;
          init();
        }
      });
      // 绘制画布函数
      function init() {
        startNodeX = Math.round(x0 + arcRadius * Math.cos(start)); //开始节点X轴坐标
        startNodeY = Math.round(y0 + arcRadius * Math.sin(start)); //开始节点Y轴坐标
        endNodeX = Math.round(x0 + arcRadius * Math.cos(end)); //结束节点X轴坐标
        endNodeY = Math.round(y0 + arcRadius * Math.sin(end)); //结束节点Y轴坐标
        if (start != end && startNodeX == endNodeX && startNodeY == endNodeY) {
          grad = ctx.createLinearGradient(
            startNodeX,
            startNodeY,
            x0 + (x0 - startNodeX),
            y0 + (y0 - startNodeX)
          );
        } else {
          grad = ctx.createLinearGradient(
            startNodeX,
            startNodeY,
            endNodeX,
            endNodeY
          );
          grad.addColorStop(0, colorList[0]);
          grad.addColorStop(0.5, colorList[1]);
          grad.addColorStop(1, colorList[2]);
        }
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.translate(0, 0); //坐标定位至0,0处
        // 1.背景颜色
        ctx.fillStyle = "#040e31";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.setLineDash([1, 0]);
        // 2.底部透明圆环
        ctx.beginPath();
        ctx.lineWidth = arcWidth;
        ctx.strokeStyle = "#ccdffb55";
        ctx.arc(x0, y0, arcRadius, 0, 2 * Math.PI); //绘制一个完整的圆
        ctx.stroke();
        // 3.渐变圆弧
        ctx.beginPath();
        ctx.lineWidth = arcWidth;
        ctx.strokeStyle = grad;
        ctx.arc(x0, y0, arcRadius, start, end);
        ctx.stroke();
        // 4.灰色虚细线
        ctx.beginPath();
        ctx.lineWidth = "20";
        ctx.setLineDash([3, 8]);
        ctx.strokeStyle = grad;
        ctx.arc(x0, y0, lineRadius, start, end);
        ctx.stroke();
        // 5.渐变虚细线
        ctx.beginPath();
        ctx.lineWidth = "20";
        ctx.strokeStyle = "#ccdffb55";
        // 设置虚线排列
        ctx.arc(x0, y0, lineRadius, end, start); //绘制一个完整的圆
        ctx.stroke();
        // 6.画圆弧两端的图标
        ctx.setLineDash([1, 0]);
        // 开始节点
        ctx.beginPath();
        ctx.lineWidth = lrw + "";
        ctx.fillStyle = colorList[0];
        ctx.arc(startNodeX, startNodeY, lrw, 0, 2 * Math.PI);
        ctx.fill();
        ctx.closePath();
        // 结束节点
        ctx.beginPath();
        ctx.fillStyle = colorList[2];
        ctx.arc(endNodeX, endNodeY, lrw, 0, 2 * Math.PI);
        ctx.fill();
        ctx.closePath();
        if (startTrack) {
          ctx.setLineDash([3, 4]);
          // 开始节点
          ctx.beginPath();
          ctx.lineWidth = lrw - 8 + "";
          ctx.strokeStyle = "#ffffff";
          ctx.arc(startNodeX, startNodeY, lrw - 5, 0, 2 * Math.PI);
          ctx.stroke();
        }
        if (endTrack) {
          ctx.setLineDash([3, 4]);
          // 结束节点
          ctx.beginPath();
          ctx.lineWidth = lrw - 8 + "";
          ctx.strokeStyle = "#ffffff";
          ctx.arc(endNodeX, endNodeY, lrw - 5, 0, 2 * Math.PI);
          ctx.stroke();
        }
        // 7.计算占比
        // let percent = (((end - start) / (2 * Math.PI)) * 100).toFixed(2);
        // ctx.font = `normal normal normal 32pt 宋体`;
        // ctx.fillStyle = colorList[0];
        // ctx.textAlign = "center";
        // ctx.lineWidth = 1;
        // ctx.textBaseline = "middle";
        // ctx.fillText(percent + "%", x0, y0);
        // 8.加载图片
        // let img = new Image();
        // img.src = "./images/circle.png";
        // img.onload = function (e) {
        //   // ctx.clearRect(0, 0, canvas.width, canvas.height);
        //   ctx.drawImage(this, 100, 100, 600, 600);
        // };
      }
      // 两点之间的距离
      function distance(x1, y1, x2, y2) {
        let squareSum = Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2);
        return Math.round(Math.sqrt(squareSum));
      }
      // 求以下两个向量之间的角度
      function vectorAngle(x, y) {
        let angle = Math.atan2(x[1] - y[1], x[0] - y[0]);
        return angle;
      }

      /** 获取鼠标在canvas上的坐标**/
      function windowToCanvas(canvas, x, y) {
        let rect = canvas.getBoundingClientRect();
        return {
          x: Math.round((x - rect.left * (canvas.width / rect.width)) * ratioW),
          y: Math.round(
            (y - rect.top * (canvas.height / rect.height)) * ratioH
          ),
        };
      }
    </script>
  </body>
</html>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值