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
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()
|
|
},
|
|
})
|
|
|