123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- <template>
- <view class="ui-echarts" :class="[customClass]" :style="wrapStyle">
- <!-- #ifndef APP-NVUE -->
- <!-- #ifdef MP-WEIXIN || MP-TOUTIAO -->
- <canvas class="ui-echarts_canvas" :style="wrapStyle" :canvas-id="canvasId" :id="canvasId" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" type="2d" />
- <!-- #endif -->
- <!-- #ifndef MP-WEIXIN || MP-TOUTIAO -->
- <canvas class="ui-echarts_canvas" :style="wrapStyle" :canvas-id="canvasId" :id="canvasId" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" />
- <!-- #endif -->
- <!-- #endif -->
- <!-- #ifdef APP-NVUE -->
- <web-view ref="webview" class="ui-echarts_webview"
- src="/uni_modules/ui-echarts/static/index.html"
- :style="wrapStyle"
- :id="canvasId"
- @onPostMessage="onPostMessage">
- </web-view>
- <!-- #endif -->
- </view>
- </template>
- <script>
- import * as echarts from '../../static/echarts.min.js';
- import * as ecStat from '../../static/ecStat.min.js';
- import WxCanvas from '../../static/wxcanvas.js';
-
- let chartList = {};
-
- 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;
- }
- /**
- * Echart 图表工具
- * @description 通过折叠面板收纳内容区域(搭配ui-collapse使用)
- * @tutorial https://echarts.apache.org/zh/api.html#echarts
- * @property {String} option 图表配置
- * @property {String | Number} width 宽度,默认100%
- * @property {String | Number} height 高度,默认300px
- * @property {Boolean} exportBase64=[base64] 输出类型, 默认false
- * @example
- */
- export default {
- name: 'ui-echarts',
- mixins: [uni.$ui.mixin],
- // #ifdef MP-WEIXIN
- // 将自定义节点设置成虚拟的,更加接近Vue组件的表现,能更好的使用flex属性
- options: {
- virtualHost: true
- },
- // #endif
- props: {
- option: {
- type: Object,
- default: () => {
- return {}
- }
- },
- width: {
- type: [String, Number],
- default: '100%',
- },
- height: {
- type: [String, Number],
- default: '600rpx'
- },
- // 输出类型, base64输出base64, H5无效。
- exportBase64: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
- canvasId: `canvas_${uni.$ui.guid(10)}`,
- ctx: null,
- // 网页初始化就绪。用于NVUE
- webviewReady: false,
- // 图表初始化就绪。用于NVUE
- webviewChart: false,
- // 图表导出图片临时文件
- webviewCallback: null
- }
- },
- computed: {
- wrapStyle () {
- const { width, height } = this;
- const { addUnit, addStyle } = uni.$ui;
- return addStyle({width: addUnit(width), height: addUnit(height)}, 'string')
- }
- },
- watch: {
- option(newValue, oldValue) {
- if(newValue.series){
- this.initChart(newValue)
- }
- },
- // #ifdef APP-NVUE
- webviewReady(state) {
- this.initChart(this.option);
- }
- // #endif
- },
- methods: {
- getCanvasAttr2d() {
- return new Promise((resolve, reject) => {
- const query = uni.createSelectorQuery().in(this)
- query
- .select('#' + this.canvasId)
- .fields({
- node: true,
- size: true
- })
- .exec(res => {
- const canvasNode = res[0].node
- this.canvasNode = canvasNode
- const canvasDpr = uni.getSystemInfoSync().pixelRatio
- const canvasWidth = res[0].width
- const canvasHeight = res[0].height
- this.ctx = canvasNode.getContext('2d')
-
- const canvas = new WxCanvas(this.ctx, this.canvasId, true, canvasNode)
- echarts.setCanvasCreator(() => {
- return canvas
- })
- resolve({
- canvas,
- canvasWidth,
- canvasHeight,
- canvasDpr
- })
- })
- });
- },
- getCanvasAttr() {
- return new Promise((resolve, reject) => {
- this.ctx = uni.createCanvasContext(this.canvasId, this);
- var canvas = new WxCanvas(this.ctx, this.canvasId, false);
- echarts.setCanvasCreator(() => {
- return canvas;
- });
- const canvasDpr = 1
- var query = uni.createSelectorQuery()
- // #ifndef MP-ALIPAY
- .in(this)
- // #endif
- query.select('#' + this.canvasId).boundingClientRect(res => {
- const canvasWidth = res.width
- const canvasHeight = res.height
- resolve({
- canvas,
- canvasWidth,
- canvasHeight,
- canvasDpr
- })
- }).exec();
- });
- },
- // #ifdef H5
- //H5绘制图表
- initChart(option) {
- this.ctx = uni.createCanvasContext(this.canvasId, this);
- chartList[this.canvasId] = echarts.init(document.getElementById(this.canvasId));
- chartList[this.canvasId].setOption(option?option:this.option);
- },
- //H5生成图片
- toImageFile(opt) {
- const base64 = chartList[this.canvasId].getDataURL()
- opt.success && opt.success({tempFilePath: base64, base64 })
- },
- // #endif
- // #ifndef H5
- //绘制图表
- async initChart(option) {
- // #ifdef APP-NVUE
- this.chartInitOption(option);
- return;
- // #endif
- // #ifdef MP-WEIXIN || MP-TOUTIAO
- const canvasAttr = await this.getCanvasAttr2d();
- // #endif
- // #ifndef MP-WEIXIN || MP-TOUTIAO
- const canvasAttr = await this.getCanvasAttr();
- // #endif
- const {
- canvas,
- canvasWidth,
- canvasHeight,
- canvasDpr
- } = canvasAttr
- chartList[this.canvasId] = echarts.init(canvas, null, {
- width: canvasWidth,
- height: canvasHeight,
- devicePixelRatio: canvasDpr // new
- });
- canvas.setChart(chartList[this.canvasId]);
- chartList[this.canvasId].setOption(option?option:this.option);
- },
- //生成图片
- toImageFile(opt) {
- const self = this;
- // #ifdef APP-NVUE
- this.chartToImage(opt);
- return;
- // #endif
- // #ifdef MP-WEIXIN || MP-TOUTIAO
- var query = uni.createSelectorQuery()
- // #ifndef MP-ALIPAY
- .in(this)
- // #endif
- query.select('#' + this.canvasId).fields({ node: true, size: true }).exec(res => {
- const canvasNode = res[0].node
- opt.canvas = canvasNode
- self.toImageFileHandle(opt)
- // uni.canvasToTempFilePath(opt, this)
- })
- // #endif
- // #ifndef MP-WEIXIN || MP-TOUTIAO
- if (!opt.canvasId) {
- opt.canvasId = this.canvasId;
- }
- this.ctx.draw(true, () => {
- self.toImageFileHandle(opt)
- // uni.canvasToTempFilePath(opt, this);
- });
- // #endif
- },
- toImageFileHandle (opt) {
- if (!this.exportBase64) {
- uni.canvasToTempFilePath(opt, this);
- } else {
- const { success, fail, complete, ...rest } = opt;
- uni.canvasToTempFilePath({
- ...rest,
- success: async (res) => {
- const tempFilePath = res.tempFilePath;
- const base64Str = await uni.$ui.base64.pathToBase64(tempFilePath);
- success && success({ errMsg: res.errMsg, tempFilePath, base64: base64Str })
- },
- fail: (res) => {
- fail && fail(res)
- },
- complete: (res) => {
- complete && complete(res)
- }
- })
- }
- },
- // #endif
- touchStart(e) {
- if (chartList[this.canvasId] && e.touches.length > 0) {
- var touch = e.touches[0];
- var handler = chartList[this.canvasId].getZr().handler;
- handler.dispatch('mousedown', {
- zrX: touch.x,
- zrY: touch.y
- });
- handler.dispatch('mousemove', {
- zrX: touch.x,
- zrY: touch.y
- });
- handler.processGesture(wrapTouch(e), 'start');
- }
- },
- touchMove(e) {
- if (chartList[this.canvasId] && e.touches.length > 0) {
- var touch = e.touches[0];
- var handler = chartList[this.canvasId].getZr().handler;
- handler.dispatch('mousemove', {
- zrX: touch.x,
- zrY: touch.y
- });
- handler.processGesture(wrapTouch(e), 'change');
- }
- },
- touchEnd(e) {
- if (chartList[this.canvasId]) {
- const touch = e.changedTouches ? e.changedTouches[0] : {};
- var handler = chartList[this.canvasId].getZr().handler;
- handler.dispatch('mouseup', {
- zrX: touch.x,
- zrY: touch.y
- });
- handler.dispatch('click', {
- zrX: touch.x,
- zrY: touch.y
- });
- handler.processGesture(wrapTouch(e), 'end');
- }
- },
- // #ifdef APP-NVUE
- chartInitOption (option) {
- this.webviewReady && this.$refs.webview.evalJs(`setOption(${JSON.stringify(option)})`);
- },
- chartToImage (opt) {
- this.webviewCallback = opt;
- this.webviewReady && this.webviewChart && this.$refs.webview.evalJs(`toImage()`);
- },
- chartClear () {
- this.webviewReady && this.webviewChart && this.$refs.webview.evalJs(`clearChart()`);
- },
- async onPostMessage (evt) {
- /**
- * 1: 准备就绪(webview)
- * 2: 初始化echarts
- * 3: 设置echarts option
- * 4: 导出图片
- * 5: 销毁echarts实例
- * */
- const res = evt?.detail?.data[0] || { state: 0 };
- if (res.state === 1) {
- this.webviewReady = true;
- } else if (res.state === 2) {
- this.webviewChart = true;
- } else if (res.state === 3) {
- // option 更新成功
- } else if (res.state === 4) {
- const opt = { ...this.webviewCallback };
- const filePath = await uni.$ui.base64.base64ToPath(res.base64);
- opt.success && opt.success({ errMsg: 'ok', tempFilePath: filePath, base64: res.base64 });
- }
- },
- // #endif
- clearChart () {
- // #ifdef APP-NVUE
- this.chartClear();
- return;
- // #endif
- if (chartList[this.canvasId]) {
- chartList[this.canvasId].clear()
- }
- }
- },
- beforeDestroy() {
- this.clearChart();
- },
- mounted() {
- // 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;
- }
- }
- });
- }
- }
- </script>
- <style lang="scss" scoped>
- .ui-echarts {
- flex: 1;
-
- &_canvas {
- width: 100%;
- height: 100%;
- }
- }
- </style>
|