钉钉小程序 + canvas实现环形进度条

1、在页面上添加 canvas 元素

<!-- 如果需要在高 dpr(设备像素比) 下取得更细腻的显示,需要先将 canvas 用属性设置放大,再用样式缩写 -->
<canvas id="canvasId" width="{{200 * pixelRatio}}" height="{{200 * pixelRatio}}" style="width:200px;height:200px;background:pink"/>

2、完成绘制圆环

  1. 代码演示
Page({
  data: {
    pixelRatio: '',
  },
  onLoad() {
    var _this = this;
    // 获取到设备的像素比,并根据像素比设置canvas画布的缩放比例
    dd.getSystemInfo({
      success: (res) => {
        _this.setData({
          pixelRatio: res.pixelRatio
        })
      }
    })
  },
  onReady() {
    // 页面加载完成
    var _this = this;

    // dd.createSelectorQuery() 获取一个节点查询对象 SelectorQuery;
    // selectorQuery.select(selector) 选择当前第一个匹配选择器的节点,选择器支持 id 选择器以及 class 选择器
    // selectorQuery.boundingClientRect() 将当前所选择节点的位置信息放入查询结果,返回对象包含 width/height/left/top/bottom/right
    // selectorQuery.exec(callback) 将查询结果放入 callback 回调中。查询结果为数组,每项为一次查询的结果,如果当前是节点列表,则单次查询结果也为数组。注意 exec 必须放到onReady函数里调用
    dd.createSelectorQuery().select("#canvasId").boundingClientRect().exec(res => {
      // 环形进度条变量
      let canvasWidth = res[0].width; // canvas元素的宽
      let canvasHeight = res[0].height;  // canvas元素的高
      let outerRadius = 180 // 外环半径
      let thickness = 40 // 圆环厚度
      let innerRadius = outerRadius - thickness // 内环半径
      let x = 0 // 圆心x坐标
      let y = 0 // 圆心y坐标
      let startAngle = -90 //开始角度
      let endAngle = 180 //结束角度

      // 创建绘图上下文
      var ctx = dd.createCanvasContext('canvasId');
      // 将绘图原点移到画布中央,注意:移动的距离不是style里宽高的一半,而是属性设置里宽高的一半,所以需要先*设备像素比再/2
      ctx.translate(canvasWidth * _this.data.pixelRatio / 2, canvasHeight * _this.data.pixelRatio / 2);

      // 开始绘图
      ctx.beginPath();
      // 步骤1 绘制外环 ctx.arc(绘图原点x坐标, 绘图原点y坐标, 绘图半径, 开始角度, 结束角度)
      ctx.arc(x, y, outerRadius, angle2Radian(startAngle), angle2Radian(endAngle))

      // 计算外环与内环第一个连接处的中心坐标,(-(innerRadius + thickness / 2), 0)
      let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)
      // 步骤2 绘制外环与内环第一个连接处的圆环,默认为顺时针方向,刚好是一个圆,从什么角度开始,什么角度结束不重要,但是这个角度线条没有交叉
      ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-180), angle2Radian(180))

      // 步骤3 绘制内环,从-90度开始 到 +180度结束,顺时针方向绘制,但是图线是交叉的
      // ctx.arc(x, y, innerRadius, angle2Radian(startAngle), angle2Radian(endAngle))

      // 步骤4 绘制内环,从 +180度开始 到 -90度结束,逆时针方向绘制,图线是连接的,没有交叉
      ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)

      // 计算外环与内环第二个连接处的中心坐标,(0, -(innerRadius + thickness / 2))
      let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)
      // 步骤5 绘制外环与内环第二个连接处的圆环,默认为顺时针方向,刚好是一个圆,从什么角度开始,什么角度结束不重要,但是这个角度线条没有交叉
      ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(90), angle2Radian(-270))
      // 步骤5 线条模式闭合,可以清楚的观察到线条走向
      // ctx.stroke();

      // // 步骤6 设置进度条的填充颜色为#eee,如果不设置,默认的填充颜色为black
      // ctx.setFillStyle('#eee');
      // // 步骤6 填充模式闭合
      // ctx.fill();


      // 步骤7 设置进度条的填充颜色为线性渐变,如果不设置,默认的填充颜色为black
      // ctx.createLinearGradient(起点x坐标, 起点y坐标, 终点x坐标, 终点y坐标) 创建一个线性的渐变色
      var lingrad = ctx.createLinearGradient(0, 0, 150, 0);
      // addColorStop 创建一个颜色的渐变点
      lingrad.addColorStop(0, '#00ABEB');
      lingrad.addColorStop(1, '#fff');
      ctx.setFillStyle(lingrad);

      // // 步骤7-1 观察颜色渐变的规律
      // ctx.fillRect(0, 0, 200, 200);
      // // 步骤7-1 填充模式闭合
      // ctx.fill();

      // 步骤7-2 填充模式闭合,仅显示圆环
      ctx.fill();

      // 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
      ctx.draw();

      //角度转弧度函数
      function angle2Radian(angle) {
        return angle * Math.PI / 180
      }

      //计算圆环上点的坐标函数
      function calcRingPoint(x, y, radius, angle) {
        let res = {}
        res.x = x + radius * Math.cos(angle * Math.PI / 180)
        res.y = y + radius * Math.sin(angle * Math.PI / 180)
        console.log(res)
        return res
      }
    });
  },
});


  1. 步骤1 ~ 步骤6 的效果图
    步骤1步骤2步骤3步骤4步骤5步骤6在这里插入图片描述在这里插入图片描述

3、根据百分比,动态渲染圆环

  1. 代码演示
Page({
  data: {
  	// 设备像素比
    pixelRatio: '',
  },
  onLoad() {
    var _this = this;
    // 获取到设备的像素比,并根据像素比设置canvas画布的缩放比例
    dd.getSystemInfo({
      success: (res) => {
        _this.setData({
          pixelRatio: res.pixelRatio
        })
      }
    })
  },
  onReady() {
    // 页面加载完成
    var _this = this;

    // dd.createSelectorQuery() 获取一个节点查询对象 SelectorQuery;
    // selectorQuery.select(selector) 选择当前第一个匹配选择器的节点,选择器支持 id 选择器以及 class 选择器
    // selectorQuery.boundingClientRect() 将当前所选择节点的位置信息放入查询结果,返回对象包含 width/height/left/top/bottom/right
    // selectorQuery.exec(callback) 将查询结果放入 callback 回调中。查询结果为数组,每项为一次查询的结果,如果当前是节点列表,则单次查询结果也为数组。注意 exec 必须放到onReady函数里调用
    dd.createSelectorQuery().select("#canvasId").boundingClientRect().exec(res => {
      // 环形进度条变量
      let canvasWidth = res[0].width; // canvas元素的宽
      let canvasHeight = res[0].height;  // canvas元素的高
      let outerRadius = 180 // 外环半径
      let thickness = 40 // 圆环厚度
      let innerRadius = outerRadius - thickness // 内环半径
      let x = 0 // 圆心x坐标
      let y = 0 // 圆心y坐标
      let startAngle = -90 //开始角度
      let endAngle = 180 //结束角度

      // 创建绘图上下文
      var ctx = dd.createCanvasContext('canvasId');
      
      // 将绘图原点移到画布中央,注意:移动的距离不是style里宽高的一半,而是属性设置里宽高的一半,所以需要先*设备像素比再/2
      ctx.translate(canvasWidth * _this.data.pixelRatio / 2, canvasHeight * _this.data.pixelRatio / 2);
      
      ctx.rotate(angle2Radian(225)) // 将画布旋转225度,这是最后一步

      // 设置进度条的填充颜色为#eee,如果不设置,默认的填充颜色为black
      ctx.setFillStyle('#eee');
      // 渲染灰色圆环背景
      renderRing(startAngle, endAngle);  


      // 设置进度条的填充颜色为线性渐变,如果不设置,默认的填充颜色为black
      // ctx.createLinearGradient(起点x坐标, 起点y坐标, 终点x坐标, 终点y坐标) 创建一个线性的渐变色
      var lingrad = ctx.createLinearGradient(0, 0, 150, 0);
      // addColorStop 创建一个颜色的渐变点
      lingrad.addColorStop(0, '#00ABEB');
      lingrad.addColorStop(1, '#000BE0'); 
      ctx.setFillStyle(lingrad); 

      // 按照百分比,动态渲染颜色为渐变色的圆环 
      let tempAngle = startAngle  // 每次前进结束时的角度
      let total = 1000 // 总分
      let now = 800 // 当前分数
      let percent = now / total // 百分比
      let twoEndAngle = percent * 270 + startAngle  // 最后结束时的角度,是一个固定值
      let step = (twoEndAngle - startAngle) / 80  // 步长,分为80步,是一个固定值
      let inter = setInterval(() => { // 设置并开启定时器
        if (tempAngle > twoEndAngle) {  // 到达目标角度
          clearInterval(inter)
        } else {  // 未到达目标角度
          tempAngle += step
        }
        renderRing(startAngle, tempAngle)
      }, 10)

      // 圆环渲染函数
      function renderRing(startAngle, endAngle) {
        // 开始绘图
        ctx.beginPath();

        // 绘制外环 ctx.arc(绘图原点x坐标, 绘图原点y坐标, 绘图半径, 开始角度, 结束角度)
        ctx.arc(x, y, outerRadius, angle2Radian(startAngle), angle2Radian(endAngle))

        // 计算外环与内环第一个连接处的中心坐标,(-(innerRadius + thickness / 2), 0)
        let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)
        // 绘制外环与内环第一个连接处的圆环,默认为顺时针方向,刚好是一个圆,从什么角度开始,什么角度结束不重要,但是这个角度线条没有交叉
        ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-180), angle2Radian(180))

        // 绘制内环,从 +180度开始 到 -90度结束,逆时针方向绘制,图线是连接的,没有交叉
        ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)

        // 计算外环与内环第二个连接处的中心坐标,(0, -(innerRadius + thickness / 2))
        let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)
        // 绘制外环与内环第二个连接处的圆环,默认为顺时针方向,刚好是一个圆,从什么角度开始,什么角度结束不重要,但是这个角度线条没有交叉
        ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(90), angle2Radian(-270))

        // 填充模式闭合
        ctx.fill();

        // 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
        ctx.draw();
      }

      //角度转弧度函数
      function angle2Radian(angle) {
        return angle * Math.PI / 180
      }

      //计算圆环上点的坐标函数
      function calcRingPoint(x, y, radius, angle) {
        let res = {}
        res.x = x + radius * Math.cos(angle * Math.PI / 180)
        res.y = y + radius * Math.sin(angle * Math.PI / 180)
        console.log(res)
        return res
      }
    });
  },
});

  1. 效果图
    在这里插入图片描述

参考文档:https://www.cnblogs.com/sweeeper/p/11580424.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值