小程序 js+Canvas 绘制半圆环虚线进度条

效果图:

思路:过程分为三步,第1步,先画虚线底部背景,第2步,画动态的虚线(已选虚线蓝颜色),第3步,画动态的外标(已选虚线外位置的标),相关联的有1和2、2和3,1和2比较明显,颜色背景位置相同,2覆盖在1上,2和3终点位置相同,也就是计算弧度是一样的。

代码实现:
<view class="progress_item">
   <canvas class="progress_ring" type="2d" id="bgline"></canvas>
   <canvas class="progress_draw" type="2d" id="drawLine"></canvas>
   <canvas class="progress_move" type="2d" id="drawMove" bind:touchstart="moveDraw" bind:touchmove="moveDraw"></canvas>
</view>
css:
.progress_item{width:100%;position: relative;}
.progress_ring{width:280px;height: 140px;margin: 30px auto;}
.progress_draw{position: absolute;left:50%;transform: translateX(-50%);z-index: 3;top:0;width:280px;height: 140px;}
.progress_move{position: absolute;left:50%;transform: translateX(-50%);z-index: 5;top:-12px;width:300px;height: 170px;}
js:
let bgLineCtx,drawCtx,markCtx;
let markTimeStamp = 0;
let gbProgress = 0;

// 初始化画布
initRing(){
  const query = wx.createSelectorQuery()
  query.selectAll('#bgline,#drawLine,#drawMove').fields({ node: true, size: true }).exec((res) => {
    const dpr = wx.getSystemInfoSync().pixelRatio
    // 1、背景 半圆背景虚线底
    const bgLineCanvas = res[0][0].node
    bgLineCtx = bgLineCanvas.getContext('2d');
    bgLineCanvas.width = res[0][0].width * dpr
    bgLineCanvas.height = res[0][0].height * dpr
    bgLineCtx.scale(dpr, dpr);
    bgLineCtx.clearRect(0, 0, 280, 140);
    bgLineCtx.beginPath()
    bgLineCtx.strokeStyle = "#aaa";
    bgLineCtx.lineWidth = 12;
    bgLineCtx.lineCap = "line";
    bgLineCtx.setLineDash([2, 12]);
    bgLineCtx.arc(140,140,130,Math.PI,2*Math.PI);
    bgLineCtx.stroke();
    // 2、选中半圆虚线
    const drawLineCanvas = res[0][1].node
    drawCtx = drawLineCanvas.getContext('2d');
    drawLineCanvas.width = res[0][1].width * dpr
    drawLineCanvas.height = res[0][1].height * dpr
    drawCtx.scale(dpr, dpr);
    drawCtx.strokeStyle = "#2a82e4";
    drawCtx.lineWidth = 12;
    drawCtx.lineCap = "line";
    drawCtx.setLineDash([2, 12]);
    this.drawPress(1);
    // 3、手指滑动
    const drawMoveCanvas = res[0][2].node
    markCtx = drawMoveCanvas.getContext('2d');
    drawMoveCanvas.width = res[0][2].width * dpr
    drawMoveCanvas.height = res[0][2].height * dpr
    markCtx.scale(dpr, dpr);
    markCtx.translate(150, 150);
    markCtx.strokeStyle = "#2a82e4";
    markCtx.lineWidth = 4;
    markCtx.lineCap = 'round';
    this.drawRingDot(Math.PI);
  })
},
// 虚线占比 num为1时有动画效果
drawPress(num){
  let addNum = gbProgress / 20; // 转到多少 π(分为100份)每次转多少 π
  function draw(x){
    drawCtx.clearRect(0,0,280,140);
    drawCtx.beginPath()
    drawCtx.arc(140,140,130,Math.PI,Math.PI+x);
    drawCtx.stroke();
  }
  function animate(s){
    if(num == 1){
      setTimeout(function(){
        s += addNum;
        if (s >= gbProgress) {
          draw(gbProgress);
        }else {
          draw(s);
          animate(s);
        }
      }, 20); 
    }else{
      draw(gbProgress);
    }
  }
  animate(0);
},
// 虚线外的指标
drawRingDot(angle){
  markCtx.clearRect(-150, -150, 300, 170);
  markCtx.beginPath()
  markCtx.moveTo((148)*Math.cos(angle),(148)*Math.sin(angle));
  markCtx.lineTo((140)*Math.cos(angle),(140)*Math.sin(angle));
  markCtx.stroke();
},
// 手指触摸
moveDraw(e){
  let x = e.changedTouches[0].x;
  let y = e.changedTouches[0].y;
  if(e.type == "touchstart"){
    markTimeStamp = parseInt(e.timeStamp);
  }
  const radius = 150; // 半圆环的半径
  let maxDistance = radius+10; // 最大圆的距离
  let minDistance = radius-30; // 最小圆的距离
  let distance = Math.sqrt(Math.pow(x-radius, 2) + Math.pow(y-radius, 2));
  let num = parseInt(e.timeStamp - markTimeStamp);
  if(e.type=="touchmove" && num >= 200){
    markTimeStamp = parseInt(e.timeStamp);
  }
  if(y >= radius){
    y = radius
  }
  if (distance <= maxDistance && distance >= minDistance) {
    let radian = Math.atan2(y-radius, x-radius);
    let realAngle = (radian/ Math.PI+2).toFixed(2)*Math.PI; //弧度转化为角度
    let pAg = ((radian/ Math.PI+1)*100).toFixed(2);
    // console.log('val',radian,realAngle,pAg)
    if(pAg == 200){pAg = 0;}
    if(pAg >= 0 && pAg <= 100){
      gbProgress = pAg * (Math.PI/100);
      this.setData({
        progress: parseInt(pAg)
      })
      this.drawPress(0);
    }
    this.drawRingDot(realAngle);
  }
},

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逸曦穆泽

您的鼓励是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值