本文目的:通过分包的方式,尽可能在微信小程序中使用最新的echarts。
当然你也可以直接使用现成的uchart或者市场里别人封好的echarts.
ec-canvas
文件夹以及下属文件,在uniapp项目中与pages同级的地方创建wxcomponents
文件夹,将复制的文件夹放于该文件夹下。wxcomponents
文件名不可更改,参考:Uniapp小程序自定义组件支持pages.json
文件,往globalStyle
中添加"usingComponents": {"ec-canvas": "/wxcomponents/ec-canvas/ec-canvas"}
。ec-canvas
下生成一个ec-canvas.vue
,我自己试过来跑在浏览器上(h5)会生成,直接跑微信小程序不会生成。ec-canvas
下已经包含echarts文件,但版本可能不是最新的,想换新的可以去echarts github下载,至此准备工作完成。ec-canvas.vue
中的代码
canvas
标签中所绑定的事件,视methods中的方法名而定
global['__wxVueOptions'] = {components:{}}
和global['__wxRoute'] = 'ec-canvas/ec-canvas'
echart.js
文件的路径与名称是否引用正确,下载过来没改名可能是echarts.min.js
export default global['__wxComponents']['ec-canvas/ec-canvas']
改为export default {}
,然后将Component
中的键按Vue的原本语法一一复制:
init
方法加上async
,在if (isUseNewCanvas)
上加上await this.$nextTick()
,否则具体的init方法中获取node会变成null。this.data
替换为this
.setData
,将唯一一条this.setData({ isUseNewCanvas })
改为this.isUseNewCanvas = isUseNewCanvas
ec-canvas.vue
参考:<template>
<uni-shadow-root class="ec-canvas-ec-canvas">
<canvas
v-if="isUseNewCanvas"
type="2d"
class="ec-canvas"
:canvas-id="canvasId"
@init="init"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
</canvas>
<canvas
v-else
class="ec-canvas"
:canvas-id="canvasId"
@init="init"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
</canvas>
</uni-shadow-root>
</template>
<script>
import WxCanvas from './wx-canvas'
import * as echarts from './echarts'
let ctx
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
}
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
}
export default {
props: {
canvasId: {
type: String,
value: 'ec-canvas',
},
ec: {
type: Object,
},
forceUseOldCanvas: {
type: Boolean,
value: false,
},
},
data() {
return {
isUseNewCanvas: false,
}
},
onReady: function () {
// 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.ec) {
console.warn(
'组件需绑定 ec 变量,例: +
'canvas-id="mychart-bar" ec="{{ ec }}"> '
)
return
}
if (!this.ec.lazyLoad) {
this.init()
}
},
methods: {
init: async function (callback) {
const version = wx.getSystemInfoSync().SDKVersion
const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0
const forceUseOldCanvas = this.forceUseOldCanvas
const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas
this.isUseNewCanvas = isUseNewCanvas
if (forceUseOldCanvas && canUseNewCanvas) {
console.warn('开发者强制使用旧canvas,建议关闭')
}
await this.$nextTick()
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.canvasId, this)
const canvas = new WxCanvas(ctx, this.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.ec &&
typeof this.ec.onInit === 'function'
) {
this.chart = this.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.canvasId, true, canvasNode)
echarts.setCanvasCreator(() => {
return canvas
})
if (typeof callback === 'function') {
this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
} else if (
this.ec &&
typeof this.ec.onInit === 'function'
) {
this.chart = this.ec.onInit(
canvas,
canvasWidth,
canvasHeight,
canvasDpr
)
} else {
this.triggerEvent('init', {
canvas: canvas,
width: canvasWidth,
height: canvasHeight,
dpr: canvasDpr,
})
}
})
},
canvasToTempFilePath(opt) {
if (this.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.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')
}
},
},
}
</script>
<style scoped>
.ec-canvas {
width: 100%;
height: 100%;
}
</style>
ec-canvas.vue
、wx-canvas.js
、echarts.js
复制到需要使用的分包中,三个文件需在同一目录下,再在组件中引用即可,注意引用组件与echarts的路径:
wxcomponents
文件夹删除以及pages中对应的地方,否则主包体积会变大。<template>
<view style="height: 500rpx">
<ec-canvas
ref="canvasRef"
style="height:100%;width:100%;"
canvas-id="mychart-bar"
:ec="{ onInit: initChart }"
/>
</view>
</template>
<script>
import * as echarts from './components/echarts.min.js'
import EcCanvas from './components/ec-canvas'
let chartInstance
export default {
components: {
EcCanvas,
},
data() {
return {}
},
mounted() {
setTimeout(() => {
this.setOption({
series: [
{
data: [12, 20, 15, 8, 7, 11, 13],
type: 'bar',
},
],
})
},3000)
},
methods: {
initChart(canvas, width, height, dpr) {
chartInstance = echarts.init(canvas, null, {
width,
height,
devicePixelRatio: dpr, // 像素
})
canvas.setChart(chartInstance)
chartInstance.setOption({
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: {
type: 'value',
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar',
},
],
})
return chartInstance
},
// 需要更新图表数据时调用
setOption(data) {
if(!chartInstance) return
chartInstance.setOption(data)
}
},
}
</script>
ec-canvas 进一步封装以及同时适配H5,参考我的下一篇文章:uniapp echarts 适配H5与微信小程序