效果图


文章目录

    • 效果图
    • 实现思路
    • (1)初始化
    • (2)绘制背景半圆刻度
    • (3)绘制百分比刻度(带动画)
    • (4)绘制小三角(带动画)
    • (5)绘制中间文字(带动画)
    • 使用组件(组件整体代码在下面)
    • 组件整体代码

实现思路

代码过多,但不复杂

  1. 采用Canvas 2D,文档请搜索h5 canvas 开发文档;
  2. 定义4个canvas,通过相对定位重叠在一起:
  <!-- 绘制背景半圆刻度 --><canvas type="2d" id="bgLine" style="position:absolute;"></canvas><!-- 绘制百分比刻度(带动画) --><canvas type="2d" id="line" style="position:absolute;"></canvas><!-- 绘制小三角(带动画) --><canvas type="2d" id="mark" style="position:absolute;"></canvas><!-- 绘制中间文字(带动画) --><canvas type="2d" id="text" style="position:absolute;"></canvas>
  1. 利用cxt.setLineDash([10,10])设置为虚线,画圆弧;
  2. 通过修改中心点,旋转画布来实现动画;

(1)初始化

let scaleTimmer,markTimmer,textTimmer;
let bgLineCtx,markCtx,textCtx,lineCtx;
Component({data: {canvas: {size: 220,r: 100,  // 半径progress: 0, // 显示进度 (单位百分比)index: 0, // 开始刻度defaultColor: 'rgba(255,255,255,0.25)', // 背景刻度颜色activeColor: 'rgba(255,255,255,1)', // 进度条颜色collegeNum: 0, //推荐数量totle: 0, //推荐总量  2000time: 1000, //动画所需总时间  1000 = 1stimer: 20, //延迟间隔  1000 = 1stextUnit: '个', //文案单位   所/个text:'自定义文案', //文案 }},methods:{/*** 组件方法调用入口*/loadArcProgress(data) {const that=  this;this.setData({'canvas.collegeNum':data.total,'canvas.totle':data.totleLimit,'canvas.progress':(data.total / data.totleLimit) * 100},()=>{// if(bgLineCtx && markCtx && textCtx && lineCtx){that.drawBg();that.scaleAnimation();that.markAnimation();that.textAnimation();}else{that.initCtx().then(()=>{that.drawBg();that.scaleAnimation();that.markAnimation();that.textAnimation();})}})},/*** 初始化 ctx * 获取dom节点信息*/initCtx(){return new Promise((resolve)=>{wx.createSelectorQuery().in(this).selectAll('#bgLine,#line,#mark,#text').fields({ node: true, size: true }).exec((res) => {const dpr = wx.getSystemInfoSync().pixelRatio// #bgLinelet bgLineCanvas = res[0][0].nodebgLineCtx = bgLineCanvas.getContext('2d')bgLineCanvas.width = res[0][0].width * dprbgLineCanvas.height = res[0][0].height * dprbgLineCtx.scale(dpr, dpr)// #linelet lineCanvas = res[0][1].nodelineCtx = lineCanvas.getContext('2d')lineCanvas.width = res[0][1].width * dprlineCanvas.height = res[0][1].height * dprlineCtx.scale(dpr, dpr)// #marklet markCanvas = res[0][2].nodemarkCtx = markCanvas.getContext('2d')markCanvas.width = res[0][2].width * dprmarkCanvas.height = res[0][2].height * dprmarkCtx.scale(dpr, dpr)// #textlet textCanvas = res[0][3].nodetextCtx = textCanvas.getContext('2d')textCanvas.width = res[0][3].width * dprtextCanvas.height = res[0][3].height * dprtextCtx.scale(dpr, dpr)resolve()})})}}
})

(2)绘制背景半圆刻度

drawBg() {let ctx = bgLineCtx;let ctx1 = markCtx;let obj = this.data.canvas;ctx.clearRect(0, 0, obj.size, obj.size);ctx1.clearRect(0, 0, obj.size, obj.size);ctx.beginPath()ctx.strokeStyle = obj.defaultColor;ctx.lineWidth=2.5;ctx.setLineDash([2.5, 2.5]);ctx.arc(obj.size / 2,obj.size / 2,obj.size / 2-10,Math.PI,2*Math.PI);ctx.stroke();},

(3)绘制百分比刻度(带动画)

/*** 绘制白色刻度进度* 通过rotate旋转画布,修改中心点* draw保存上一次绘制内容*/scaleAnimation() {let ctx = lineCtx;let obj = this.data.canvas;ctx.beginPath()ctx.strokeStyle = obj.activeColor;ctx.lineWidth=2.5;ctx.setLineDash([2.5, 2.5]);function draw(x){ctx.beginPath()ctx.clearRect(0,0,obj.size,obj.size / 2);ctx.arc(obj.size / 2,obj.size / 2,obj.size / 2-10,Math.PI,Math.PI+x);ctx.stroke();}let num = obj.progress * (Math.PI / 100); // 转到多少 πlet addNum = num / (obj.time / obj.timer); // 每次转多少 πfunction animate(s){scaleTimmer = setInterval(function () {s += addNum;if (s > num) {draw(num);clearInterval(scaleTimmer);} else {draw(s);}}, obj.timer)}clearInterval(scaleTimmer);if(obj.collegeNum>0){animate(obj.index)}else{ctx.clearRect(0,0,obj.size / 2,obj.size / 2);}},

(4)绘制小三角(带动画)

/*** 绘制小三角* rotate旋转画布,修改中心点* draw清空上一次内容*/markAnimation() {let ctx = markCtx;let obj = this.data.canvas;function draw(x){ctx.clearRect(0,0,obj.size,obj.size);ctx.save();// 角度 = 弧度 * 180 / Math.PIlet deg = (Math.PI/180)*x*180 / Math.PI;let offsetY = -(Math.sin(deg) * obj.r);let offsetX = -(Math.cos(deg) * obj.r);ctx.fillStyle='#fff';ctx.translate(obj.size / 2 + offsetX, obj.size / 2 + offsetY);ctx.rotate(deg);ctx.beginPath();ctx.moveTo(-11, -4);ctx.lineTo(-3, 0);ctx.lineTo(-11, 4);ctx.closePath();ctx.fill();ctx.restore();}let num = obj.progress * (Math.PI / 100); // 转到多少 πlet addNum = num / (obj.time / obj.timer); // 每次转多少 πfunction animate(s){markTimmer = setInterval(function () {s += addNum;if (s > num) {draw(num);clearInterval(markTimmer);} else {draw(s);}}, obj.timer)}clearInterval(markTimmer);if(obj.collegeNum>0){animate(obj.index);}else{draw(0);}},

(5)绘制中间文字(带动画)

/*** 绘制中间文字* 默认 000 三位起,当推荐数量4位时 默认 0000 四位起* draw清空上一次内容*/textAnimation() {let ctx = textCtx;let obj = this.data.canvasobj.numFontSize = 12;obj.textFontSize = 39.6;ctx.beginPath();ctx.font = obj.textFontSize+'px Arial';obj.textY = obj.size / 2 - 25;let collegeNumLen = obj.collegeNum.toString().length;if (collegeNumLen == 4) {obj.textNumW = ctx.measureText('0000').width;} else {obj.textNumW = ctx.measureText('000').width;}ctx.beginPath();ctx.font = obj.numFontSize+'px Arial';obj.textSuoW = ctx.measureText('个').width;function drawText(num){ctx.clearRect(0,0,obj.size,obj.size)ctx.beginPath();ctx.fillStyle='#fff';ctx.textAlign='center';ctx.textBaseline='middle';ctx.font=obj.numFontSize+'px Arial';ctx.fillText(obj.text, obj.size / 2, obj.size / 2 - 10);ctx.beginPath();ctx.font = obj.textFontSize+'px Arial';ctx.textBaseline='bottom';ctx.fillText(num, obj.size / 2 - obj.textSuoW / 2, obj.textY + 9)ctx.beginPath();ctx.font = obj.numFontSize+'px Arial';ctx.textBaseline='bottom'ctx.fillText(obj.textUnit, obj.size / 2 + obj.textNumW / 2 + 2, obj.textY);}let addNum = Math.ceil(obj.collegeNum / (obj.time / obj.timer));function animate(s){textTimmer = setInterval(function () {s = s + addNum;if (s >= obj.collegeNum) {let num = obj.collegeNum.toString();num = num.length == 1 ? `00${num}` : num.length == 2 ? `0${num}` : num;drawText(num);clearInterval(textTimmer);} else {let num = s.toString();if (collegeNumLen == 4) {num = num.length == 1 ? `000${num}` : num.length == 2 ? `00${num}` : num.length == 3 ? `0${num}` : num;} else {num = num.length == 1 ? `00${num}`: num.length == 2 ? `0${num}` : num;}drawText(num);}}, obj.timer)}if (collegeNumLen == 4) {drawText('0000');} else {drawText('000');}clearInterval(textTimmer);animate(obj.index);}

使用组件(组件整体代码在下面)

index.wxml

<arcProgress id="arcProgress"></arcProgress>

index.json

{"usingComponents": {"arcProgress":"/components/arcProgress/arcProgress"}
}

index.js

onLoad(){this.selectComponent('#arcProgress').loadArcProgress({total: 1293, //推荐数量totleLimit: 2000, //推荐总量});}

组件整体代码

/components/arcProgress.wxml

<view style="height:{{canvas.size/2}}px;width:{{canvas.size}}px;margin:auto;position:relative;line-height:1;color:#fff;"><view class="textFS" style="position:absolute;bottom:0;right:{{canvas.size}}px;">0</view><view class="textFS" style="position:absolute;bottom:0;left:{{canvas.size}}px;">{{canvas.totle}}</view><canvas type="2d" id="bgLine" style="width:{{canvas.size}}px;height:{{canvas.size/2}}px;position:absolute;"></canvas><canvas type="2d" id="line" style="width:{{canvas.size}}px;height:{{canvas.size/2}}px;position:absolute;"></canvas><canvas type="2d" id="mark" style="width:{{canvas.size+10}}px;height:{{canvas.size/2+20}}px;position:absolute;"></canvas><canvas type="2d" id="text" style="width:{{canvas.size}}px;height:{{canvas.size/2}}px;position:absolute;"></canvas>
</view>

/components/arcProgress.js

/*** 默认  调用组件方法* * this.selectComponent('#arcProgress').loadArcProgress({total: 0, //推荐数量totleLimit: 0, //推荐总量});* 获取到数据后  调用组件方法* * this.selectComponent('#arcProgress').loadArcProgress({total: 1293, //推荐数量totleLimit: 2000, //推荐总量});*/
let scaleTimmer,markTimmer,textTimmer;
let bgLineCtx,markCtx,textCtx,lineCtx;
Component({data: {canvas: {size: 220,r: 100,  // 半径progress: 0, // 显示进度 (单位百分比)index: 0, // 开始刻度defaultColor: 'rgba(255,255,255,0.25)', // 背景刻度颜色activeColor: 'rgba(255,255,255,1)', // 进度条颜色collegeNum: 0, //推荐数量totle: 0, //推荐总量  2000time: 1000, //动画所需总时间  1000 = 1stimer: 20, //延迟间隔  1000 = 1stextUnit: '个', //文案单位   所/个text:'自定义文案', //文案 }},methods: {/*** 初始化 ctx * 获取dom节点信息*/initCtx(){return new Promise((resolve)=>{wx.createSelectorQuery().in(this).selectAll('#bgLine,#line,#mark,#text').fields({ node: true, size: true }).exec((res) => {const dpr = wx.getSystemInfoSync().pixelRatio// #bgLinelet bgLineCanvas = res[0][0].nodebgLineCtx = bgLineCanvas.getContext('2d')bgLineCanvas.width = res[0][0].width * dprbgLineCanvas.height = res[0][0].height * dprbgLineCtx.scale(dpr, dpr)// #linelet lineCanvas = res[0][1].nodelineCtx = lineCanvas.getContext('2d')lineCanvas.width = res[0][1].width * dprlineCanvas.height = res[0][1].height * dprlineCtx.scale(dpr, dpr)// #marklet markCanvas = res[0][2].nodemarkCtx = markCanvas.getContext('2d')markCanvas.width = res[0][2].width * dprmarkCanvas.height = res[0][2].height * dprmarkCtx.scale(dpr, dpr)// #textlet textCanvas = res[0][3].nodetextCtx = textCanvas.getContext('2d')textCanvas.width = res[0][3].width * dprtextCanvas.height = res[0][3].height * dprtextCtx.scale(dpr, dpr)resolve()})})},loadArcProgress(data) {const that=  this;this.setData({'canvas.collegeNum':data.total,'canvas.totle':data.totleLimit,'canvas.progress':(data.total / data.totleLimit) * 100},()=>{if(bgLineCtx && markCtx && textCtx && lineCtx){that.drawBg();that.scaleAnimation();that.markAnimation();that.textAnimation();}else{that.initCtx().then(()=>{that.drawBg();that.scaleAnimation();that.markAnimation();that.textAnimation();})}})},drawBg() {let ctx = bgLineCtx;let ctx1 = markCtx;let obj = this.data.canvas;ctx.clearRect(0, 0, obj.size, obj.size);ctx1.clearRect(0, 0, obj.size, obj.size);ctx.beginPath()ctx.strokeStyle = obj.defaultColor;ctx.lineWidth=2.5;ctx.setLineDash([2.5, 2.5]);ctx.arc(obj.size / 2,obj.size / 2,obj.size / 2-10,Math.PI,2*Math.PI);ctx.stroke();},/*** 绘制白色刻度进度* 通过rotate旋转画布,修改中心点* draw保存上一次绘制内容*/scaleAnimation() {let ctx = lineCtx;let obj = this.data.canvas;ctx.beginPath()ctx.strokeStyle = obj.activeColor;ctx.lineWidth=2.5;ctx.setLineDash([2.5, 2.5]);function draw(x){ctx.beginPath()ctx.clearRect(0,0,obj.size,obj.size / 2);ctx.arc(obj.size / 2,obj.size / 2,obj.size / 2-10,Math.PI,Math.PI+x);ctx.stroke();}let num = obj.progress * (Math.PI / 100); // 转到多少 πlet addNum = num / (obj.time / obj.timer); // 每次转多少 πfunction animate(s){scaleTimmer = setInterval(function () {s += addNum;if (s > num) {draw(num);clearInterval(scaleTimmer);} else {draw(s);}}, obj.timer)}clearInterval(scaleTimmer);if(obj.collegeNum>0){animate(obj.index)}else{ctx.clearRect(0,0,obj.size / 2,obj.size / 2);}},/*** 绘制小三角* rotate旋转画布,修改中心点* draw清空上一次内容*/markAnimation() {let ctx = markCtx;let obj = this.data.canvas;function draw(x){ctx.clearRect(0,0,obj.size,obj.size);ctx.save();// 角度 = 弧度 * 180 / Math.PIlet deg = (Math.PI/180)*x*180 / Math.PI;let offsetY = -(Math.sin(deg) * obj.r);let offsetX = -(Math.cos(deg) * obj.r);ctx.fillStyle='#fff';ctx.translate(obj.size / 2 + offsetX, obj.size / 2 + offsetY);ctx.rotate(deg);ctx.beginPath();ctx.moveTo(-11, -4);ctx.lineTo(-3, 0);ctx.lineTo(-11, 4);ctx.closePath();ctx.fill();ctx.restore();}let num = obj.progress * (Math.PI / 100); // 转到多少 πlet addNum = num / (obj.time / obj.timer); // 每次转多少 πfunction animate(s){markTimmer = setInterval(function () {s += addNum;if (s > num) {draw(num);clearInterval(markTimmer);} else {draw(s);}}, obj.timer)}clearInterval(markTimmer);if(obj.collegeNum>0){animate(obj.index);}else{draw(0);}},/*** 绘制中间文字* 默认 000 三位起,当推荐数量4位时 默认 0000 四位起* draw清空上一次内容*/textAnimation() {let ctx = textCtx;let obj = this.data.canvasobj.numFontSize = 12;obj.textFontSize = 39.6;ctx.beginPath();ctx.font = obj.textFontSize+'px Arial';obj.textY = obj.size / 2 - 25;let collegeNumLen = obj.collegeNum.toString().length;if (collegeNumLen == 4) {obj.textNumW = ctx.measureText('0000').width;} else {obj.textNumW = ctx.measureText('000').width;}ctx.beginPath();ctx.font = obj.numFontSize+'px Arial';obj.textSuoW = ctx.measureText('个').width;function drawText(num){ctx.clearRect(0,0,obj.size,obj.size)ctx.beginPath();ctx.fillStyle='#fff';ctx.textAlign='center';ctx.textBaseline='middle';ctx.font=obj.numFontSize+'px Arial';ctx.fillText(obj.text, obj.size / 2, obj.size / 2 - 10);ctx.beginPath();ctx.font = obj.textFontSize+'px Arial';ctx.textBaseline='bottom';ctx.fillText(num, obj.size / 2 - obj.textSuoW / 2, obj.textY + 9)ctx.beginPath();ctx.font = obj.numFontSize+'px Arial';ctx.textBaseline='bottom'ctx.fillText(obj.textUnit, obj.size / 2 + obj.textNumW / 2 + 2, obj.textY);}let addNum = Math.ceil(obj.collegeNum / (obj.time / obj.timer));function animate(s){textTimmer = setInterval(function () {s = s + addNum;if (s >= obj.collegeNum) {let num = obj.collegeNum.toString();num = num.length == 1 ? `00${num}` : num.length == 2 ? `0${num}` : num;drawText(num);clearInterval(textTimmer);} else {let num = s.toString();if (collegeNumLen == 4) {num = num.length == 1 ? `000${num}` : num.length == 2 ? `00${num}` : num.length == 3 ? `0${num}` : num;} else {num = num.length == 1 ? `00${num}`: num.length == 2 ? `0${num}` : num;}drawText(num);}}, obj.timer)}if (collegeNumLen == 4) {drawText('0000');} else {drawText('000');}clearInterval(textTimmer);animate(obj.index);}}
})

微信小程序-canvas 2d带动画的半圆形刻度进度条相关推荐

  1. 记一次微信小程序canvas 2d 生成海报问题

    因项目需要,需要制作海报分享. 如: 事情总是不是那么顺利,canvas生成海报生成中遇到各种奇葩问题.一开始是 wx.canvasToTempFilePath 中获取不到canvas对象,调用返回  ...

  2. 微信小程序使用html2canvas,微信小程序canvas 2d 引入本地图片并生成分享图

    在小程序基础库 v2.9.0 正式开放一套全新的 Canvas 接口.该接口符合 HTML Canvas 2D 的标准,实现上采用 GPU 硬件加速,渲染性能相比于现有的 Canvas 接口有一倍左右 ...

  3. 微信小程序实现自定义音乐播放(定时、进度条)

    实现关键: 1.wx.getBackgroundAudioManager() 2.在app.json中配置:"requiredBackgroundModes": ["au ...

  4. 微信小程序canvas 2d 绘制图片与文字 导出图片

    wxml内容 如下 <canvas id="myCanvas" type="2d"style="width: {{ canvas.width } ...

  5. 【微信小程序】UNI仿驾考宝典答题进度条的一种实现方式

    设置基本布局 <view class="vc1 vc2" :style="{'background':bcgss}"><view class= ...

  6. 微信小程序canvas实现简易手写签名版(uni-app)

    微信小程序可以通过canvas实现手写签名的效果,本文中使用的是微信小程序Canvas 2D接口 本示例中绘制的是横屏签名的效果,效果图如下: 这里我们需要调整canvas的物理宽高,默认物理宽高为3 ...

  7. 微信小程序 canvas type = 2d 绘制海报心得(包括怎么绘制图片和圆角图片和圆角矩形等)

    微信小程序 canvas type=2d 使用心得 为了方便这里我封装成了一个component 然后说说怎么使用最新的方法(使用方法类似于html中的canvas可以进行参考)获取--canvas ...

  8. 微信小程序canvas画布新接口type为2D时drawImage方法的使用以及注意事项

    微信小程序canvas画布自2.9.0 起支持一套新 Canvas 2D 接口(需指定 type 属性),但文档不全,从原canvas的api转变到新的api时遇到不少问题,现将新版与旧版的画布载入图 ...

  9. 微信小程序详解 php,微信小程序canvas基础详解

    canvas 元素用于在网页上绘制图形.HTML5 的 canvas 元素使用 JavaScript 在网页上绘制2D图像.本文主要和大家分享微信小程序canvas基础详解,希望能帮助到大家. 一.了 ...

  10. 微信小程序canvas简单使用

    微信小程序canvas简单使用 index.wxml文件 index.js文件 效果图 index.wxml文件 <canvas type="2d" id="can ...

最新文章

  1. Git 常用操作(3)- 本地分之显示、创建、切换、合并和删除操作
  2. [转载]“java.sql.SQLException:指定了无效的 Oracle URL”
  3. [转载] ffmpeg超详细综合教程——摄像头直播
  4. java 字符串过长_idea java常量字符串过长解决办法
  5. Asp.Net Mvc3.0(MEF依赖注入实例)
  6. [POJ2342]Anniversary party(树dp)
  7. Jerry Wang重装系统的一些备份
  8. 终于没刘海了!iPhone12 Pro 渲染图首曝,回归经典
  9. go语言 error
  10. office visio 替代_5款替代微软Visio的开源免费软件
  11. 《程序员面试宝典》.pdf
  12. 手游入门必备基础知识
  13. 烤仔TVのCCW | 交易的生命周期
  14. 非常感人的分手对白:伤感日志
  15. vue3实现主题切换功能
  16. 使用递归函数计算1到n之和
  17. 斗破苍穹java游戏美杜莎在哪_《斗破苍穹》四大魔兽体质排名,美杜莎的九彩吞天蟒仅排第二...
  18. Java把带小数点的字符串转换成整数
  19. Oracle回收站及flashback drop
  20. mongoDB地理位置检索

热门文章

  1. JS一些常用证件信息的正则表达式
  2. [工业互联-6]:PLC工业控制系统快速概览
  3. 高感 动态范围和宽容度的关系
  4. 几何分布的期望和方差公式推导_统计学笔记——概率、期望、排列组合和几何分布等...
  5. VOC检测格式数据分析和处理
  6. 十道解分式方程及答案_10道解分式方程练习题及答案.doc
  7. Promise初步详解(resolve,reject,catch)
  8. RCWL-0516微波雷达感应开关 人体感应传感器,迷你多普勒雷达
  9. MSP430F149的看门狗定时器
  10. 企业邮箱哪个好,邮箱品牌介绍—TOM邮箱