From ff7cd3b7c00cb515f8b31eed0d079505967ee99b Mon Sep 17 00:00:00 2001 From: kola Date: Tue, 15 Oct 2024 16:56:51 +0800 Subject: [PATCH] add canvas --- src/echart/components/ec-canvas/ec-canvas.js | 267 +++++++++++++++++++++++++ src/echart/components/ec-canvas/ec-canvas.json | 4 + src/echart/components/ec-canvas/ec-canvas.wxml | 4 + src/echart/components/ec-canvas/ec-canvas.wxss | 5 + src/echart/components/ec-canvas/wx-canvas.js | 111 ++++++++++ 5 files changed, 391 insertions(+) create mode 100644 src/echart/components/ec-canvas/ec-canvas.js create mode 100644 src/echart/components/ec-canvas/ec-canvas.json create mode 100644 src/echart/components/ec-canvas/ec-canvas.wxml create mode 100644 src/echart/components/ec-canvas/ec-canvas.wxss create mode 100644 src/echart/components/ec-canvas/wx-canvas.js diff --git a/src/echart/components/ec-canvas/ec-canvas.js b/src/echart/components/ec-canvas/ec-canvas.js new file mode 100644 index 0000000..c97c3f9 --- /dev/null +++ b/src/echart/components/ec-canvas/ec-canvas.js @@ -0,0 +1,267 @@ +import WxCanvas from './wx-canvas'; + +let ctx; +let echarts + +function compareVersion(v1, v2) { + v1 = v1.split('.') + v2 = v2.split('.') + const len = Math.max(v1.length, v2.length) + + while (v1.length < len) { + v1.push('0') + } + while (v2.length < len) { + v2.push('0') + } + + for (let i = 0; i < len; i++) { + const num1 = parseInt(v1[i]) + const num2 = parseInt(v2[i]) + + if (num1 > num2) { + return 1 + } else if (num1 < num2) { + return -1 + } + } + return 0 +} + +Component({ + properties: { + canvasId: { + type: String, + value: 'ec-canvas' + }, + + ec: { + type: Object + }, + + forceUseOldCanvas: { + type: Boolean, + value: false + } + }, + + data: { + isUseNewCanvas: false + }, + + ready:async function () { + echarts = await require.async('../../resource/components/echart/echarts.js') + // Disable prograssive because drawImage doesn't support DOM as parameter + // See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html + echarts.registerPreprocessor(option => { + if (option && option.series) { + if (option.series.length > 0) { + option.series.forEach(series => { + series.progressive = 0; + }); + } + else if (typeof option.series === 'object') { + option.series.progressive = 0; + } + } + }); + + if (!this.data.ec) { + console.warn('组件需绑定 ec 变量,例:'); + return; + } + + if (!this.data.ec.lazyLoad) { + this.init(); + } + }, + + methods: { + init: async function (callback) { + echarts = await require.async('../../resource/components/echart/echarts.js') + const version = wx.getSystemInfoSync().SDKVersion + + const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0; + const forceUseOldCanvas = this.data.forceUseOldCanvas; + const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; + this.setData({ isUseNewCanvas }); + + if (forceUseOldCanvas && canUseNewCanvas) { + console.warn('开发者强制使用旧canvas,建议关闭'); + } + + if (isUseNewCanvas) { + // console.log('微信基础库版本大于2.9.0,开始使用'); + // 2.9.0 可以使用 + this.initByNewWay(callback); + } else { + const isValid = compareVersion(version, '1.9.91') >= 0 + if (!isValid) { + console.error('微信基础库版本过低,需大于等于 1.9.91。' + + '参见:https://github.com/ecomfe/echarts-for-weixin' + + '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82'); + return; + } else { + console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能'); + this.initByOldWay(callback); + } + } + }, + + initByOldWay(callback) { + // 1.9.91 <= version < 2.9.0:原来的方式初始化 + ctx = wx.createCanvasContext(this.data.canvasId, this); + const canvas = new WxCanvas(ctx, this.data.canvasId, false); + + echarts.setCanvasCreator(() => { + return canvas; + }); + // const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr + const canvasDpr = 1 + var query = wx.createSelectorQuery().in(this); + query.select('.ec-canvas').boundingClientRect(res => { + if (typeof callback === 'function') { + this.chart = callback(canvas, res.width, res.height, canvasDpr); + } + else if (this.data.ec && typeof this.data.ec.onInit === 'function') { + this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr); + } + else { + this.triggerEvent('init', { + canvas: canvas, + width: res.width, + height: res.height, + canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init + }); + } + }).exec(); + }, + + initByNewWay(callback) { + // version >= 2.9.0:使用新的方式初始化 + const query = wx.createSelectorQuery().in(this) + query + .select('.ec-canvas') + .fields({ node: true, size: true }) + .exec(res => { + const canvasNode = res[0].node + this.canvasNode = canvasNode + + const canvasDpr = wx.getSystemInfoSync().pixelRatio + const canvasWidth = res[0].width + const canvasHeight = res[0].height + + const ctx = canvasNode.getContext('2d') + + const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode) + echarts.setCanvasCreator(() => { + return canvas + }) + + if (typeof callback === 'function') { + this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr) + } else if (this.data.ec && typeof this.data.ec.onInit === 'function') { + this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr) + } else { + this.triggerEvent('init', { + canvas: canvas, + width: canvasWidth, + height: canvasHeight, + dpr: canvasDpr + }) + } + }) + }, + canvasToTempFilePath(opt) { + if (this.data.isUseNewCanvas) { + // 新版 + const query = wx.createSelectorQuery().in(this) + query + .select('.ec-canvas') + .fields({ node: true, size: true }) + .exec(res => { + const canvasNode = res[0].node + opt.canvas = canvasNode + wx.canvasToTempFilePath(opt) + }) + } else { + // 旧的 + if (!opt.canvasId) { + opt.canvasId = this.data.canvasId; + } + ctx.draw(true, () => { + wx.canvasToTempFilePath(opt, this); + }); + } + }, + + touchStart(e) { + if (this.chart && e.touches.length > 0) { + var touch = e.touches[0]; + var handler = this.chart.getZr().handler; + handler.dispatch('mousedown', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {} + }); + handler.dispatch('mousemove', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {} + }); + handler.processGesture(wrapTouch(e), 'start'); + } + }, + + touchMove(e) { + if (this.chart && e.touches.length > 0) { + var touch = e.touches[0]; + var handler = this.chart.getZr().handler; + handler.dispatch('mousemove', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {} + }); + handler.processGesture(wrapTouch(e), 'change'); + } + }, + + touchEnd(e) { + if (this.chart) { + const touch = e.changedTouches ? e.changedTouches[0] : {}; + var handler = this.chart.getZr().handler; + handler.dispatch('mouseup', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {} + }); + handler.dispatch('click', { + zrX: touch.x, + zrY: touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {} + }); + handler.processGesture(wrapTouch(e), 'end'); + } + } + } +}); + +function wrapTouch(event) { + for (let i = 0; i < event.touches.length; ++i) { + const touch = event.touches[i]; + touch.offsetX = touch.x; + touch.offsetY = touch.y; + } + return event; +} diff --git a/src/echart/components/ec-canvas/ec-canvas.json b/src/echart/components/ec-canvas/ec-canvas.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/src/echart/components/ec-canvas/ec-canvas.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/src/echart/components/ec-canvas/ec-canvas.wxml b/src/echart/components/ec-canvas/ec-canvas.wxml new file mode 100644 index 0000000..88826d9 --- /dev/null +++ b/src/echart/components/ec-canvas/ec-canvas.wxml @@ -0,0 +1,4 @@ + + + + diff --git a/src/echart/components/ec-canvas/ec-canvas.wxss b/src/echart/components/ec-canvas/ec-canvas.wxss new file mode 100644 index 0000000..5307021 --- /dev/null +++ b/src/echart/components/ec-canvas/ec-canvas.wxss @@ -0,0 +1,5 @@ +.ec-canvas { + width: 100%; + height: 100%; + z-index: 1; +} diff --git a/src/echart/components/ec-canvas/wx-canvas.js b/src/echart/components/ec-canvas/wx-canvas.js new file mode 100644 index 0000000..6c7c90b --- /dev/null +++ b/src/echart/components/ec-canvas/wx-canvas.js @@ -0,0 +1,111 @@ +export default class WxCanvas { + constructor(ctx, canvasId, isNew, canvasNode) { + this.ctx = ctx; + this.canvasId = canvasId; + this.chart = null; + this.isNew = isNew + if (isNew) { + this.canvasNode = canvasNode; + } + else { + this._initStyle(ctx); + } + + // this._initCanvas(zrender, ctx); + + this._initEvent(); + } + + getContext(contextType) { + if (contextType === '2d') { + return this.ctx; + } + } + + // canvasToTempFilePath(opt) { + // if (!opt.canvasId) { + // opt.canvasId = this.canvasId; + // } + // return wx.canvasToTempFilePath(opt, this); + // } + + setChart(chart) { + this.chart = chart; + } + + addEventListener() { + // noop + } + + attachEvent() { + // noop + } + + detachEvent() { + // noop + } + + _initCanvas(zrender, ctx) { + zrender.util.getContext = function () { + return ctx; + }; + + zrender.util.$override('measureText', function (text, font) { + ctx.font = font || '12px sans-serif'; + return ctx.measureText(text); + }); + } + + _initStyle(ctx) { + ctx.createRadialGradient = () => { + return ctx.createCircularGradient(arguments); + }; + } + + _initEvent() { + this.event = {}; + const eventNames = [{ + wxName: 'touchStart', + ecName: 'mousedown' + }, { + wxName: 'touchMove', + ecName: 'mousemove' + }, { + wxName: 'touchEnd', + ecName: 'mouseup' + }, { + wxName: 'touchEnd', + ecName: 'click' + }]; + eventNames.forEach(name => { + this.event[name.wxName] = e => { + const touch = e.touches[0]; + this.chart.getZr().handler.dispatch(name.ecName, { + zrX: name.wxName === 'tap' ? touch.clientX : touch.x, + zrY: name.wxName === 'tap' ? touch.clientY : touch.y, + preventDefault: () => {}, + stopImmediatePropagation: () => {}, + stopPropagation: () => {} + }); + }; + }); + } + + set width(w) { + if (this.canvasNode) this.canvasNode.width = w + } + set height(h) { + if (this.canvasNode) this.canvasNode.height = h + } + + get width() { + if (this.canvasNode) + return this.canvasNode.width + return 0 + } + get height() { + if (this.canvasNode) + return this.canvasNode.height + return 0 + } +}