uniapp使用echarts(支持微信小程序canvas2d渲染)

本文档提供了一种在微信小程序中成功运行ECharts的详细教程,包括下载指定版本的ECharts,创建并配置uni-app的Echarts.vue组件,处理不同版本微信基础库的兼容问题,以及如何在myecharts.vue中设置ECharts配置项。通过这个教程,开发者可以解决在小程序中使用ECharts遇到的常见问题,如依赖下载、初始化图表、触摸事件处理等。
摘要由CSDN通过智能技术生成

相信众多朋友们找个靠谱能跑的微信小程序echarts的demo比较费劲,各种报错,话不多说,按照流程走包你跑起来

1.下载echarts

首先博主使用的是vuecli创建的uniapp,所以可以直接npm 下载包,如果是hbuildX创建项目的话自己去官网搞个echarts.js包下来即可,下载命令如下(必须要5.2.2版本),最新版会报错,粗略看了下是因为没有用?.链式操作符导致只能在h5用

npm i echarts@5.2.2 -S  

下载完成后在plugins/echarts目录下新建echarts.vue文件,代码如下

<template>
  <canvas
    v-if="canvasId"
    :type="forceUseOldCanvas ? '' : '2d'"
    class="ec-canvas"
    :id="canvasId"
    :canvasId="canvasId"
    @touchstart="touchStart"
    @touchmove="touchMove"
    @touchend="touchEnd"
  >
  </canvas>
</template>

<script>
import WxCanvas2 from './wx-canvas2'
import WxCanvas from './wx-canvas'
import * as echarts from 'echarts/dist/echarts.min'
// import * as echarts from './echarts.min.js'
function compareVersion (v1s, v2s) {
  const v1 = v1s.split('.')
  const v2 = v2s.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 += 1) {
    const num1 = parseInt(v1[i])
    const num2 = parseInt(v2[i])

    if (num1 > num2) {
      return 1
    } else if (num1 < num2) {
      return -1
    }
  }
  return 0
}
export default {
  props: {
    // echarts: {
    //   required: true,
    //   type: Object,
    //   default () {
    //     return null
    //   }
    // },
    forceUseOldCanvas: {
      type: Boolean,
      default: false
    },
    canvasId: {
      type: String,
      default: 'ec-canvas'
    },
    onInit: {
      type: Function,
      default: null
    },
    lazyLoad: {
      type: Boolean,
      default: false
    },
    disableTouch: {
      type: Boolean,
      default: false
    },
    throttleTouch: {
      type: Boolean,
      default: false
    }
  },

  // onReady () {
  //   if (!echarts) {
  //     console.warn('组件需echarts 变量')
  //     return
  //   }

  //   if (!this.lazyLoad) {
  //     this.init()
  //   }
  // },
  // #ifdef H5
  mounted () {
    if (!this.lazyLoad) this.init()
  },
  // #endif
  // #ifndef H5
  onReady () {
    if (!this.lazyLoad) this.init()
  },
  // #endif
  methods: {
    init (callback) {
      const version = wx.getSystemInfoSync().SDKVersion

      const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0
      this.isUseNewCanvas = canUseNewCanvas && !this.forceUseOldCanvas

      if (this.forceUseOldCanvas && canUseNewCanvas) {
        console.warn('开发者强制使用旧canvas,建议关闭')
      }

      if (this.isUseNewCanvas) {
        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'
          )
        } else {
          console.warn(
            '建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能'
          )
          this.initByOldWay(callback)
        }
      }
    },
    setChart (chart) {
      this.chart = chart
    },
    initByNewWay (callback) {
      // version >= 2.9.0:使用新的方式初始化
      const { canvasId } = this
      const query = wx.createSelectorQuery().in(this)
      query
        .select(`#${canvasId}`)
        .fields({ node: true, size: true })
        .exec(res => {
          if (!res) {
            setTimeout(() => this.init(), 50)
            return
          }
          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')

          this.canvas = new WxCanvas2(ctx, canvasId, true, canvasNode)
          echarts.setCanvasCreator(() => this.canvas)
          setTimeout(() => {
            this.$emit('onInit', {
              width: res[0].width,
              height: res[0].height,
              canvasDpr
            })
          }, 50)
          // if (typeof callback === 'function') {
          //   this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
          // } else if (typeof this.onInit === 'function') {
          //   this.chart = this.onInit(
          //     canvas,
          //     canvasWidth,
          //     canvasHeight,
          //     canvasDpr
          //   )
          // } else {
          //   this.triggerEvent('init', {
          //     canvas,
          //     width: canvasWidth,
          //     height: canvasHeight,
          //     dpr: canvasDpr
          //   })
          // }
        })
    },
    initByOldWay (callback) {
      const { canvasId } = this
      this.ctx = wx.createCanvasContext(canvasId, this)
      this.canvas = new WxCanvas(this.ctx, canvasId)
      const query = wx.createSelectorQuery().in(this)
      query
        .select(`#${canvasId}`)
        .boundingClientRect(res => {
          if (!res) {
            setTimeout(() => this.init(), 50)
            return
          }
          this.$emit('onInit', {
            width: res.width,
            height: res.height
          })
        })
        .exec()
    },
    touchStart (e) {
      const { disableTouch, chart } = this
      if (disableTouch || !chart || !e.mp.touches.length) return
      const touch = e.mp.touches[0]
      chart._zr.handler.dispatch('mousedown', {
        zrX: touch.x,
        zrY: touch.y
      })
      chart._zr.handler.dispatch('mousemove', {
        zrX: touch.x,
        zrY: touch.y
      })
    },
    touchMove (e) {
      const { disableTouch, throttleTouch, chart, lastMoveTime } = this
      if (disableTouch || !chart || !e.mp.touches.length) return
      if (throttleTouch) {
        const currMoveTime = Date.now()
        if (currMoveTime - lastMoveTime < 240) return
        this.lastMoveTime = currMoveTime
      }
      const touch = e.mp.touches[0]
      chart._zr.handler.dispatch('mousemove', {
        zrX: touch.x,
        zrY: touch.y
      })
    },
    touchEnd (e) {
      const { disableTouch, chart } = this
      if (disableTouch || !chart) return
      const touch = e.mp.changedTouches ? e.mp.changedTouches[0] : {}
      chart._zr.handler.dispatch('mouseup', {
        zrX: touch.x,
        zrY: touch.y
      })
      chart._zr.handler.dispatch('click', {
        zrX: touch.x,
        zrY: touch.y
      })
    }
  }
}
</script>

<style scoped>
.ec-canvas {
  width: 100%;
  height: 100%;
}
</style>

导入的依赖wx-canvas和wx-canvas2都在echarts.vue同级目录下

wx-canvas.js(使用的是旧canvas渲染)

export default class WxCanvas {
  constructor(ctx, canvasId) {
    this.ctx = ctx;
    this.canvasId = canvasId;
    this.chart = null;

    WxCanvas.initStyle(ctx);
    this.initEvent();
  }

  getContext(contextType) {
    return contextType === '2d' ? this.ctx : null;
  }

  setChart(chart) {
    this.chart = chart;
  }

  attachEvent() {
    // noop
  }

  detachEvent() {
    // noop
  }

  static initStyle(ctx) {
    const styles = ['fillStyle', 'strokeStyle', 'globalAlpha',
      'textAlign', 'textBaseAlign', 'shadow', 'lineWidth',
      'lineCap', 'lineJoin', 'lineDash', 'miterLimit', 'fontSize'];

    styles.forEach((style) => {
      Object.defineProperty(ctx, style, {
        set: (value) => {
          if ((style !== 'fillStyle' && style !== 'strokeStyle')
            || (value !== 'none' && value !== null)
          ) {
            ctx[`set${style.charAt(0).toUpperCase()}${style.slice(1)}`](value);
          }
        },
      });
    });

    ctx.createRadialGradient = () => 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.mp.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,
        });
      };
    });
  }
}

wx-canvas2.js(使用canvas2d渲染)

export default class WxCanvas2 {
  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;
  }

  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) {
    var styles = ['fillStyle', 'strokeStyle', 'globalAlpha',
      'textAlign', 'textBaseAlign', 'shadow', 'lineWidth',
      'lineCap', 'lineJoin', 'lineDash', 'miterLimit', 'fontSize'];

    styles.forEach(style => {
      Object.defineProperty(ctx, style, {
        set: value => {
          if (style !== 'fillStyle' && style !== 'strokeStyle'
            || value !== 'none' && value !== null
          ) {
            ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value);
          }
        }
      });
    });

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

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

这样就封装好了echarts组件,然后在使用的地方再新建一个myecharts.vue

myecharts.vue(真正配置echarts配置项的地方)

<template>
  <view class="echarts-wrap">
    <uniappEcharts
      class="ec-canvas"
      @onInit="myinit"
      canvasId="demo-canvas"
      ref="chart1"
    />
  </view>
</template>

<script>
import * as echarts from 'echarts/dist/echarts.min'
import uniappEcharts from '@/plugins/echarts/echarts.vue'
let chart1 = null
export default {
  components: {
    uniappEcharts
  },
  methods: {
    // 就是返回echarts配置项的
    getOptions () {
      const option = {
        xAxis: {
          type: 'category',
          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
        },
        yAxis: {
          type: 'value'
        },
        series: [
          {
            data: [150, 230, 224, 218, 135, 147, 260],
            type: 'line'
          }
        ]
      }
      return option
    },
    myinit (e) {
      let { width, height , canvasDpr } = e
      let canvas = this.$refs.chart1.canvas
      echarts.setCanvasCreator(() => canvas)
      chart1 = echarts.init(canvas, null, {
        width: width,
        height: height,
        devicePixelRatio: canvasDpr
      })
      canvas.setChart(chart1)
      console.log(this.getOptions(),2222);
      chart1.setOption(this.getOptions())
      this.$refs.chart1.setChart(chart1)
    }
  }
}
</script>
<style>
.echarts-wrap {
  width: 100%;
  height: 500px;
}
</style>

随便找个html文档插入即可看到效果(如果想使用旧的canvas渲染的话,只需在uniappEcharts上添加      forceUseOldCanvas属性即可)

注意:小程序一般会限制单包2m大小,碰上这种情况自己去定制一个echarts.js就行,很简单的,小学生都会用,选自己需要的图例即可

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值