uniapp 微信小程序使用echarts
本文目的:通过分包的方式,尽可能在微信小程序中使用最新的echarts。
当然你也可以直接使用现成的uchart或者市场里别人封好的echarts.
- 准备工作
- 下载echarts-for-weixin源码。
- 复制
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下载,至此准备工作完成。
- 复制
- 下载echarts-for-weixin源码。
- 修改
ec-canvas.vue
中的代码- 替换两个
canvas
标签中所绑定的事件,视methods中的方法名而定- @touchstart=“touchStart”
- @touchmove=“touchMove”
- @touchend=“touchEnd”
- 去除import上的两行代码:
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的原本语法一一复制:- properties => props, 其中的value改为default
- data => data() { return {} }
- ready => mounted() {}
- methods => 直接复制即可
- compareVersion和wrapTouch这两个方法至于export default上即可,无需放在methods中
- 给methods的
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"><canvasv-if="isUseNewCanvas"type="2d"class="ec-canvas":canvas-id="canvasId"@init="init"@touchstart="touchStart"@touchmove="touchMove"@touchend="touchEnd"></canvas><canvasv-elseclass="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 ctxfunction 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.xtouch.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.htmlecharts.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 变量,例:<ec-canvas id="mychart-dom-bar" ' +'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>')return}if (!this.ec.lazyLoad) {this.init()}},methods: {init: async function (callback) {const version = wx.getSystemInfoSync().SDKVersionconst canUseNewCanvas = compareVersion(version, '2.9.0') >= 0const forceUseOldCanvas = this.forceUseOldCanvasconst isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvasthis.isUseNewCanvas = isUseNewCanvasif (forceUseOldCanvas && canUseNewCanvas) {console.warn('开发者强制使用旧canvas,建议关闭')}await this.$nextTick()if (isUseNewCanvas) {// console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');// 2.9.0 可以使用 <canvas type="2d"></canvas>this.initByNewWay(callback)} else {const isValid = compareVersion(version, '1.9.91') >= 0if (!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不能传入dprconst canvasDpr = 1var 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].nodethis.canvasNode = canvasNodeconst canvasDpr = wx.getSystemInfoSync().pixelRatioconst canvasWidth = res[0].widthconst canvasHeight = res[0].heightconst 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].nodeopt.canvas = canvasNodewx.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().handlerhandler.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().handlerhandler.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().handlerhandler.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-canvasref="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) returnchartInstance.setOption(data)}},
}
</script>
还可以考虑进一步修改,将initChart方法的相关逻辑放在ec-canvas.vue自身去处理。
还可以通过条件编译的方式,实现一个echarts组件同时适配H5与微信小程序。
这些视情况,放到下一次讲。
相关文章:
uniapp 微信小程序使用echarts
本文目的:通过分包的方式,尽可能在微信小程序中使用最新的echarts。 当然你也可以直接使用现成的uchart或者市场里别人封好的echarts. 准备工作 下载echarts-for-weixin源码。 复制ec-canvas文件夹以及下属文件,在uniapp项目中与pages同级的地…...

【漏洞复现】企望制造 ERP命令执行
漏洞描述 由于企望制造 ERP comboxstore.action接口权限设置不当,默认的配置可执行任意SQL语句,利用xp_cmdshell函数可远程执行命令,未经认证的攻击者可通过该漏洞获取服务器权限。 免责声明 技术文章仅供参考,任何个人和组织…...

2023 “华为杯” 中国研究生数学建模竞赛(E题)深度剖析|数学建模完整代码+建模过程全解全析
问题一 血肿扩张风险相关因素探索建模 思路: 根据题目要求,首先需要判断每个患者是否发生了血肿扩张事件。根据定义,如果后续检查的血肿体积比首次检查增加≥6 mL或≥33%,则判断为发生了血肿扩张。 具体判断步骤: (1) 从表1中提取每个患者的入院首次影像检查…...

【腾讯云国际站】CDN内容分发网络特性介绍
为什么使用腾讯云国际站 CDN 内容分发网络? 当用户直接访问源站中的静态内容时,可能面临的体验问题: 客户离服务器越远,访问速度越慢。客户数量越多,网络带宽费用越高。跨境用户访问体验较差。 腾讯云国际站CDN 如何改…...

【工业机器人视觉】
工业机器人视觉 工业机器人的定位、抓取任务是工业生产线上一项重要的应用,一般通过预先示教的方式让机器人执行预定的指令动作。但是,一旦工件的状态发生改变时,机器人便无法完成工作任务。区别:就像人睁眼走直线和闭眼走直线。…...
跨域(浏览器)
跨域问题 是前端开发中常遇到的一个挑战。由于浏览器的同源策略限制,前端在发起异步请求时会受到限制,只能向相同源(域名、协议和端口号都相同)的服务器发送请求。当请求的目标服务器与当前页面的源不一致时,就会触发…...
Leetcode 2866. Beautiful Towers II
Leetcode 2866. Beautiful Towers II 1. 解题思路2. 代码实现 题目链接:2866. Beautiful Towers II 1. 解题思路 这一题其实思路上还是比较明显的,就是一个单调数组的问题,问题在于说如果具体去设计这个单调数组。 我们从题目出发&#x…...

电脑C盘爆红怎么办?(小白篇)
文章目录 前言:1、清理临时和系统文件2、更改电脑默认软件安装位置3、微信、QQ文件存储路径放在其它盘4、卸载一些不常用的软件彩蛋 前言: C盘作为电脑的系统盘,如果出现爆满或者剩余空间很小整个C盘变红,这样会导致电脑系统运行…...
Office Xml 2003转XLSX
一、使用到的依赖包 1、xelem-3.1.jar 下载地址:管网下载地址 2、poi-3.17.jar 下载地址:https://mvnrepository.com/artifact/org.apache.poi/poi 二、实现方法 1、Xml2003公式转XLSX公式算法 (1)Xml2003函数格式 SUM(R[-1…...
skyWalking搭建(一)
title: “SkyWalking搭建(一)” createTime: 2021-07-27T14:34:2108:00 updateTime: 2021-07-27T14:34:2108:00 draft: false author: “name” tags: [“skywalking”] categories: [“java”] description: “测试的” 基于 docker 部署 skywalking 并实现 SpringBoot 全链路…...
Golang开发--sync.WaitGroup
sync.WaitGroup 是 Go 语言标准库中的一个并发原语,用于等待一组并发操作的完成。它提供了一种简单的方式来跟踪一组 goroutine 的执行状态,并在所有 goroutine 完成后恢复执行。 下面是关于 sync.WaitGroup 的实现细节的详细解释: 创建 Wa…...
Linux命令教程:使用cat命令查看和处理文件
文章目录 教程:使用cat命令在Linux中查看和处理文件1. 引言2. cat命令的基本概述3. 查看文件内容4. 创建文件5. 文件重定向和管道6. 格式化和编辑文件7. 实际应用示例7.1 使用cat命令浏览日志文件7.2 利用cat命令合并多个配置文件7.3 使用cat命令将文件内容发送到其…...

Websocket集群解决方案以及实战(附图文源码)
最近在项目中在做一个消息推送的功能,比如客户下单之后通知给给对应的客户发送系统通知,这种消息推送需要使用到全双工的websocket推送消息。 所谓的全双工表示客户端和服务端都能向对方发送消息。不使用同样是全双工的http是因为http只能由客户端主动发…...
科技的成就(五十一)
397、初等数论的不可解问题 1936 年 4 月,邱奇证明判定性问题不可解。33 岁的邱奇发表论文《初等数论的不可解问题》,运用λ演算给出了判定性问题一个否定的答案。λ演算是一套从数学逻辑中发展起来的形式系统,采用变量绑定和替换,…...

Tomcat8 任意写文件PUT方法 (CVE-2017-12615)
Tomcat 任意写文件PUT方法 (CVE-2017-12615) 文章目录 Tomcat 任意写文件PUT方法 (CVE-2017-12615)1 在线漏洞解读:2 版本影响3 环境搭建4 漏洞复现4.1 访问4.2 POC攻击点4.2.1 直接发送以下数据包,然后shell将被写入Web根目录。4.2.2 访问是否通,可以访…...
SAP服务器修改主机名操作手册
1、业务背景 SAP服务器P2V:虚拟化后的服务器主机名(或叫计算机名、设备名,hostname,下文同)会和原参照克隆的服务器主机名一样,若两台服务器处于同一网域,会出现域冲突,导致以下事故发生 (1)、使得原服务器出现掉域情况(DEV->CLN->PRD后台服务器访问失效) …...

【大数据】Doris 构建实时数仓落地方案详解(一):实时数据仓库概述
本系列包含: Doris 构建实时数仓落地方案详解(一):实时数据仓库概述Doris 构建实时数仓落地方案详解(二):Doris 核心功能解读Doris 构建实时数仓落地方案详解(三)&#…...

C++ list容器的实现及讲解
所需要的基础知识 对C类的基本了解 默认构造函数 操作符重载 this指针 引用 模板等知识具有一定的了解,阅读该文章会很轻松。 链表节点 template<class T>struct list_node{T _data;list_node<T>* _next;list_node<T>* _prev;list_node(const T&…...

前端项目练习(练习-002-NodeJS项目初始化)
首先,创建一个web-002项目,内容和web-001一样。 下一步,规范一下项目结构,将html,js,css三个文件放到 src/view目录下面: 由于html引入css和js时,使用的是相对路径,所以…...

C++QT day11
绘制时钟 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent>//绘制事件类 #include <QDebug>//信息调试类 #include <QPainter>//画家类 #include <QTimer>//定时器类 #include <QTime> #include &…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...