最近項目須要作一個羅盤儀效果,網上找了下圖表插件,感受都挺大;改爲本身須要的ui又十分麻煩,乾脆本身寫個練練手,說幹就幹。javascript
簡單的羅盤儀效果css
效果圖以下 html
貼代碼(基於VUE)<template>
<div class="gauge">
<div class="progress" ref="progress">
<canvas>瀏覽器不支持Canvas,請升級或改用其它瀏覽器</canvas>
</div>
<div class="invite">{{text}}</div>
</div>
</template>
複製代碼
邏輯仍是比較簡單的java
先初始化一個canvas,定義canvas的寬高canvas
如今,咱們要開始畫畫了;首先是畫一個半圓,而後是進度條(小球的尾巴),還有小球;這裏比較麻煩的是小球和進度條的運行軌跡,由於小球和進度條的運行軌跡都是基於半圓的圓弧瀏覽器
如今開始,要讓羅盤儀動起來!!定義一個變量speed(表示增長的弧度值),經過 requestAnimationFrame 進行動畫,更顯平滑流暢,空值speed的速度,就能夠空值羅盤儀的變化方式動畫
最後,給羅盤儀加點文字;主要用到fillStyle,fillText,textAlign;這裏有個坑須要注意一下,textAlign的是相對於畫布中fillText的起始座標來的;跟css的textAlign不同 ui
上圖起始點的座標都在中間,textAlign展示形式不一樣。全部的邏輯代碼,整合以下:this
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
}
}
複製代碼
差很少就這麼多,下班spa