共计 4407 个字符,预计需要花费 12 分钟才能阅读完成。
最终效果
一、定义初始变量
let radius = 140 // 外环半径
let thickness = 20 // 圆环厚度
let innerRadius = radius - thickness // 内环半径
let startAngle = -90 // 开始角度
let endAngle = 180 // 结束角度
let x = 0 // 圆心 x 坐标
let y = 0 // 圆心 y 坐标
let canvas = document.getElementById('tutorial');
canvas.width = 300;
canvas.height = 300;
let ctx = canvas.getContext('2d');
ctx.translate(canvas.width / 2, canvas.height / 2);// 将绘图原点移到画布中央
ctx.rotate(angle2Radian(225)) // 将画布旋转 225 度
ctx.fillStyle = "#f2d7d7"; // 初始填充颜色
二、工具方法
// 计算圆环上点的坐标
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)
return res
}
// 弧度转角度
function radian2Angle(radian) {return 180 * radian / Math.PI}
// 角度转弧度
function angle2Radian(angle) {return angle * Math.PI / 180}
三、渲染方法
// 渲染函数
function renderRing(startAngle, endAngle) {ctx.beginPath();
// 绘制外环
ctx.arc(x, y, radius, angle2Radian(startAngle), angle2Radian(endAngle))
// 计算外环与内环第一个连接处的中心坐标
let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)
// 绘制外环与内环第一个连接处的圆环
ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))
// 绘制内环
ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)
// 计算外环与内环第二个连接处的中心坐标
let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)
// 绘制外环与内环第二个连接处的圆环
ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))
ctx.fill()
// ctx.stroke()}
具体思路:
为了方便,代码中使用弧度的地方一律由角度转为弧度
1. 绘制外环:
这一步最简单,直接按照官方的使用方法使用即可
2. 绘制外环与内环连接处的第一个圆环
首先算出,外环结束处与内环开始处中间点的坐标
计算圆环上点的坐标公式为:
x = x + radius Math.cos(angle Math.PI / 180)
y = y + radius Math.sin(angle Math.PI / 180)
代入以上公式可算出圆环上任意一点的坐标,然后再以此为圆心,圆环厚度 / 2 为半径 画圆环
3. 绘制内环
这一步只需将半径缩短,并将绘制外环的开始角度与结束角度调换即可
4. 绘制内环与外环连接处的第二圆环
和第二步同理,先计算出外环开始处与内环结束处中间点的坐标,然后再以此为圆心,圆环厚度 / 2 为半径 画圆环
5. 完成填充
到这一步,圆环就完成了
四、动态进度条
// 进度条动画
ctx.fillStyle = "#e87c7c";
let tempAngle = startAngle
let twoEndAngle = 0
let step = (twoEndAngle - startAngle) / 100
let numberSpan = document.querySelector('.number')
let count = 0
let inter = setInterval(() => {if (tempAngle > twoEndAngle) {clearInterval(inter)
} else {
count++
numberSpan.innerText = count
tempAngle += step
}
renderRing(startAngle, tempAngle)
}, 16.7)
动态计算结束角度,然后设定一个计数器,重复执行渲染方法。
五、完整代码
<!DOCTYPE html>
<html lang="cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>canvas</title>
<style>
.ring {
width: 300px;
height: 300px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
}
.fraction {
position: absolute;
font-size: 30px;
font-weight: bold;
color: red;
}
.small {
font-size: 12px;
font-weight: lighter;
}
.title {
font-size: 20px;
color: red;
bottom: 40px;
position: absolute;
}
</style>
</head>
<body>
<div class="ring">
<canvas id="tutorial"></canvas>
<span class="fraction"><span class="number">0</span> <span class="small"> 分 </span> </span>
<span class="title"> 服务分 </span>
</div>
<script>
let radius = 140 // 外环半径
let thickness = 20 // 圆环厚度
let innerRadius = radius - thickness // 内环半径
let startAngle = -90 // 开始角度
let endAngle = 180 // 结束角度
let x = 0 // 圆心 x 坐标
let y = 0 // 圆心 y 坐标
let canvas = document.getElementById('tutorial');
canvas.width = 300;
canvas.height = 300;
let ctx = canvas.getContext('2d');
ctx.translate(canvas.width / 2, canvas.height / 2);// 将绘图原点移到画布中央
ctx.rotate(angle2Radian(225)) // 将画布旋转 225 度
ctx.fillStyle = "#f2d7d7"; // 初始填充颜色
renderRing(startAngle, endAngle)
// 进度条动画
ctx.fillStyle = "#e87c7c";
let tempAngle = startAngle
let twoEndAngle = 0
let step = (twoEndAngle - startAngle) / 100
let numberSpan = document.querySelector('.number')
let count = 0
let inter = setInterval(() => {if (tempAngle > twoEndAngle) {clearInterval(inter)
} else {
count++
numberSpan.innerText = count
tempAngle += step
}
renderRing(startAngle, tempAngle)
}, 16.7)
// 渲染函数
function renderRing(startAngle, endAngle) {ctx.beginPath();
// 绘制外环
ctx.arc(x, y, radius, angle2Radian(startAngle), angle2Radian(endAngle))
// 计算外环与内环第一个连接处的中心坐标
let oneCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, endAngle)
// 绘制外环与内环第一个连接处的圆环
ctx.arc(oneCtrlPoint.x, oneCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))
// // 绘制内环
ctx.arc(x, y, innerRadius, angle2Radian(endAngle), angle2Radian(startAngle), true)
// 计算外环与内环第二个连接处的中心坐标
let twoCtrlPoint = calcRingPoint(x, y, innerRadius + thickness / 2, startAngle)
// 绘制外环与内环第二个连接处的圆环
ctx.arc(twoCtrlPoint.x, twoCtrlPoint.y, thickness / 2, angle2Radian(-90), angle2Radian(270))
ctx.fill()
// ctx.stroke()}
// 计算圆环上点的坐标
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)
return res
}
// 弧度转角度
function radian2Angle(radian) {return 180 * radian / Math.PI}
// 角度转弧度
function angle2Radian(angle) {return angle * Math.PI / 180}
</script>
</body>
</html>
正文完
发表至: javascript
2019-07-27