| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- const PI_3_2 = Math.PI * 1.5;
- const PI_1_2 = Math.PI * 0.5;
- const mergeProps = function(newVal, oldVal) {
- return { ...oldVal,
- ...newVal
- }
- }
- const defaultObjectProps = {
- indicatorTextStyle: {
- type: Object,
- value: {
- show: false,
- size: 12,
- color: '#666',
- text: ''
- }
- },
- indicatorValueStyle: {
- type: Object,
- value: {
- show: false,
- size: 18,
- color: '#4575e8'
- }
- },
- indicatorCircleStyle: {
- type: Object,
- value: {
- show: false,
- bgColor: '#00b800',
- r: 9,
- borderRadius: 3,
- borderColor: '#fff'
- }
- },
- scaleTextStyle: {
- type: Object,
- value: {
- show: false,
- size: 16,
- color: '#f0f0f0'
- }
- }
- }
- // components/gauge.js
- Component({
- /**
- * 组件的属性列表
- */
- properties: {
- width: {
- type: Number,
- value: 750
- },
- height: {
- type: Number,
- value: 350
- },
- gaugeid: {
- type: String,
- value: 'gauge' + Math.random()
- },
- r: {
- type: Number,
- value: 95
- },
- startAngle: {
- type: Number,
- // value: 90 / 90 * Math.PI,
- value: 80 / 90 * Math.PI,
- },
- endAngle: {
- type: Number,
- // value: 180 / 90 * Math.PI,
- value: 10 / 90 * Math.PI,
- },
- indicatorBgColor: {
- type: [Array, String],
- value: [{
- progress: 0,
- value: null
- },
- {
- progress: 0.6,
- value: null
- },
- {
- progress: 1,
- value: null
- }
- ]
- },
- bgColor: {
- type: String,
- value: null,
- },
- bgWidth: {
- type: Number,
- value: 15,
- },
- min: {
- type: Number,
- value: 0,
- },
- max: {
- type: Number,
- value: 100,
- },
- value: {
- type: Number,
- value: null,
- observer: function(newVal, oldVal, changedPath) {
- this.setData({
- value: newVal
- },
- function() {
- this.drawGauge(this.canvasId, this.x, this.y)
- }
- )
- }
- },
- animateMsec: {
- type: Number,
- value: 0,
- },
- indicatorText: {
- type: String,
- value: ''
- },
- scale: {
- type: Array,
- value: [
- 0, 100
- ]
- },
- ...defaultObjectProps
- },
- /**
- * 组件的初始数据
- */
- data: {
- currentValue: 0
- },
- /**
- * 组件的方法列表
- */
- methods: {
- getPoint: function(x, y, r, angle) {
- const x1 = x + r * Math.cos(angle);
- const y1 = y + r * Math.sin(angle);
- return {
- x: x1,
- y: y1
- }
- // }
- },
- /**
- * 绘制圆圈
- * @params {CanvasContext} canvas context
- * @params {Object} 组件配置文件
- */
- _drawCircle: function(ctx, cfg) {
- ctx.beginPath()
- const config = cfg
- const innerCircleRadius = config.r - config.bgWidth;
- // ctx.moveTo(config.x,config.y)
- // 外圈
- ctx.arc(config.x, config.y, config.r, config.startAngle, config.endAngle)
- // 外圈结束坐标
- const outEndPoint = this.getPoint(config.x, config.y, config.r, config.endAngle)
- // 内圈结束坐标
- const innerEndPoint = this.getPoint(config.x, config.y, innerCircleRadius, config.endAngle)
- // 结束位置小圆圈坐标
- const endCirclePoint = {
- x: (outEndPoint.x + innerEndPoint.x) / 2,
- y: (outEndPoint.y + innerEndPoint.y) / 2,
- }
- // 结束位置圆弧
- ctx.arc(endCirclePoint.x, endCirclePoint.y, config.bgWidth / 2, config.endAngle, config.endAngle + Math.PI)
- // 内圈圆弧
- ctx.arc(config.x, config.y, innerCircleRadius, config.endAngle, config.startAngle, true)
- // 外圈开始位置坐标
- const outStartPoint = this.getPoint(config.x, config.y, config.r, config.startAngle)
- // 内圈开始位置坐标
- const innerStartPoint = this.getPoint(config.x, config.y, innerCircleRadius, config.startAngle)
- // 开始位置小圆圈坐标
- const startCirclePoint = {
- x: (outStartPoint.x + innerStartPoint.x) / 2,
- y: (outStartPoint.y + innerStartPoint.y) / 2,
- }
- // 开始位置小圆圈坐标
- // 开始位置圆弧
- ctx.moveTo(outStartPoint.x, outStartPoint.y)
- ctx.arc(startCirclePoint.x, startCirclePoint.y, config.bgWidth / 2, config.startAngle, config.startAngle - Math.PI, true)
- // 设置填充渐变色
- // 当背景颜色是数组时,设置为渐变色
- const {
- backgroundColor
- } = config
- if (Array.isArray(backgroundColor)) {
- const fillGrd = ctx.createLinearGradient(innerStartPoint.x, innerStartPoint.y, innerEndPoint.x, innerEndPoint.y);
- const {
- length
- } = config.backgroundColor
- for (let i = 0; i < length; i++) {
- const bg = backgroundColor[i]
- fillGrd.addColorStop(bg.progress, bg.value || '#32d900')
- }
- ctx.setFillStyle(fillGrd)
- } else {
- ctx.setFillStyle(config.backgroundColor)
- }
- ctx.closePath()
- ctx.fill()
- },
- _animate: function(func) {
- const {
- animateMsec
- } = this.data
- if (animateMsec === 0) {
- return func(1)
- }
- const startTime = Date.now();
- const endTime = startTime + animateMsec;
- // const traceTime = endTime - startTime;
- let timeOutId;
- const animateFunc = function() {
- const curTime = Date.now();
- const percent = (curTime - startTime) / animateMsec;
- if (percent >= 1) {
- func(1);
- // timeOutId && clearTimeout(timeOutId);
- return
- }
- func(percent)
- timeOutId = setTimeout(function() {
- animateFunc();
- }, 16)
- }
- animateFunc();
- },
- _drawBackground: function(ctx, config) {
- const newCfg = {
- ...config,
- backgroundColor: config.bgColor
- }
- delete newCfg.bgColor
- this._drawCircle(ctx, newCfg)
- },
- /**
- * 绘制指示器圆圈
- */
- _drawIndicator: function(ctx, value = 0, config) {
- let {
- startAngle,
- endAngle,
- min,
- max
- } = config
- if (endAngle <= startAngle) {
- endAngle += 2 * Math.PI
- }
- const currentAngle = (value / (max - min)) * (endAngle - startAngle) + startAngle
- const newCfg = {
- ...config,
- backgroundColor: config.indicatorBgColor,
- endAngle: currentAngle,
- }
- this._drawCircle(ctx, newCfg)
- },
- _drawIndicatorValue: function(ctx, text, config) {
- const {
- x,
- y,
- indicatorValueStyle
- } = config
- const {
- size = 25,
- color = '#1AAD16'
- } = indicatorValueStyle
- ctx.setFillStyle(color)
- // 以下精度可以加接口控制
- ctx.setFontSize(size)
- ctx.setTextAlign('center')
- ctx.fillText((!text||text==0)?'':Number.prototype.toFixed.call(text, 0), x, y)
- },
- _drawIndicatorText: function(ctx, config) {
- const {
- x,
- y,
- indicatorTextStyle
- } = config
- const {
- size,
- color,
- text
- } = mergeProps(indicatorTextStyle, defaultObjectProps.indicatorTextStyle.value)
- ctx.save()
- ctx.setFillStyle(color)
- // 以下精度可以加接口控制
- ctx.setFontSize(size)
- ctx.setTextAlign('center')
- const mergedIndicatorValueStyle = mergeProps(config.indicatorValueStyle, defaultObjectProps.indicatorValueStyle.value)
- ctx.fillText(text, x, y - 5 - mergedIndicatorValueStyle.size)
- },
- _drawIndicatorScale: function(ctx, config) {
- const {
- bgWidth,
- scale,
- r,
- x,
- y,
- min,
- max,
- scaleTextStyle
- } = config;
- let {
- startAngle,
- endAngle,
- } = config
- if (endAngle <= startAngle) {
- endAngle += Math.PI * 2
- }
- const len = scale.length;
- const {
- size,
- color
- } = mergeProps(scaleTextStyle, defaultObjectProps.scaleTextStyle.value);
- ctx.setFillStyle(color)
- // 以下精度可以加接口控制
- ctx.setFontSize(size)
- ctx.setTextAlign('center')
- for (let i = 0; i < len; i++) {
- const value = scale[i]
- let angle = (value / (max - min)) * (endAngle - startAngle) + startAngle
- if (angle >= Math.PI * 2) {
- angle = angle - Math.PI * 2
- }
- const point = this.getPoint(x, y, r - bgWidth - size - 5, angle);
- ctx.save()
- ctx.translate(point.x, point.y)
- const rotateDegrees = angle >= PI_3_2 ? (angle - PI_3_2) : (angle + PI_1_2);
- ctx.rotate(rotateDegrees)
- ctx.fillText(value, 0, 0)
- ctx.restore()
- }
- },
- /**
- * 绘制终点圆圈指示器
- */
- _drawIndicatorCircle: function(ctx, value = 0, config) {
- ctx.beginPath()
- const {
- indicatorCircleStyle,
- x,
- y,
- r,
- max,
- min,
- bgWidth
- } = config;
- let {
- startAngle,
- endAngle,
- } = config
- if (endAngle <= startAngle) {
- endAngle += 2 * Math.PI
- }
- const currentAngle = (value / (max - min)) * (endAngle - startAngle) + startAngle
- const outPoint = this.getPoint(x, y, r, currentAngle);
- const innerPoint = this.getPoint(x, y, r - bgWidth, currentAngle);
- const point = {
- x: (outPoint.x + innerPoint.x) / 2,
- y: (outPoint.y + innerPoint.y) / 2,
- }
- ctx.moveTo(point.x, point.y)
- let mergedIndicatorCircleStyle
- const {
- bgColor,
- borderRadius,
- borderColor
- } = mergedIndicatorCircleStyle = mergeProps(indicatorCircleStyle, defaultObjectProps.indicatorCircleStyle.value)
- // 边框
- if (!isNaN(borderRadius) && borderRadius !== 0) {
- const outR = mergedIndicatorCircleStyle.r + borderRadius
- ctx.arc(point.x, point.y, outR, 0, 2 * Math.PI);
- if (Array.isArray(borderColor)) {
- const fillGrd = ctx.createCircularGradient(point.x, point.y, outR);
- const {
- length
- } = borderColor
- for (let i = 0; i < length; i++) {
- const bg = borderColor[i]
- fillGrd.addColorStop(bg.progress, bg.value || '#32d900')
- }
- ctx.setFillStyle(fillGrd)
- } else {
- ctx.setFillStyle(borderColor)
- }
- ctx.closePath()
- ctx.fill()
- }
- // 内圈指示器
- ctx.beginPath()
- ctx.arc(point.x, point.y, mergedIndicatorCircleStyle.r, 0, 2 * Math.PI);
- ctx.setFillStyle(bgColor)
- ctx.closePath()
- ctx.fill()
- },
- drawGauge: function(canvasId, x, y) {
- const ctx = wx.createCanvasContext(canvasId, this);
- this.ctx = ctx;
- const config = {
- x,
- y,
- ...this.properties
- }
- this._animate(this._drawGaugeWithAnimate.bind(this, config))
- },
- _drawGaugeWithAnimate: function(config, percent) {
- const {
- ctx
- } = this
- const {
- value,
- min
- } = this.data;
- const {
- indicatorTextStyle,
- indicatorValueStyle,
- indicatorCircleStyle,
- scaleTextStyle
- } = this.properties
- const animateValue = min + (value - min) * percent;
- this._drawBackground(ctx, config)
- this._drawIndicator(ctx, animateValue, config)
- if (scaleTextStyle.show) {
- this._drawIndicatorScale(ctx, config)
- }
- if (indicatorTextStyle.show) {
- this._drawIndicatorText(ctx, config)
- }
- if (indicatorCircleStyle.show) {
- this._drawIndicatorCircle(ctx, animateValue, config)
- }
- if (indicatorValueStyle.show) {
- this._drawIndicatorValue(ctx, animateValue, config)
- }
- ctx.draw()
- }
- },
- ready: function(opt) {
- const canvasId = 'gauge_' + this.data.gaugeid;
- const that = this;
- let x = 187;
- let y = 187;
- wx.getSystemInfo({
- success: function(res) {
- const {
- screenWidth,
- screenHeight
- } = res;
- const rpxTopx = screenWidth / 750;
- const {
- width,
- height
- } = that.data
- that.x = x = width * rpxTopx / 2;
- that.y = y = height * rpxTopx / 2;
- that.canvasId = canvasId
- that.drawGauge(canvasId, x, y)
- },
- })
- }
- })
|