[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,一经查实,立即删除!

相关文章

华纳云:怎么判断VPS的ip是不是公网ip

要判断一个VPS的IP地址是否为公网IP&#xff0c;您可以执行以下步骤&#xff1a; 查看IP地址&#xff1a; 首先&#xff0c;获取您的VPS的IP地址。您可以使用以下命令来查看VPS的IP地址&#xff1a; curl ifconfig.me 或 curl ipinfo.io/ip 这些命令将显示VPS的公网IP地址。 检…

el-table的formatter属性的使用方法

一、formatter是什么&#xff1f; formatter是el-table-column的一个属性&#xff0c;用来格式化内容。&#xff08;比如后台给你返0或1&#xff0c;你需要展示成“否”和“是”&#xff09; 二、详细使用 1.知道formatter之前&#xff1a; 代码如下&#xff08;示例&#…

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;}…

react使用echarts图表

要在React中使用Echarts图表库&#xff0c;可以按照以下步骤进行操作&#xff1a; 首先&#xff0c;确保你的React项目已经安装了Echarts库。可以通过运行以下命令来安装Echarts npm install echarts --save在你的组件文件中&#xff0c;导入Echarts库&#xff1a; import ech…

黑马程序员Java Web--14.综合案例--删除品牌功能实现

一、BrandMapper 添加删除单个数据的函数和sql语句 Delete("delete from tb_brand where id#{id}")void deleteById(int id);二、BrandService 定义一个删除单个数据的接口 void deleteById(int id);三、BrandServiceImpl 在BrandServiceImpl重写deleteById方法&a…

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

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

RabbitMQ高级知识点

以下是一些 RabbitMQ 的高级知识点&#xff1a; 1. Exchange&#xff1a; RabbitMQ 中的 Exchange 是消息路由器&#xff0c;用来接收消息并且转发到对应的 Queue 中。Exchange 有四种类型&#xff1a;Direct Exchange、Fanout Exchange、Topic Exchange 和 Headers Exchange。…

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;右子树和左子树比 ——也就是利用比较两个…

深入探析网络代理与网络安全

随着互联网的快速发展&#xff0c;网络安全问题日益突出&#xff0c;而网络代理技术正成为应对安全挑战的重要工具。本文将深入探讨Socks5代理、IP代理以及它们在网络安全、爬虫开发和HTTP协议中的关键作用&#xff0c;以期帮助读者更好地理解和应用这些技术。 1. Socks5代理&…

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

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

深度学习:UserWarning: The parameter ‘pretrained‘ is deprecated since 0.13..解决办法

深度学习&#xff1a;UserWarning: The parameter ‘pretrained’ is deprecated since 0.13 and may be removed in the future, please use ‘weights’ instead. 解决办法 1 报错警告&#xff1a; pytorch版本&#xff1a;0.14.1 在利用pytorch中的预训练模型时&#xff0…

搭建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>…

springBoot视频在线播放,支持快进,分片播放

1、主要是通过ResourceHttpRequestHandler类来实现&#xff0c;ResourceHttpRequestHandler本身支持分片加载&#xff0c;前端在请求头中携带Range: bytes 0-5001&#xff0c;就可以从后台自动截取对应大小视频内容。 第一步&#xff0c;先自定义实现一个ResourceHttpRequest…

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

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