小程序 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);
  }
},

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/702838.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

面向对象编程

文章目录 面向对象编程是怎么回事&#xff1a;面向对象编程的好处是&#xff1a;对象是啥&#xff0c;如何得到&#xff1f;运行原理代码举例注意事项&#xff1a;空指针异常 黑马学习笔记 面向对象编程是怎么回事&#xff1a; 祖师爷 詹姆斯高斯林 认为万物皆对象&#xff0c…

[next.js] svgr/webpack

nextjs如何配置svg文件&#xff0c;使其像react组件一样导入? 当前next.js 开发环境我使用了--turbo 来开启turbopack加速文件构建&#xff0c;所以之前的一些webpack loader之类的无法正常工作。通过搜索发现一般都是使用svgr/webpack来处理svg&#xff0c;打开svgr官网发现…

Stable Diffusion: ControlNet 插件安装

前面介绍了一些通过代码实现ControlNet进行控制的案例。现在通过Stable Diffusion体验一下更便捷的操作。 Stable Diffusion插件安装办法大致相同。启动Stable Diffusion后&#xff0c;点击最右边的“扩展”&#xff0c;点击“可下载”&#xff0c;点击“加载扩展列表”。 视网…

Linux服务器安装Jupyter,并设置公网访问详细教程

本章教程,主要介绍如何在Linux服务器上安装jupyter,并可以通过公网地址进行访问。 一、安装jupyter pip install jupyter二、生成jupyter配置文件 jupyter notebook --generate-config三、编辑这个配置文件 找到配置文件并修改以下配置项: # 允许所有 IP 地址访问 c.Noteb…

【Java面试】十九、并发篇(下):线程池

文章目录 1、为什么要使用线程池2、线程池的执行原理2.1 七个核心参数2.2 线程池的执行原理 3、线程池用到的常见的阻塞队列有哪些4、如何确定核心线程数开多少个&#xff1f;5、线程池的种类有哪些&#xff1f;6、为什么不建议用Executors封装好的静态方法创建线程池7、线程池…

【C++ | 左值、右值】一文了解C++的左值、右值、左值引用()、右值引用()

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-12 1…

鸿蒙轻内核A核源码分析系列五 虚实映射(5)虚实映射解除

虚实映射解除函数LOS_ArchMmuUnmap解除进程空间虚拟地址区间与物理地址区间的映射关系&#xff0c;其中参数包含MMU结构体、解除映射的虚拟地址和解除映射的数量count,数量的单位是内存页数。 ⑴处函数OsGetPte1用于获取指定虚拟地址对应的L1页表项数据。⑵处计算需要解除的无效…

python实现高斯(Gauss)迭代自动计算

实现高斯&#xff08;Gauss&#xff09;迭代自动计算 输入系数矩阵mx、值矩阵mr、迭代次数n&#xff0c;即可得到答案。本人在原博主的代码基础上优化了数据输出形式&#xff0c;原文链接&#xff1a;python实现高斯(Gauss)迭代法_python中gausspp-CSDN博客 运算结果如下图&am…

家用洗地机怎么选?四大行业精品集合,识别度超高

家用洗地机&#xff0c;作为一种能够高效清洁地面的清洁工具&#xff0c;不仅减轻了人们家务的轻度&#xff0c;也给人们腾出了很多空闲的时间去享受生活。但是洗地机那么多&#xff0c;我们在面对洗地机选购的时候&#xff0c;我们应该要注意哪些呢&#xff1f;下面就为大家详…

游戏研发(策略+sass+回调模式)

前言 由于这边需要对接游戏研发后台,基本就是开服,封禁.角色日志等,但是每个游戏提供的接口都是不一样的,所以为了统一处理提前进行sass封装,以便后续可以更好的兼容 同时还涉及了多数据源的问题,因为有些日志太大不可能直接去http调用,会使用直接查询游戏研发的数据库方式这一…

从零实现KV存储项目实战

本项目是从零实现一个完整的、兼容Redis协议的KV数据库项目。 通过每一行代码的编写。你会对整个系统了如指拿&#xff0c;这样对自己基本功的锻炼、对编程能力的提升都是很大的 项目提供完整的视频教程代码 下面是关于KV存储项目的技术大纲&#xff1a; 如果你在学习的过程…

BUAA-2024年春-OO第四单元总结

正向建模与开发 在本单元中&#xff0c;我们需要模拟一个小型的图书管理系统&#xff0c;完成图书馆所支持的相关业务&#xff0c;并遵守一定的规章制度。与前几次不同的是&#xff0c;本单元中&#xff0c;我们需要预先将自己的设计思路用UML来实现&#xff0c;然后进行编程。…

Ecovadis审核的内容

Ecovadis审核的内容。Ecovadis是一家国际性的企业社会责任评估机构&#xff0c;旨在为全球供应链的可持续性发展提供评估和审核。在本文中&#xff0c;我们将从以下几个方面详细介绍Ecovadis审核的内容&#xff1a; 一、Ecovadis审核的范围和目的 Ecovadis审核的范围涵盖了各个…

EMI电路

PFC 功率部分 1 、整流桥是串联 2 、 PFC 电感串联 3 、二极管并联 4 、 MOSFET 并联 EMI电路图

C++中的结构体——结构体嵌套结构体

作用&#xff1a;结构体中的成员可以是另一个结构体 例如&#xff1a;每一个老师辅导一个学生&#xff0c;每个老师的结构体中&#xff0c;记录一个学生的结构体 示例 运行结果

springboot二屯村钓鱼场管理系统-计算机毕业设计源码58167

摘 要 在互联网时代的来临&#xff0c;电子商务的骤起&#xff0c;一时间网络进行购物这一形式备受欢迎&#xff0c;到现在&#xff0c;网购更是普及。现如今各个行业也通过网购的方式来进行拓展业务&#xff0c;增加企业的知名度以及提升业绩&#xff0c;满足了用户像网购一样…

云原生应用开发培训,开启云计算时代的新征程

在云计算时代&#xff0c;云原生应用开发技术已经成为IT领域的热门话题。如果您想要转型至云原生领域&#xff0c;我们的云原生应用开发培训将帮助您开启新征程。 我们的课程内容涵盖了云原生技术的基础概念、容器技术、微服务架构、持续集成与持续发布&#xff08;CI/CD&#…

单细胞RNA测序(scRNA-seq) 理解Seurat对象存储信息含义和基本操作

单细胞测序技术是在单个细胞水平上&#xff0c;对基因组、转录组和表观基因组水平进行分析测序技术。bulk RNA-seq获得的是组织或器官等大量细胞中表达信号的均值&#xff0c;无法获取细胞之间的差异信息&#xff08;即丢失了细胞的异质性&#xff09;&#xff0c; 而单细胞测序…

【数组】【双指针】三数之和

打算冲一把算法类比赛&#xff0c;之前一直对算法提不起兴趣&#xff0c;也有我自己对它的抵触&#xff0c;本身算法也比较菜。 但现在打算勤勤恳恳刷题&#xff0c;踏踏实实总结&#xff0c;冲&#xff01; 数组——双指针 三数之和 该题力扣网址 错误做法 三重循环框架&a…