当前位置:   article > 正文

Taro引入echarts【兼容多端小程序(飞书/微信/支付宝小程序)】_taro echarts

taro echarts

近期接到公司新需求,开发飞书小程序,并且原型中含有大量的图表,本想使用飞书内置图表组件 ——
chart-space,但官方表示已经停止维护了,无奈之下,只能另寻他路,于是乎,图表之王(文档最完整,api最透彻,社区最活跃)echarts映入我脑海中,决定就是你了!

下载echarts代码

进入echarts下载地址,点击在线定制
在这里插入图片描述
勾选你需要的图表和组件,点击下载在这里插入图片描述
在这里插入图片描述

echarts引入项目

在开发项目中,components中创建Echarts文件,放入下载后的js包,并在该目录下创建wx-canvas.js,编写canvas构造函数代码,如下

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
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

目录结构如下图
在这里插入图片描述
在Echarts文件中,创建ec-canvas.vue文件,创建canvas对象

<template>
  <!--  // 多个echarts时将canvasId作为唯一标识,动态获取canvasId用于多个echarts可同时显示    -->
  <canvas
    type="2d"
    class="ec-canvas"
    :id="canvasId"
    :canvas-id="canvasId"
    @touchStart="touchStart"
    @touchMove="touchMove"
    @touchEnd="touchEnd"
  ></canvas>
</template>

<script lang="js">
import Taro from "@tarojs/taro";
import WxCanvas from "@/components/Echarts/wx-canvas";
import * as echarts from "@/components/Echarts/echarts.min";

export default {
  name: "EcCanvas",
  props: {
    canvasId: {
      type: String,
      default: "",
    },
    chartData: {
      type: Array,
      default: () => [],
    },
    option: {
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
    }
  },
  watch: {
    chartData: function() {
      console.log('chartData', this.chartData)
      this.handleCreate()
    },
  },
  mounted() {
    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;
        }
      }
    })
  },
  methods: {
    init(callback) {
      this.initByNewWay(callback);
    },
    // eslint-disable-next-line complexity
    handleCreate() {
      const option = this.option
      const v = this
      v.init((canvas, width, height, canvasDpr) => {
        const chart = echarts.init(canvas, null, {
            width: width,
            height: height,
            devicePixelRatio: canvasDpr,
        })
        canvas.setChart(chart);
        chart.setOption(option);
        chart.on('click', (params) => {
          console.log('params', params)
          this.$emit('clickEChartItem', params)
        });
        return chart;
      })
    },
    initByNewWay(callback) {
      const query = Taro.createSelectorQuery();

      query
        .select(`#${this.canvasId}`) // 根据canvasId动态获取不同的echarts图表
        .fields({
          // node: true, // 飞书里面没有node
          dataset: true,
          size: true,
        })
        // eslint-disable-next-line complexity
        .exec(res => {
          console.log('query res', res)
          // eslint-disable-next-line eqeqeq
          // if (!res || res.length == 0 || res[0] == null || res[0].node == null) {
          //   console.error('未获取到canvas的dom节点,请确认在页面渲染完成后或节点,taro中页面渲染完成的生命周期是useReady');
          //   return
          // }
          // const canvasNode = res[0].node;
          // this.canvasNode = canvasNode;
          console.log(11,this.option.height);
          const canvasDpr = Taro.getSystemInfoSync().pixelRatio;
          const canvasWidth = res[0].width;
          const canvasHeight = res[0].height;

          // const ctx = canvasNode.getContext("2d");
          const ctx = tt.createCanvasContext(this.canvasId)
          console.log('ctx', ctx)
          // const canvas = new WxCanvas(ctx, this.canvasId, true, canvasNode) // 不给canvasNode也可以
          const canvas = new WxCanvas(ctx, this.canvasId, true);
          echarts.setCanvasCreator(() => {
            return canvas;
          });

          this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
        });
    },
    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,
        });
        handler.dispatch("mousemove", {
          zrX: touch.x,
          zrY: touch.y,
        });
        handler.processGesture(this.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,
        });
        handler.processGesture(this.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,
        });
        handler.dispatch("click", {
          zrX: touch.x,
          zrY: touch.y,
        });
        handler.processGesture(this.wrapTouch(e), "end");
      }
    },
    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;
    },
  },
};
</script>

<style>
.ec-canvas {
  width: 100%;
  height: 100%;
  min-height: 208px;
  flex: 1;
}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179

二次封装echarts组件

使用创建的echart组件,稍微进行二次封装,大佬可以用自己的方法封装更好的,我的思路是,页面只要给echarts传入不同option,就可以进行渲染,代码如下:

// eChart.vue
<template>
  <EcCanvas :ref="canvasId" :canvasId="canvasId" :option="option"></EcCanvas>
</template>

<script>
import EcCanvas from './ec-canvas.vue'
import { watch, ref, toRefs } from 'vue'

export default {
  components: {
    EcCanvas,
  },
  props: {
    canvasId: {
      type: String,
      default: () => '',
    },
    option: {
      type: Object,
      default: () => {},
    },
  },
  watch: {
    option: {
      handler(value) {
        this.$nextTick(() => { // 没有下一帧会出现无法渲染的问题 页面没有挂载完成
          this.$refs[this.canvasId] && this.$refs[this.canvasId].handleCreate()
        })
      },
      deep: true,
    },
  },
}
</script>

<style lang="scss">
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

使用echarts组件渲染页面

<template>
<view class="chart">
            <e-chart canvasId="canvasId" :option="option"></e-chart>
          </view>
 </template>
 
 <script>
 import eChart from '@/componets/Echart/eChart.vue'
 import { reactive}  from 'vue'

 export default {
	components: { eChart },
  setup() {
  	const state =  reactive({
  		option: {}
  	})
 onMounted(async () => {
     // 模拟在挂载后,进行数据请求,这边我直接对option赋值
     // 数据来源于echarts官方实例中,最基础的柱状图
     state.option = {
		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 {
      ...toRefs(state),
    }
  }
}
 </script>
<style>
.chart {
        width: 100%;
        height: 300px;
    }
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

数据与图不符
最后,需要进行各种各样的渲染与修改,可直接参看echarts官网
希望此文对大家有帮助,也请大佬不吝赐教~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/874121
推荐阅读
相关标签
  

闽ICP备14008679号