上篇《canvas初识》讲解了canvas的一些基本功能,本篇开始讲解canvas在项目中的应用。
前言
在项目中,UI给出了下图这样一个仪表盘的设计
当然,echart也有类似的仪表盘的功能,因项目中没有引入echart,且与设计稿还是有些不同,本人还是决定用canvas手绘一个。
实战
分析
1、创建画布
2、画底色大半圆(灰色的)
3、画实际进度圆弧(蓝色)
4、画刻度
5、填充文本
<div class="inline-block relative">
<canvas ref="canvas" :width="width" :height="height"></canvas>
<div class="absolute content">
<slot></slot>
</div>
</div>
props: {
percent: {
type: Number,
default: 0
},
background: {
type: String,
default: '#5a8cf9'
},
width: {
type: Number,
default: 125
},
height: {
type: Number,
default: 125
}
}
const canvas = ref(null)
const bgX = 70
const bgY = 70
const lineW = 12
const dashW = 4
const scale = context => {
// const dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1
const dpr = 2
// Get the size of the canvas in CSS pixels.
const oldWidth = canvas.value.width
const oldHeight = canvas.value.height
// Give the canvas pixel dimensions of their CSS
// size * the device pixel ratio.
// console.log(
// dpr,
// 'dprdpr',
// window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1
// )
const _dpr = dpr + 0.3
canvas.value.width = Math.round(oldWidth * _dpr)
canvas.value.height = Math.round(oldHeight * _dpr)
canvas.value.style.width = oldWidth + 'px'
canvas.value.style.height = oldHeight + 'px'
context.scale(dpr, dpr)
return [Math.round(oldWidth * dpr), Math.round(oldHeight * dpr)]
}
const drawBg = context => {
context.save()
context.beginPath()
context.arc(bgX, bgY, Math.abs(bgX - lineW / 2), Math.PI * 0.7, 2.3 * Math.PI, false)
context.strokeStyle = '#F4F6FA'
context.lineWidth = lineW
context.lineCap = 'round'
context.stroke()
// context.fill()
// context.closePath()
// context.beginPath()
// context.arc(bgX, bgY, Math.abs(bgX - lineW), 0, 2 * Math.PI, false)
// context.fillStyle = '#fff'
// context.fill()
// context.closePath()
// context.restore()
}
const drawCycle = (context, percent) => {
context.save()
context.beginPath()
context.lineWidth = lineW
context.lineCap = 'round'
context.arc(bgX, bgY, Math.abs(bgX - lineW / 2), Math.PI * 0.7, 1.6 * Math.PI * percent + Math.PI * 0.7, false)
context.strokeStyle = props.background
context.stroke()
context.closePath()
context.restore()
}
const drawDash = context => {
context.save()
context.beginPath()
// 设置线宽
context.lineWidth = dashW
// "butt" | "round" | "square";
context.lineCap = 'butt'
// 设置间距(参数为无限数组,虚线的样式会随数组循环)
context.setLineDash([1, 11])
context.arc(bgX, bgY, Math.abs(bgX - lineW - dashW), Math.PI * 0.75, Math.PI * 2.25, false)
// 填充颜色
context.strokeStyle = props.background
// 开始填充
context.stroke()
context.closePath()
context.restore()
}
const draw = () => {
// CanvasRenderingContext2D
const context = canvas.value.getContext('2d')
const [width, height] = scale(context)
const id = requestAnimationFrame(() => {
drawBg(context)
drawDash(context)
// 如果百分比小于0.2 精度设置为0.0001
let steps = 0.01
if (props.percent < 0.02) steps = 0.0001
for (let i = 0; i < props.percent; i += steps) {
setTimeout(() => {
context.clearRect(0, 0, width, height)
drawBg(context)
drawDash(context)
drawCycle(context, i)
}, 1000 * i)
if (i >= props.percent) {
cancelAnimationFrame(id)
}
}
})
}