最近项目需要做一个罗盘仪效果,网上找了下图表插件,感觉都挺大;改成自己需要的ui又十分麻烦,干脆自己写个练练手,说干就干。
简单的罗盘仪效果
效果图如下
贴代码(基于VUE)
<template>
<div class="gauge">
<div class="progress" ref="progress">
<canvas>浏览器不支持Canvas,请升级或改用其它浏览器</canvas>
</div>
<div class="invite">{{text}}</div>
</div>
</template>
逻辑还是比较简单的
先初始化一个canvas,定义canvas的宽高
- 需要注意的是canvas元素有自己默认的宽高: 默认宽 300px, 高 150px
- canvas.width,canvas.height 和 canvas.style.width,canvas.style.height是两个不一样的概念,canva.width|height 是canvas画布真实的宽高,而canva.style.width|height 是canvas画布容器的宽高;真是宽高在容器宽高内会进行缩放; 如图
- 在Retina屏上面,canvas会出现模糊的情况;这是因为 canvas 不是矢量图,而是像图片一样是位图模式的。高 dpi 显示设备意味着每平方英寸有更多的像素。也就是说二倍屏,浏览器就会以2个像素点的宽度来渲染一个像素,该 canvas 在 Retina 屏幕下相当于占据了2倍的空间,相当于图片被放大了一倍,因此绘制出来的图片文字等会变模糊。所以我们需要算出屏幕像素比 ratio = window.devicePixelRatio || 1
现在,我们要开始画画了;首先是画一个半圆,然后是进度条(小球的尾巴),还有小球;这里比较麻烦的是小球和进度条的运行轨迹,因为小球和进度条的运行轨迹都是基于半圆的圆弧
- 小球完全是在圆弧上面运动的,小球的半径为圆弧的宽度 / 2;重点是要算出当前小球的圆心坐标才能画出小球的位置和形状;所以我们需要定义一个单位时间的弧度值angle,来计算小球单位时间的位置,speed不断增加,小球不断变化位置,每次变化位置,需要抹去之前画的小球,进度条;接着就是套公式了
- 进度条,这个简单,只半圆弧的基础上,另外画一条弧,弧度为总弧度(Math.PI)和当前进度的比值
现在开始,要让罗盘仪动起来!!定义一个变量speed(表示增加的弧度值),通过 requestAnimationFrame 进行动画,更显平滑流畅,空值speed的速度,就可以空值罗盘仪的变化方式
最后,给罗盘仪加点文字;主要用到fillStyle,fillText,textAlign;这里有个坑需要注意一下,textAlign的是相对于画布中fillText的起始坐标来的;跟css的textAlign不一样
上图起始点的坐标都在中间,textAlign展现形式不同。
所有的逻辑代码,整合如下:
const ratio = window.devicePixelRatio || 1 // Retina上面显示模糊,兼容苹果手机
const bound = {
start: Math.PI + 0.1,
end: Math.PI * 2 - 0.1
}
const colors = [{ // 定义颜色,不同等级,弧度的颜色不一样
start: 'ffb488',
end: 'ffddc2'
}, {
start: 'ffb488',
end: 'ffddc2'
}, {
start: 'babfcd',
end: 'dde1eb'
}, {
start: 'e4b23f',
end: 'ffe892'
}]
let ctx = null
let r = 0 // 半径
let lineWidth = 0
let layerColor = 'rgba(255,255,255, 0.5)'
let width = 0
let height = 0
let angle = 0.1
let endAngle = 0
let speed = 0.04
let lineCap = 'round'
let color = null
export default {
data () {
return {
canvas: null,
width: 0,
height: 0,
color: {}
}
},
props: {
rate: {
type: Number || String,
default: 0
},
count: {
type: Number || String,
default: 0
},
silver: {
type: Number || String,
default: 0
},
level: {
type: Number,
default: 1
},
text: {
type: String,
default: ''
}
},
methods: {
initCanvas () {
const container = this.$refs['progress']
const width = ~~container.clientWidth
const height = ~~container.clientHeight
this.canvas = container.getElementsByTagName('canvas')[0]
this.canvas.width = width * ratio
this.canvas.height = height * ratio
this.canvas.style.width = width + 'px'
this.canvas.style.height = height + 'px'
this.color = colors[ this.level - 1 ]
// this.canvas.getContext('2d').scale(ratio, ratio)
},
layer () { // 半圆
const grd = ctx.createLinearGradient(0, height, width, height)
grd.addColorStop(0, layerColor)
grd.addColorStop(1, layerColor)
ctx.beginPath()
ctx.strokeStyle = grd
ctx.lineWidth = lineWidth
ctx.lineCap = lineCap
ctx.arc(width / 2, height, r, bound.start, bound.end)
ctx.stroke()
ctx.closePath()
},
ball () { // 小圆球
const start = Math.max(angle, 0)
const end = Math.min(angle, Math.PI - 0.1)
ctx.beginPath()
ctx.fillStyle = '#fff'
ctx.arc(width / 2 - Math.cos(start) * r, height - Math.sin(end) * r, lineWidth / 2 + 2, 0, Math.PI * 2)
ctx.fill()
ctx.closePath()
},
step () { // 进度条
const start = Math.min(Math.PI + angle, bound.start)
const end = Math.min(Math.PI + angle, bound.end)
const progressGrd = ctx.createLinearGradient(0, height, width, height)
progressGrd.addColorStop(0, `#${color.start}`)
progressGrd.addColorStop(1, `#${color.end}`)
ctx.beginPath()
ctx.strokeStyle = progressGrd
ctx.lineWidth = lineWidth
ctx.lineCap = lineCap
ctx.arc(width / 2, height, r, start, end)
ctx.stroke()
ctx.closePath()
},
animate () {
if (endAngle < angle) {
return window.cancelAnimationFrame(this.animate)
} else {
ctx.clearRect(0, 0, width, height)
this.setText()
this.layer()
this.step()
this.ball()
window.requestAnimationFrame(this.animate)
angle += speed // 匀速增加
}
},
setText () {
ctx.font = `${12 * ratio}px 微软雅黑`
ctx.fillStyle = '#fff'
ctx.textAlign = 'center'
ctx.fillText('已邀会员', width / 2, height / 2, width)
ctx.font = `${32 * ratio}px 微软雅黑`
ctx.fillText(this.count || 0, width / 2, height - 5, width)
},
init () {
this.initCanvas()
ctx = this.canvas.getContext('2d')
width = this.canvas.width
height = this.canvas.height
color = this.color
lineWidth = ~~(width / 18)
r = width / 2 - lineWidth
endAngle = Math.max(Math.PI * this.rate, angle)
this.animate()
}
},
watch: {
rate: {
handler () {
this.init()
}
}
},
beforeDestroy () {
angle = 0.1
endAngle = 0
}
}
差不多就这么多,下班
今天的文章嫌图标插件太大?canvas自制罗盘仪分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16057.html