volunteer-secondary/src/chart-sub/uni_modules/lime-echart/components/l-echart/l-echart.vue

551 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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
: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/uvue.html?v=1"
@pagefinish="finished = true"
@onPostMessage="onMessage"
></web-view>
<!-- #endif -->
</view>
</template>
<script>
// @ts-nocheck
// #ifndef APP-NVUE
import { Canvas, setCanvasCreator, dispatch } from './canvas'
import {
wrapTouch,
convertTouchesToArray,
devicePixelRatio,
sleep,
canIUseCanvas2d,
getRect,
getDeviceInfo,
} from './utils'
// #endif
// #ifdef APP-NVUE
import { base64ToPath, sleep } from './utils'
import { Echarts } from './nvue'
// #endif
/**
* LimeChart 图表
* @description 全端兼容的eCharts
* @tutorial https://ext.dcloud.net.cn/plugin?id=4899
* @property {String} customStyle 自定义样式
* @property {String} type 指定 canvas 类型
* @value 2d 使用canvas 2d部分小程序支持
* @value '' 使用原生canvas会有层级问题
* @value bottom right 不缩放图片,只显示图片的右下边区域
* @property {Boolean} isDisableScroll
* @property {number} beforeDelay = [30] 延迟初始化 (毫秒)
* @property {Boolean} enableHover PC端使用鼠标悬浮
* @event {Function} finished 加载完成触发
*/
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,
},
landscape: Boolean,
},
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: {
rootStyle() {
if (this.landscape) {
return `transform: translate(-50%,-50%) rotate(90deg); top:50%; left:50%;`
} else {
return ``
}
},
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.rootStyle +
(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
beforeUnmount() {
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 } = getDeviceInfo()
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 detail = e?.detail?.data[0] || null
const data = detail?.data
const key = detail?.event
const options = data?.options
const event = data?.event
const file = detail?.file
if (key == 'log' && data) {
console.log(data)
}
if (event) {
this.chart.dispatchAction(event.replace(/"/g, ''), options)
}
if (file) {
thie.file = file
}
},
// #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.isDisposed()) {
this.chart.clear()
}
},
dispose() {
if (this.chart && !this.chart.isDisposed()) {
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(() => {
getRect('.lime-echart', this).then((res) => {
if (res) {
let { width, height } = res
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 (args && args.length == 0 && !echarts) {
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)
try {
this.chart = echarts.init(config.canvas, theme, Object.assign({}, config, opts || {}))
callback?.(this.chart)
return this.chart
} catch (e) {
console.error('【lime-echarts】:', e)
return null
}
// #endif
// #ifdef APP-NVUE
this.chart = new Echarts(this.$refs.webview)
this.$refs.webview.evalJs(`init(null, null, ${JSON.stringify(opts)}, ${theme})`)
callback?.(this.chart)
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}`, this, this.use2dCanvas).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
// #ifdef WEB
dpr = 1
// #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]) || {}
const touch = x ? touches[0] : this.getRelative(e, touches)
if (this.landscape) {
;[touch.x, touch.y] = [touch.y, this.height - touch.x]
}
return touch
},
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)) || {}
if (this.landscape) {
;[touch.x, touch.y] = [touch.y, this.height - touch.x]
}
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>