榆山数据端小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

199 lines
5.7 KiB

import baseComponent from '../helpers/baseComponent'
import classNames from '../helpers/classNames'
const toAngle = (a) => a / 180 * Math.PI
const percent = (a) => toAngle(a / 100 * 360)
const easeInOutCubic = (a, b, c, d) => {
a /= d / 2
if (a < 1) return c / 2 * a * a * a + b
a -= 2
return c / 2 * (a * a * a + 2) + b
}
baseComponent({
properties: {
prefixCls: {
type: String,
value: 'wux-circle',
},
percent: {
type: Number,
value: 0,
observer: 'redraw',
},
strokeWidth: {
type: Number,
value: 10,
},
size: {
type: Number,
value: 120,
observer: 'updateStyle',
},
lineCap: {
type: String,
value: 'round',
},
backgroundColor: {
type: String,
value: '#f3f3f3',
},
color: {
type: String,
value: '#33cd5f',
},
sAngle: {
type: Number,
value: 0,
observer(newVal) {
this.setData({
beginAngle: toAngle(newVal),
})
},
},
counterclockwise: {
type: Boolean,
value: false,
},
speed: {
type: Number,
value: 2000,
},
animate: {
type: Boolean,
value: true,
},
background: {
type: Boolean,
value: true,
},
},
data: {
beginAngle: 0,
startAngle: 0,
endAngle: 0,
currentAngle: 0,
},
computed: {
classes: ['prefixCls', function(prefixCls) {
const wrap = classNames(prefixCls)
const inner = `${prefixCls}__inner`
return {
wrap,
inner,
}
}],
},
methods: {
/**
* 更新样式
*/
updateStyle(size = this.data.size) {
const style = `width: ${size}px; height: ${size}px;`
this.setData({
style,
})
},
/**
* 着帧绘制 canvas
*/
redraw(value = this.data.percent) {
const endAngle = percent(value)
const now = Date.now()
const decrease = this.data.currentAngle > endAngle
const startAngle = !decrease ? this.data.currentAngle : this.data.endAngle
this.cancelNextCallback()
this.clearTimer()
this.safeSetData({ startAngle, endAngle }, () => {
this.animate(now, now, decrease)
})
},
/**
* 绘制 canvas
*/
draw(line = true) {
const { lineCap, backgroundColor, color, size, strokeWidth, counterclockwise, background } = this.data
const position = size / 2
const radius = position - strokeWidth / 2
const p = 2 * Math.PI
const startAngle = counterclockwise ? p - this.data.beginAngle : this.data.beginAngle
const endAngle = counterclockwise ? p - (this.data.beginAngle + this.data.currentAngle) : this.data.beginAngle + this.data.currentAngle
// 创建 canvas 绘图上下文
this.ctx = this.ctx || wx.createCanvasContext('circle', this)
// 清除画布
this.ctx.clearRect(0, 0, size, size)
// 绘制背景
if (background) {
this.ctx.beginPath()
this.ctx.arc(position, position, radius, 0, 2 * Math.PI)
this.ctx.setLineWidth(strokeWidth)
this.ctx.setStrokeStyle(backgroundColor)
this.ctx.stroke()
}
// 绘制进度
if (line) {
this.ctx.beginPath()
this.ctx.arc(position, position, radius, startAngle, endAngle)
this.ctx.setLineWidth(strokeWidth)
this.ctx.setStrokeStyle(color)
this.ctx.setLineCap(lineCap)
this.ctx.stroke()
}
// 绘制完成
this.ctx.draw(false, () => {
this.triggerEvent('change', { value: this.data.currentAngle })
})
},
/**
* 开始动画
*/
animate(c, d, e) {
const now = Date.now()
const f = now - c < 1 ? 1 : now - c
const { animate, speed, startAngle, endAngle } = this.data
const isEnd = !e && 1000 * this.data.currentAngle <= Math.floor(1000 * endAngle) || e && 1000 * this.data.currentAngle >= Math.floor(1000 * endAngle)
if (animate && c - d < 1.05 * speed && isEnd) {
const value = easeInOutCubic((c - d) / f, startAngle, endAngle - startAngle, speed / f)
const currentAngle = value < 0 ? 0 : value
c = Date.now()
this.safeSetData({ currentAngle }, () => {
this.draw(currentAngle !== 0)
this.timer = setTimeout(() => this.animate(c, d, e), 1000 / 60)
})
} else {
this.safeSetData({ currentAngle: endAngle }, () => this.draw(endAngle !== 0))
}
},
/**
* 清除定时器
*/
clearTimer() {
if (this.timer) {
clearTimeout(this.timer)
this.timer = null
}
},
},
attached() {
this.updateStyle()
if (this.data.percent === 0) {
this.draw(false)
}
},
detached() {
this.ctx = null
this.clearTimer()
},
})