HomeLease/uni_modules/lime-echart/components/l-echart/l-echart.vue
2025-09-09 18:03:31 +08:00

545 lines
15 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view
class="lime-echart"
:style="customStyle"
v-if="canvasId"
ref="limeEchart"
:aria-label="ariaLabel"
>
<!-- #ifndef APP-NVUE -->
<canvas
class="lime-echart__canvas"
v-if="use2dCanvas"
type="2d"
:id="canvasId"
:style="canvasStyle"
:disable-scroll="isDisableScroll"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
/>
<!-- <canvas
class="lime-echart__canvas"
v-else-if="isPC"
:style="canvasStyle"
:id="canvasId"
:canvas-id="canvasId"
:disable-scroll="isDisableScroll"
@mousedown="touchStart"
@mousemove="touchMove"
@mouseup="touchEnd"
/> -->
<canvas
class="lime-echart__canvas"
v-else
:width="nodeWidth"
:height="nodeHeight"
:style="canvasStyle"
:canvas-id="canvasId"
:id="canvasId"
:disable-scroll="isDisableScroll"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
/>
<view
class="lime-echart__mask"
v-if="isPC"
@mousedown="touchStart"
@mousemove="touchMove"
@mouseup="touchEnd"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>
</view>
<canvas
v-if="isOffscreenCanvas"
:style="offscreenStyle"
:canvas-id="offscreenCanvasId"
></canvas>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<web-view
class="lime-echart__canvas"
:id="canvasId"
:style="canvasStyle"
:webview-styles="webviewStyles"
ref="webview"
src="/uni_modules/lime-echart/static/nvue.html?v=1"
@pagefinish="finished = true"
@onPostMessage="onMessage"
></web-view>
<!-- #endif -->
</view>
</template>
<script>
// #ifdef VUE3
// #ifdef APP-PLUS
global = {}
// #endif
// #endif
// #ifndef APP-NVUE
import { Canvas, setCanvasCreator, dispatch } from './canvas'
import {
wrapTouch,
convertTouchesToArray,
devicePixelRatio,
sleep,
canIUseCanvas2d,
getRect,
} from './utils'
// #endif
// #ifdef APP-NVUE
import { base64ToPath, sleep } from './utils'
import { Echarts } from './nvue'
// #endif
const charts = {}
const echartsObj = {}
export default {
name: 'lime-echart',
props: {
// #ifdef MP-WEIXIN || MP-TOUTIAO
type: {
type: String,
default: '2d',
},
// #endif
// #ifdef APP-NVUE
webviewStyles: Object,
// hybrid: Boolean,
// #endif
customStyle: String,
isDisableScroll: Boolean,
isClickable: {
type: Boolean,
default: true,
},
enableHover: Boolean,
beforeDelay: {
type: Number,
default: 30,
},
},
data() {
return {
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: true,
// #endif
// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: false,
// #endif
ariaLabel: '图表',
width: null,
height: null,
nodeWidth: null,
nodeHeight: null,
// canvasNode: null,
config: {},
inited: false,
finished: false,
file: '',
platform: '',
isPC: false,
isDown: false,
isOffscreenCanvas: false,
offscreenWidth: 0,
offscreenHeight: 0,
}
},
computed: {
canvasId() {
return `lime-echart${(this._ && this._.uid) || this._uid}`
},
offscreenCanvasId() {
return `${this.canvasId}_offscreen`
},
offscreenStyle() {
return `width:${this.offscreenWidth}px;height: ${this.offscreenHeight}px; position: fixed; left: 99999px; background: red`
},
canvasStyle() {
return this.width && this.height
? 'width:' + this.width + 'px;height:' + this.height + 'px'
: ''
},
},
// #ifndef VUE3
beforeDestroy() {
this.clear()
this.dispose()
// #ifdef H5
if (this.isPC) {
document.removeEventListener('mousewheel', this.mousewheel)
}
// #endif
},
// #endif
// #ifdef VUE3
unmounted() {
this.clear()
this.dispose()
// #ifdef H5
if (this.isPC) {
document.removeEventListener('mousewheel', this.mousewheel)
}
// #endif
},
// #endif
created() {
// #ifdef H5
if (!('ontouchstart' in window)) {
this.isPC = true
document.addEventListener('mousewheel', this.mousewheel)
}
// #endif
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
const { platform } = uni.getSystemInfoSync()
this.isPC = /windows/i.test(platform)
// #endif
this.use2dCanvas = this.type === '2d' && canIUseCanvas2d()
},
mounted() {
this.$nextTick(() => {
this.$emit('finished')
})
},
methods: {
// #ifdef APP-NVUE
onMessage(e) {
const res = e?.detail?.data[0] || null
if (res?.event) {
const data = JSON.parse(res.data)
if (res.event === 'inited') {
this.inited = true
} else if (res.event.startsWith('@')) {
this.chart.dispatchAction(res.event.split('@')[1], data.options)
}
this.$emit(res.event, data)
} else if (res?.file) {
this.file = res.data
} else if (!res[0] && JSON.stringify(res[0]) != '{}') {
console.error(res)
} else {
console.log(...res)
}
},
// #endif
setChart(callback) {
if (!this.chart) {
console.warn(`组件还未初始化,请先使用 init`)
return
}
if (typeof callback === 'function' && this.chart) {
callback(this.chart)
}
// #ifdef APP-NVUE
if (typeof callback === 'function') {
this.$refs.webview.evalJs(
`setChart(${JSON.stringify(callback.toString())}, ${JSON.stringify(this.chart.options)})`
)
}
// #endif
},
setOption() {
if (!this.chart || !this.chart.setOption) {
console.warn(`组件还未初始化,请先使用 init`)
return
}
this.chart.setOption(...arguments)
},
showLoading() {
if (this.chart) {
this.chart.showLoading(...arguments)
}
},
hideLoading() {
if (this.chart) {
this.chart.hideLoading()
}
},
clear() {
if (this.chart) {
this.chart.clear()
}
},
dispose() {
if (this.chart) {
this.chart.dispose()
}
},
resize(size) {
if (size && size.width && size.height) {
this.height = size.height
this.width = size.width
if (this.chart) {
this.chart.resize(size)
}
} else {
this.$nextTick(() => {
uni
.createSelectorQuery()
.in(this)
.select(`.lime-echart`)
.boundingClientRect()
.exec(res => {
if (res) {
let { width, height } = res[0]
this.width = width = width || 300
this.height = height = height || 300
this.chart.resize({ width, height })
}
})
})
}
},
canvasToTempFilePath(args = {}) {
// #ifndef APP-NVUE
const { use2dCanvas, canvasId } = this
return new Promise((resolve, reject) => {
const copyArgs = Object.assign(
{
canvasId,
success: resolve,
fail: reject,
},
args
)
if (use2dCanvas) {
delete copyArgs.canvasId
copyArgs.canvas = this.canvasNode
}
uni.canvasToTempFilePath(copyArgs, this)
})
// #endif
// #ifdef APP-NVUE
this.file = ''
this.$refs.webview.evalJs(`canvasToTempFilePath()`)
return new Promise((resolve, reject) => {
this.$watch('file', async file => {
if (file) {
const tempFilePath = await base64ToPath(file)
resolve(args.success({ tempFilePath }))
} else {
reject(args.fail({ error: `` }))
}
})
})
// #endif
},
async init(echarts, ...args) {
// #ifndef APP-NVUE
if (arguments && arguments.length < 1) {
console.error(
'缺少参数init(echarts, theme?:string, opts?: object, callback?: function)'
)
return
}
// #endif
let theme = null,
opts = {},
callback
Array.from(arguments).forEach(item => {
if (typeof item === 'function') {
callback = item
}
if (['string'].includes(typeof item)) {
theme = item
}
if (typeof item === 'object') {
opts = item
}
})
if (this.beforeDelay) {
await sleep(this.beforeDelay)
}
let config = await this.getContext()
// #ifndef APP-NVUE
setCanvasCreator(echarts, config)
this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts))
if (typeof callback === 'function') {
callback(this.chart)
} else {
return this.chart
}
// #endif
// #ifdef APP-NVUE
this.chart = new Echarts(this.$refs.webview)
this.$refs.webview.evalJs(`init(null, null, ${JSON.stringify(opts)}, ${theme})`)
if (callback) {
callback(this.chart)
} else {
return this.chart
}
// #endif
},
getContext() {
// #ifdef APP-NVUE
if (this.finished) {
return Promise.resolve(this.finished)
}
return new Promise(resolve => {
this.$watch('finished', val => {
if (val) {
resolve(this.finished)
}
})
})
// #endif
// #ifndef APP-NVUE
return getRect(`#${this.canvasId}`, {
context: this,
type: this.use2dCanvas ? 'fields' : 'boundingClientRect',
}).then(res => {
if (res) {
let dpr = devicePixelRatio
let { width, height, node } = res
let canvas
this.width = width = width || 300
this.height = height = height || 300
if (node) {
const ctx = node.getContext('2d')
canvas = new Canvas(ctx, this, true, node)
this.canvasNode = node
} else {
// #ifdef MP-TOUTIAO
dpr = !this.isPC ? devicePixelRatio : 1 // 1.25
// #endif
// #ifndef MP-ALIPAY || MP-TOUTIAO
dpr = this.isPC ? devicePixelRatio : 1
// #endif
// #ifdef MP-ALIPAY || MP-LARK
dpr = devicePixelRatio
// #endif
this.rect = res
this.nodeWidth = width * dpr
this.nodeHeight = height * dpr
const ctx = uni.createCanvasContext(this.canvasId, this)
canvas = new Canvas(ctx, this, false)
}
return { canvas, width, height, devicePixelRatio: dpr, node }
} else {
return {}
}
})
// #endif
},
// #ifndef APP-NVUE
getRelative(e, touches) {
let { clientX, clientY } = e
if (!(clientX && clientY) && touches && touches[0]) {
clientX = touches[0].clientX
clientY = touches[0].clientY
}
return {
x: clientX - this.rect.left,
y: clientY - this.rect.top,
wheelDelta: e.wheelDelta || 0,
}
},
getTouch(e, touches) {
const { x } = (touches && touches[0]) || {}
return x ? touches[0] : this.getRelative(e, touches)
},
touchStart(e) {
this.isDown = true
const next = () => {
const touches = convertTouchesToArray(e.touches)
if (this.chart) {
const touch = this.getTouch(e, touches)
this.startX = touch.x
this.startY = touch.y
this.startT = new Date()
const handler = this.chart.getZr().handler
dispatch.call(handler, 'mousedown', touch)
dispatch.call(handler, 'mousemove', touch)
handler.processGesture(wrapTouch(e), 'start')
clearTimeout(this.endTimer)
}
}
if (this.isPC) {
getRect(`#${this.canvasId}`, { context: this }).then(res => {
this.rect = res
next()
})
return
}
next()
},
touchMove(e) {
if (this.isPC && this.enableHover && !this.isDown) {
this.isDown = true
}
const touches = convertTouchesToArray(e.touches)
if (this.chart && this.isDown) {
const handler = this.chart.getZr().handler
dispatch.call(handler, 'mousemove', this.getTouch(e, touches))
handler.processGesture(wrapTouch(e), 'change')
}
},
touchEnd(e) {
this.isDown = false
if (this.chart) {
const touches = convertTouchesToArray(e.changedTouches)
const { x } = (touches && touches[0]) || {}
const touch = (x ? touches[0] : this.getRelative(e, touches)) || {}
const handler = this.chart.getZr().handler
const isClick = Math.abs(touch.x - this.startX) < 10 && new Date() - this.startT < 200
dispatch.call(handler, 'mouseup', touch)
handler.processGesture(wrapTouch(e), 'end')
if (isClick) {
dispatch.call(handler, 'click', touch)
} else {
this.endTimer = setTimeout(() => {
dispatch.call(handler, 'mousemove', { x: 999999999, y: 999999999 })
dispatch.call(handler, 'mouseup', { x: 999999999, y: 999999999 })
}, 50)
}
}
},
// #endif
// #ifdef H5
mousewheel(e) {
if (this.chart) {
dispatch.call(this.chart.getZr().handler, 'mousewheel', this.getTouch(e))
}
},
// #endif
},
}
</script>
<style>
.lime-echart {
position: relative;
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
.lime-echart__canvas {
/* #ifndef APP-NVUE */
width: 100%;
height: 100%;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
}
/* #ifndef APP-NVUE */
.lime-echart__mask {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
z-index: 1;
}
/* #endif */
</style>