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.
233 lines
4.8 KiB
233 lines
4.8 KiB
import { |
|
BLUE, |
|
WHITE |
|
} from '../common/color'; |
|
import { |
|
VantComponent |
|
} from '../common/component'; |
|
import { |
|
getSystemInfoSync |
|
} from '../common/utils'; |
|
import { |
|
isObj |
|
} from '../common/validator'; |
|
import { |
|
canIUseCanvas2d |
|
} from '../common/version'; |
|
import { |
|
adaptor |
|
} from './canvas'; |
|
|
|
function format(rate) { |
|
return Math.min(Math.max(rate, 0), 100); |
|
} |
|
const PERIMETER = 2 * Math.PI; |
|
const BEGIN_ANGLE = -Math.PI / 2; |
|
const STEP = 1; |
|
VantComponent({ |
|
props: { |
|
text: String, |
|
lineCap: { |
|
type: String, |
|
value: 'round', |
|
}, |
|
value: { |
|
type: Number, |
|
value: 0, |
|
observer: 'reRender', |
|
}, |
|
speed: { |
|
type: Number, |
|
value: 50, |
|
}, |
|
size: { |
|
type: Number, |
|
value: 100, |
|
observer() { |
|
this.drawCircle(this.currentValue); |
|
}, |
|
}, |
|
fill: String, |
|
layerColor: { |
|
type: String, |
|
value: WHITE, |
|
}, |
|
color: { |
|
type: null, |
|
value: BLUE, |
|
observer() { |
|
this.setHoverColor().then(() => { |
|
this.drawCircle(this.currentValue); |
|
}); |
|
}, |
|
}, |
|
type: { |
|
type: String, |
|
value: '', |
|
}, |
|
strokeWidth: { |
|
type: Number, |
|
value: 4, |
|
}, |
|
clockwise: { |
|
type: Boolean, |
|
value: true, |
|
}, |
|
canvasId: { |
|
type: String, |
|
value: '' |
|
} |
|
}, |
|
data: { |
|
hoverColor: BLUE, |
|
}, |
|
methods: { |
|
getContext() { |
|
const { |
|
type, |
|
size |
|
} = this.data; |
|
if (type === '' || !canIUseCanvas2d()) { |
|
const ctx = wx.createCanvasContext('van-circle', this); |
|
return Promise.resolve(ctx); |
|
} |
|
const dpr = getSystemInfoSync().pixelRatio; |
|
return new Promise((resolve) => { |
|
wx.createSelectorQuery() |
|
.in(this) |
|
.select('#van-circle') |
|
.node() |
|
.exec((res) => { |
|
const canvas = res[0].node; |
|
const ctx = canvas.getContext(type); |
|
if (!this.inited) { |
|
this.inited = true; |
|
canvas.width = size * dpr; |
|
canvas.height = size * dpr; |
|
ctx.scale(dpr, dpr); |
|
} |
|
resolve(adaptor(ctx)); |
|
}); |
|
}); |
|
}, |
|
setHoverColor() { |
|
const { |
|
color, |
|
size |
|
} = this.data; |
|
if (isObj(color)) { |
|
return this.getContext().then((context) => { |
|
if (!context) |
|
return; |
|
const LinearColor = context.createLinearGradient(size, 0, 0, 0); |
|
Object.keys(color) |
|
.sort((a, b) => parseFloat(a) - parseFloat(b)) |
|
.map((key) => LinearColor.addColorStop(parseFloat(key) / 100, color[key])); |
|
this.hoverColor = LinearColor; |
|
}); |
|
} |
|
this.hoverColor = color; |
|
return Promise.resolve(); |
|
}, |
|
presetCanvas(context, strokeStyle, beginAngle, endAngle, fill) { |
|
const { |
|
strokeWidth, |
|
lineCap, |
|
clockwise, |
|
size |
|
} = this.data; |
|
const position = size / 2; |
|
const radius = position - strokeWidth / 2; |
|
context.setStrokeStyle(strokeStyle); |
|
context.setLineWidth(strokeWidth); |
|
context.setLineCap(lineCap); |
|
context.beginPath(); |
|
context.arc(position, position, radius, beginAngle, endAngle, !clockwise); |
|
context.stroke(); |
|
if (fill) { |
|
context.setFillStyle(fill); |
|
context.fill(); |
|
} |
|
}, |
|
renderLayerCircle(context) { |
|
const { |
|
layerColor, |
|
fill |
|
} = this.data; |
|
this.presetCanvas(context, layerColor, 0, PERIMETER, fill); |
|
}, |
|
renderHoverCircle(context, formatValue) { |
|
const { |
|
clockwise |
|
} = this.data; |
|
// 结束角度 |
|
const progress = PERIMETER * (formatValue / 100); |
|
const endAngle = clockwise ? |
|
BEGIN_ANGLE + progress : |
|
3 * Math.PI - (BEGIN_ANGLE + progress); |
|
this.presetCanvas(context, this.hoverColor, BEGIN_ANGLE, endAngle); |
|
}, |
|
drawCircle(currentValue) { |
|
const { |
|
size |
|
} = this.data; |
|
this.getContext().then((context) => { |
|
if (!context) |
|
return; |
|
context.clearRect(0, 0, size, size); |
|
this.renderLayerCircle(context); |
|
const formatValue = format(currentValue); |
|
if (formatValue !== 0) { |
|
this.renderHoverCircle(context, formatValue); |
|
} |
|
context.draw(); |
|
}); |
|
}, |
|
reRender() { |
|
// tofector 动画暂时没有想到好的解决方案 |
|
const { |
|
value, |
|
speed |
|
} = this.data; |
|
if (speed <= 0 || speed > 1000) { |
|
this.drawCircle(value); |
|
return; |
|
} |
|
this.clearMockInterval(); |
|
this.currentValue = this.currentValue || 0; |
|
const run = () => { |
|
this.interval = setTimeout(() => { |
|
if (this.currentValue !== value) { |
|
if (Math.abs(this.currentValue - value) < STEP) { |
|
this.currentValue = value; |
|
} else if (this.currentValue < value) { |
|
this.currentValue += STEP; |
|
} else { |
|
this.currentValue -= STEP; |
|
} |
|
this.drawCircle(this.currentValue); |
|
run(); |
|
} else { |
|
this.clearMockInterval(); |
|
} |
|
}, 1000 / speed); |
|
}; |
|
run(); |
|
}, |
|
clearMockInterval() { |
|
if (this.interval) { |
|
clearTimeout(this.interval); |
|
this.interval = null; |
|
} |
|
}, |
|
}, |
|
mounted() { |
|
this.currentValue = this.data.value; |
|
this.setHoverColor().then(() => { |
|
this.drawCircle(this.currentValue); |
|
}); |
|
}, |
|
destroyed() { |
|
this.clearMockInterval(); |
|
}, |
|
}); |