[uni-app] canvas绘制圆环进度条

文章目录

    • 需求
    • 参考链接
    • 基本问题的处理
      • 1:画布旋转的问题
      • 2:注意arc()的起始位置是3点钟方向
      • 3: 如果绘制1.9*Matn.PI的圆环, 要保证其实位置在0点方向?
      • 4:小线段怎么画, 角度怎么处理?
    • 源码

需求

要绘制一个如此的进度条
在这里插入图片描述

参考链接

uni-app使用canvas绘制时间刻度以及不显示问题处理

官网api

基本问题的处理

其实基本看参考链接学着搞, 不难的 . 主要是针对一些api的坑,要有了解就可以.

1:画布旋转的问题

在这里插入图片描述

2:注意arc()的起始位置是3点钟方向

在这里插入图片描述

3: 如果绘制1.9*Matn.PI的圆环, 要保证其实位置在0点方向?

在这里插入图片描述

			//0.1把画布先 逆时针旋转90度, 从0点绘制开始绘制baseCtx.rotate(-90 * Math.PI / 180)

梳理一下流程, 如果要画一个圆环, 且要保证起始点是0点方向, 步骤是
1.画布逆时针旋转90度
2.画圆操作
3.恢复画布旋转角度(因为rotate()角度会叠加,为了防止计算混乱, 可以旋转画布)

(有人会问执行第三点,不会把绘制的圆环又逆回去? 这里要明确的是, 画布是画布(即context), 绘制好的图像是绘制好的图像)

在这里插入图片描述

4:小线段怎么画, 角度怎么处理?

首先360°的圆,分成10份, 角度单位是36°
那么处理办法也是一样的
for循环内
1.画布旋转36° / 72° / 108° …
2.绘制小线段
3.画布恢复 36° / 72° / 108° …

在这里插入图片描述
绘制出的10等分小线段已经完成, 想要做到如下图效果. 我们只要在for循环内, 选出i=0,6,8即可
(即 100% 60% 80%进度)
在这里插入图片描述
但是问题来了…
实际效果如下
在这里插入图片描述
好像不对了…

通过控制不同下标的小线段的绘制, 得到如下的分析图,
(小线段因为是基于moveTo/lineTo,绘制的)
在这里插入图片描述
当i越大,小线段的起始点与结束点的距离也越大, 所以i=0的时候, 小线段最短,
在这里插入图片描述
那么我们就发现, 他是从原始画布的90°方向开始绘制的,

我们为了要得到从0点位置那就是对画布进行逆时针的180°旋转
在这里插入图片描述

那么步骤就是
for循环内
1.画布旋转36°-180° / 72°-180° / 108°-180° …
2.绘制小线段
3.画布恢复 逆向的 36°-180° / 72°-180° / 108°-180° …

于是得到了

在这里插入图片描述
再进过处理

在这里插入图片描述

再把圆环补全,去掉不用的小线段
在这里插入图片描述
与效果图对比(UI图实际是有点错误的,但这不重要)
在这里插入图片描述
最终效果:

	<scoreLevelCanvas :score="277" :progress="0.4"></scoreLevelCanvas>

在这里插入图片描述

<scoreLevelCanvas :score="277" :progress="0.68"></scoreLevelCanvas>

在这里插入图片描述

<scoreLevelCanvas :score="277" :progress="0.97"></scoreLevelCanvas>

在这里插入图片描述

源码

<template><view class="score-level-box" :style="{'width':reactWH+'px','height':reactWH+'px'}"><!-- baseCanvas --><canvas id="scoreLevelBase" canvas-id="scoreLevelBase":style="{'width':reactWH+'px','height':reactWH+'px'}"></canvas><!-- progressCanvas --><canvas id="scoreLevelProgress" canvas-id="scoreLevelProgress"style="position: absolute;left: 0;top:0":style="{'width':reactWH+'px','height':reactWH+'px'}"></canvas><view class="score-view" :style="{'fontSize':scoreFontSize+'px','color':strokeColor}">{{scoreString}}</view></view>
</template><script>export default {name: "scoreLevelCanvas",props: {// canvas视图的宽高(矩形->正方形)reactWH: {type: Number,default: () => 200,},// 进度progress: {type: Number,default: () => 0.4},score: {type: Number,default: () => 20,}},data() {return {baseCtx: null,progressCtx: null,};},computed: {// 计算中心点XcenterPointX() {return this.reactWH / 2;},// 计算中心点YcenterPointY() {return this.reactWH / 2;},//计算半径radius() {return this.reactWH / 2 * 0.9;},//计算小线段绘制的起始 - 偏内0.1个半径dotLineMoveTo() {return this.reactWH / 2 * (0.9 - 0.1);},//计算小线段绘制的结束 - 偏外0.1个半径dotLineLineTo() {return this.reactWH / 2 * (0.9 + 0.1);},//计算进度环的厚度progressWidth() {// 进度环的厚度, 设置为半径的8%return (this.reactWH / 2) * 0.08;},//计算小线段的厚度dotLineWidth() {// 小线段的厚度, 同圆环厚度return (this.reactWH / 2) * 0.08;},//计算进度环颜色strokeColor() {let strokeColor = ""if (this.progress < 0.6) {strokeColor = "#EA532F"} else if (this.progress >= 0.6 && this.progress < 0.8) {strokeColor = "#F9B93C"} else if (this.progress >= 0.8) {strokeColor = "#4CBA85"}return strokeColor},//计算得分字段scoreString() {return (this.score || "") + "分"},//计算得分字体大小scoreFontSize() {return this.radius * 0.4}},mounted() {// 绘制 基础圆样式this.drawScoreLevelBaseView()// 绘制 动态进度圆this.drawScoreLevelProgressView()// 最终绘制 - draw()this.draw()},methods: {// 绘制 基础圆样式drawScoreLevelBaseView() {const baseCtx = uni.createCanvasContext("scoreLevelBase", this);baseCtx.save();// 把圆心移到矩形中心点baseCtx.translate(this.centerPointX, this.centerPointY);//0.1把画布先 逆时针旋转90度, 从0点绘制开始绘制baseCtx.rotate(-90 * Math.PI / 180)//0.2绘制圆心, 方便观察// baseCtx.beginPath()// baseCtx.setStrokeStyle('#090')// baseCtx.arc(0, 0, 3, 0, 2 * Math.PI)// baseCtx.stroke()//1.绘制基础圆baseCtx.beginPath()baseCtx.setStrokeStyle("#EAEAEA")baseCtx.setLineWidth(this.progressWidth)baseCtx.setLineCap('round')baseCtx.arc(0, 0, this.radius, 0, 2 * Math.PI)baseCtx.stroke()//1.1恢复旋转角度(目的是恢复画布)baseCtx.rotate(90 * Math.PI / 180)//2. 绘制小线段 (360°/10)for (var i = 0; i < 10; i++) {// 计算每个小线段的旋转角度- 顺时针旋转画布// 发现,小线段在原始画布下, 是从90°方向顺时针绘制的, 因此要逆时针旋转180°let rotateDeg = i * 36 - 180baseCtx.rotate(rotateDeg * Math.PI / 180)baseCtx.beginPath()baseCtx.setLineWidth(0.3) // 预设一个极细的厚度,baseCtx.setLineCap('round')baseCtx.setStrokeStyle('#EAEAEA')// baseCtx.moveTo(0, this.dotLineMoveTo - (i * 8)) // (i*8)为了测试方便,baseCtx.moveTo(0, this.dotLineMoveTo)baseCtx.lineTo(0, this.dotLineLineTo)// 保留 100% 60% 80%进度的小线段if (i == 0 || i == 6 || i == 8) {baseCtx.setLineWidth(this.dotLineWidth)}baseCtx.stroke()// 绘制完成小线段后, 恢复画布旋转角度;baseCtx.rotate(-rotateDeg * Math.PI / 180)}this.baseCtx = baseCtx;},// 绘制 进度圆drawScoreLevelProgressView() {const progressCtx = uni.createCanvasContext("scoreLevelProgress", this);progressCtx.save();// 把圆心移到矩形中心点progressCtx.translate(this.centerPointX, this.centerPointY);//0.1把画布先 逆时针旋转90度, 从0点绘制开始绘制progressCtx.rotate(-90 * Math.PI / 180)//0.2绘制圆心, 方便观察// progressCtx.beginPath()// progressCtx.setStrokeStyle('#113')// progressCtx.arc(0, 0, 3, 0, 2 * Math.PI)// progressCtx.stroke()//1.绘制基础圆progressCtx.beginPath()progressCtx.setStrokeStyle(this.strokeColor)progressCtx.setLineWidth(this.progressWidth)progressCtx.setLineCap('round')progressCtx.arc(0, 0, this.radius, 0, 2 * this.progress * Math.PI)progressCtx.stroke()//1.1恢复旋转角度(目的是恢复画布)progressCtx.rotate(90 * Math.PI / 180)this.progressCtx = progressCtx;},draw() {setTimeout(() => {this.baseCtx.draw();this.progressCtx.draw();}, 50)},}}
</script><style lang="scss">.score-level-box {position: relative;// background-color: #91f;.score-view {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);font-family: PingFangSC-Medium, PingFang SC;font-weight: 500;}}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cesium Vue(三)— 相机配置

1. 坐标系转换 1.1 cesium使用到的坐标系 屏幕坐标系&#xff0c;二维的笛卡尔坐标系&#xff0c;API > Cartesian2地理空间坐标系&#xff0c;WGS-84坐标系&#xff0c; API > Cartographic(经度&#xff0c;维度&#xff0c;高度)三维笛卡尔空间直角坐标系&#xff0…

leetcode-279. 完全平方数

1. 题目链接 链接: 题目链接 2. 解答 #include <stdio.h> #include <stdlib.h> #include <stdbool.h>bool issquare(int n) {if (n 1 || n 4) return true;if (n 2 || n 3) return false;for (int i 3; i < n/2; i ) {if (n i*i) return true;}…

笔记本Win10系统一键重装操作方法

笔记本电脑已经成为大家日常生活和工作中必不可少的工具之一&#xff0c;如果笔记本电脑系统出现问题了&#xff0c;那么就会影响到大家的正常操作。这时候就可以考虑给笔记本电脑重装系统了。接下来小编给大家介绍关于一键重装Win10笔记本电脑系统的详细步骤方法。 推荐下载 系…

ftp靶机_获取shell

ftp靶机_获取shell 文章目录 ftp靶机_获取shellftp概念实验环境信息探测 发现漏洞优化shell ftp概念 FTP 是File Transfer Protocol(文件传输协议)的英文简称&#xff0c;而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时&#xff0c;它也是一个应用程序(…

自动化测试框架指南

目录 定义测试自动化 不同类型的框架 以工具为中心的框架 面向项目的框架 关键字驱动的框架 完美测试自动化框架的主要组件 测试库 单元测试 集成和端到端测试 行为驱动开发 测试数据管理 mock&#xff0c;Stubs和虚拟化 实施模式的通用机制 测试结果报告 CI平台…

Java系列 | 如何讲自己的JAR包上传至阿里云maven私有仓库【云效制品仓库】

什么是云效 云效是云原生时代一站式 BizDevOps 平台&#xff0c;产研数字化同行者&#xff0c;支持公共云、专有云和混合云多种部署形态&#xff0c;通过云原生新技术和研发新模式&#xff0c;助力创新创业和数字化转型企业快速实现产研数字化&#xff0c;打造“双敏”组织&…

mysql——面试题初体验

查询环境 1、student&#xff08;学生表&#xff09; 2、课程表(course) 3、教师表(teacher) 4、成绩表(score) 问题 (1) 查询所有学生的学号、姓名、选课数、总成绩 mysql> select s.s_id as 学号,s.s_name as 姓名 from student as s; ---------------- | 学号 | 姓名…

对称(镜像)二叉树

之前写的比较两棵树是否相等&#xff0c;是左子树和左子树比&#xff0c;右子树和右子树比——利用这个思想镜像二叉树就是从第二层的两个节点作为两棵树的根&#xff0c;然后比较&#xff0c;这里的比较是左子树和右子树比&#xff0c;右子树和左子树比 ——也就是利用比较两个…

解决 MyBatis 一对多查询中,出现每组元素只有一个,总组数与元素数总数相等的问题

文章目录 问题简述场景描述问题描述问题原因解决办法 问题简述 笔者在使用 MyBatis 进行一对多查询的时候遇到一个奇怪的问题。对于笔者的一对多的查询结果&#xff0c;出现了这样的一个现象&#xff1a;原来每个组里有多个元素&#xff0c;查询目标是查询所查的组&#xff0c;…

搭建Atlas2.2.0 集成CDH6.3.2 生产环境+kerberos

首先确保环境的干净&#xff0c;如果之前有安装过清理掉相关残留 确保安装atlas的服务器有足够的内存&#xff08;至少16G&#xff09;&#xff0c;有必要的hadoop角色 HDFS客户端 — 检索和更新Hadoop使用的用户组信息&#xff08;UGI&#xff09;中帐户成员资格的信息。对调…

Day2力扣打卡

打卡记录 无限数组的最短子数组&#xff08;滑动窗口&#xff09; 链接 思路&#xff1a;先求单个数组的总和&#xff0c;再对两个重复数组所组成的新数组上使用 不定长的滑动窗口 来求得满足目标的最小长度。 class Solution { public:int minSizeSubarray(vector<int>…

项目经理每天,每周,每月的工作清单

很多不懂项目管理的伙伴问&#xff0c;项目经理每天每周每个月的工作是什么呢&#xff1f; 仿佛他们什么都管&#xff0c;但是又没有具体的产出&#xff0c;但是每天看他们比谁都忙&#xff0c;其实很简单&#xff0c;项目中的每个环节负责具体的事情&#xff0c;但是每个环节…

Python---if选择判断结构、嵌套结构(if elif else)

1、if选择判断结构作用 if 英 /ɪf/ conj. &#xff08;表条件&#xff09;如果&#xff1b;&#xff08;表假设&#xff09;要是&#xff0c;假如&#xff1b;无论何时&#xff1b;虽然&#xff0c;即使&#xff1b;&#xff08;用于间接疑问&#xff09;是否&#xff1b…

uniapp-vue3-微信小程序-标签选择器wo-tag

采用uniapp-vue3实现, 是一款支持高度自定义的标签选择器组件&#xff0c;支持H5、微信小程序&#xff08;其他小程序未测试过&#xff0c;可自行尝试&#xff09; 可到插件市场下载尝试&#xff1a; https://ext.dcloud.net.cn/plugin?id14960 使用示例 <template>&…

System.exit()方法参数

说明文档&#xff1a;System (Java Platform SE 8 ) 终止当前正在运行的Java虚拟机。该参数用作状态代码&#xff1b;按照惯例&#xff0c;非零状态码表示异常终止。 此方法调用类Runtime中的exit方法。此方法从不正常返回。 调用System.exit&#xff08;n&#xff09;实际上等…

Android MediaCodec将h264实时视频流数据解码为yuv,并转换yuv的颜色格式为nv21

初始化mediacodec //宽高根据摄像头分辨率设置private int Width 1280;private int Height 720;private MediaCodec mediaCodec;private ByteBuffer[] inputBuffers;private void initMediaCodec(Surface surface) {try {Log.d(TAG, "onGetNetVideoData: ");//创建…

牛客:FZ12 牛牛的顺时针遍历

FZ12 牛牛的顺时针遍历 文章目录 FZ12 牛牛的顺时针遍历题目描述题解思路题解代码 题目描述 题解思路 通过一个变量来记录当前方向&#xff0c;遍历矩阵&#xff0c;每次遍历一条边&#xff0c;将该边的信息加入到结果中 题解代码 func spiralOrder(matrix [][]int) []int {…

Docker安装ES7.14和Kibana7.14(无账号密码)

一、Docker安装ES7.14.0 1、下载镜像 docker pull elasticsearch:7.14.0 2、docker安装7.14.0 mkdir -p /usr/local/elasticsearch/config mkdir -p /usr/local/elasticsearch/data chmod 777 -R /usr/local/elasticsearch/ echo "http.host: 0.0.0.0" >> /u…

IDEA中点击New没有Java Class

解决办法&#xff1a;右键src&#xff0c;也可以是其他文件名&#xff0c;点击Mark Directory as 点击Sources Root即可

【Java 进阶篇】JavaScript DOM Element 对象详解

JavaScript是一门广泛用于网页开发的脚本语言&#xff0c;而DOM&#xff08;文档对象模型&#xff09;是JavaScript在网页中操作HTML和XML文档的核心。DOM以树状结构表示文档&#xff0c;允许开发者以编程方式访问、操作和修改文档的内容和结构。在DOM中&#xff0c;Element对象…