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

news2024/11/16 9:38:57

近期接到公司新需求,开发飞书小程序,并且原型中含有大量的图表,本想使用飞书内置图表组件 ——
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
  }
}

目录结构如下图
在这里插入图片描述
在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>

二次封装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>

使用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>

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1630592.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CRM客户关系管理-客户资源企业化销售管理平台

什么是CRM 客户关系管理&#xff08;Customer Relationship Management&#xff0c;简称CRM&#xff09;&#xff0c;是指企业为提高核心竞争力&#xff0c;利用相应的信息技术以及互联网技术协调企业与顾客间在销售、营销和服务上的交互&#xff0c;从而提升其管理方式&#…

dockerfile 搭建lamp 实验模拟

一 实验目的 二 实验 环境 1, 实验环境 192.168.217.88一台机器安装docker 并做mysql nginx php 三台容器 2&#xff0c; 大致框架 3&#xff0c; php php:Nginx服务器不能处理动态页面&#xff0c;需要由 Nginx 把动态请求交给 php-fpm 进程进行解析 php有三…

记录些AI Agents设计模式和NL2SQL知识

吴恩达分享的四种 自我反思&#xff08;Reflection&#xff09;&#xff1a;可以自我修正&#xff1b;使用工具&#xff08;Tool Use&#xff09;&#xff1a;链接其他系统去做一些事情&#xff0c;比如把电脑里面的未归档文件做好归档&#xff1b;规划&#xff08;Planning&a…

【炼金术士】BatchSize对网络训练的影响

文章目录 1 BatchSize对于网络训练的影响2 调整学习率可以提高大BatchSize的性能3 实际训练时的建议3.1 设置初始学习率的方法3.2 多卡训练时学习率的设置 参考资料&#xff1a; 【深度学习】Batch Size对神经网络训练的影响【AI不惑境】学习率和batchsize如何影响模型的性能&…

Windows使用SSH登录本机Linux虚拟机

SSH&#xff08;Secure Shell&#xff09;&#xff0c;一种网络协议&#xff0c;可以在安全外壳下实现数据传输通信&#xff0c;所以主要用于计算机间加密登录&#xff0c;可以简单理解为远程控制。除了计算机间直接互联&#xff0c;在git中也可以看到&#xff0c;常见的协议有…

购买 DDoS 高防 IP 防护哪家好?

DDoS 高防 IP 哪里买会比较好?在这场攻与守的游戏里&#xff0c;DDoS 高防 IP 是一种针对 DDoS 攻击的防护措施&#xff0c;通过将网站或应用的 IP 地址映射到高防 IP 上&#xff0c;实现对流量的清洗和过滤&#xff0c;从而有效抵御 DDoS 攻击。在选择 DDoS 高防 IP 服务提供…

Ubuntu下部署gerrit+报错分析(超详细)

Ubuntu下部署gerrit代码平台 之前安装过几次 最后都在Apache代理这里失败了&#xff0c;如下图&#xff0c;总是gerrit.config与Apache2.config配置有问题&#xff0c;后面换了使用ngnix代理&#xff0c;简单多了 安装Mysql、gerrit、jdk、git 这一步也是非必须得&#xff0…

无监督学习的评价指标

轮廓系数&#xff08;Silhouette Coefficient&#xff09; 轮廓系数用于判断聚类结果的紧密度和分离度。轮廓系数综合了样本与其所属簇内的相似度以及最近的其他簇间的不相似度。 其计算方法如下&#xff1a; 1、计算簇中的每个样本i 1.计算a&#xff08;i&#xff09; &#x…

实时采集麦克风并播放(springboot+webscoekt+webrtc)

项目技术 springbootwebscoektwebrtc 项目介绍 项目通过前端webrtc采集麦克风声音&#xff0c;通过websocket发送后台&#xff0c;然后处理成g711-alaw字节数据发生给广播UDP并播放。 后台处理项目使用线程池(5个线程)接受webrtc数据并处理g711-alaw字节数组放到Map容器中&…

opencv基础篇 ——(九)图像几何变换

图像几何变换是通过对图像的几何结构进行变换来改变图像的形状、大小、方向或者透视关系。常见的图像几何变换包括缩放、旋转、平移、仿射变换和透视变换等。下面对这些几何变换进行简要介绍&#xff1a; 矩阵的转置&#xff08;transpose &#xff09;&#xff1a; 对于图像来…

吴恩达2022机器学习专项课程(一) 7.1 逻辑回归的成本函数第三周课后实验:Lab4逻辑回归的损失函数

问题预览/关键词 上节课回顾逻辑回归模型使用线性回归模型的平方误差成本函数单个训练样本的损失损失函数&#xff0c;成本函数&#xff0c;代价函数的区别线性回归损失函数和逻辑回归损失函数的区别逻辑回归模型的成本函数是什么&#xff1f;逻辑回归模型的损失函数实验逻辑回…

STL——List常用接口模拟实现及其使用

认识list list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素…

linux tcpdump的交叉编译以及使用

一、源码下载 官网&#xff1a;点击跳转 二、编译 1、解压 tar -xf libpcap-1.10.4.tar.xz tar -xf tcpdump-4.99.4.tar.xz 2、配置及编译 //libpcap&#xff1a; ./configure --hostarm-linux --targetarm-linux CCarm-linux-gcc --with-pcaplinux --prefix$PWD/build//t…

对象与JSON字符串互转

1、JSON字符串转化成JSON对象 JSONObject jsonobject JSON.parseObject(str); 或者 JSONObject jsonobject JSONObject.parseObject(str); 功能上是一样的&#xff0c;都是将JSON字符串&#xff08;str&#xff09;转换成JSON对象 jsonobject 。注意str一定得是以键值对存在…

STM32之HAL开发——电容按键

电容按键原理 电容器 (简称为电容) 就是可以容纳电荷的器件&#xff0c;两个金属块中间隔一层绝缘体就可以构成一个最简单的电容。如图 32_1 (俯视图)&#xff0c;有两个金属片&#xff0c;之间有一个绝缘介质&#xff0c;这样就构成了一个电容。这样一个电容在电路板上非常容…

二维数组求最大值(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int i, j, max 0, row 0, colum 0;int arr[3][4] { {1, 2, 3}, {4, 5, 16}, {7, 8, 9} …

线上办理离婚快速离婚,无需双方见面异地可办

现在离婚有两种方式 一种是协议离婚&#xff0c;双方都同意的情况下&#xff0c;可以去民政局协议离婚&#xff0c;有30天冷静期&#xff0c;冷静期过后需要双方再次去民政局办理离婚手续。 另一种是诉讼离婚&#xff0c;一方不同意离婚&#xff0c;可以选择诉讼离婚。可以全…

Vue 3 路由机制详解与实践

一、路由的理解 路由是指导用户界面导航的一种机制。它通过映射 URL 到应用程序的不同视图组件来实现页面间的切换和导航。 二、路由基本切换效果 路由基本切换效果指的是当用户在应用程序中进行页面导航时&#xff0c;通过路由可以实现页面的切换&#xff0c;从而展示不同的…

ICMP详解

3 ICMP ICMP&#xff08;Internet Control Message Protocol&#xff0c;因特网控制报文协议&#xff09;是一个差错报告机制&#xff0c;是TCP/IP协议簇中的一个重要子协议&#xff0c;通常被IP层或更高层协议&#xff08;TCP或UDP&#xff09;使用&#xff0c;属于网络层协议…

CTF(web方向)--md5的“===”和“==”的绕过

一、PHP弱类型说明 1.简介 php是一种弱类型语言&#xff0c;对数据的类型要求并不严格&#xff0c;可以让数据类型互相转换。 在php中有两种比较符号: 一种是 &#xff0c;另外一种是 &#xff0c;都是用来比较两个数值是否相等的操作符&#xff0c;但他们也是有区别的: &a…