2023-12-05 16:45:28 +08:00
|
|
|
|
const cacheChart = {}
|
2025-08-14 11:22:53 +08:00
|
|
|
|
const fontSizeReg = /([\d\.]+)px/
|
2023-12-05 16:45:28 +08:00
|
|
|
|
class EventEmit {
|
2025-08-14 11:22:53 +08:00
|
|
|
|
constructor() {
|
|
|
|
|
|
this.__events = {}
|
|
|
|
|
|
}
|
|
|
|
|
|
on(type, listener) {
|
|
|
|
|
|
if (!type || !listener) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const events = this.__events[type] || []
|
|
|
|
|
|
events.push(listener)
|
|
|
|
|
|
this.__events[type] = events
|
|
|
|
|
|
}
|
|
|
|
|
|
emit(type, e) {
|
|
|
|
|
|
if (type.constructor === Object) {
|
|
|
|
|
|
e = type
|
|
|
|
|
|
type = e && e.type
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!type) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const events = this.__events[type]
|
|
|
|
|
|
if (!events || !events.length) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
events.forEach(listener => {
|
|
|
|
|
|
listener.call(this, e)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
off(type, listener) {
|
|
|
|
|
|
const __events = this.__events
|
|
|
|
|
|
const events = __events[type]
|
|
|
|
|
|
if (!events || !events.length) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!listener) {
|
|
|
|
|
|
delete __events[type]
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
for (let i = 0, len = events.length; i < len; i++) {
|
|
|
|
|
|
if (events[i] === listener) {
|
|
|
|
|
|
events.splice(i, 1)
|
|
|
|
|
|
i--
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
class Image {
|
2025-08-14 11:22:53 +08:00
|
|
|
|
constructor() {
|
|
|
|
|
|
this.currentSrc = null
|
|
|
|
|
|
this.naturalHeight = 0
|
|
|
|
|
|
this.naturalWidth = 0
|
|
|
|
|
|
this.width = 0
|
|
|
|
|
|
this.height = 0
|
|
|
|
|
|
this.tagName = 'IMG'
|
|
|
|
|
|
}
|
|
|
|
|
|
set src(src) {
|
|
|
|
|
|
this.currentSrc = src
|
|
|
|
|
|
uni.getImageInfo({
|
|
|
|
|
|
src,
|
|
|
|
|
|
success: res => {
|
|
|
|
|
|
this.naturalWidth = this.width = res.width
|
|
|
|
|
|
this.naturalHeight = this.height = res.height
|
|
|
|
|
|
this.onload()
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: () => {
|
|
|
|
|
|
this.onerror()
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
get src() {
|
|
|
|
|
|
return this.currentSrc
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
class OffscreenCanvas {
|
2025-08-14 11:22:53 +08:00
|
|
|
|
constructor(ctx, com, canvasId) {
|
|
|
|
|
|
this.tagName = 'canvas'
|
|
|
|
|
|
this.com = com
|
|
|
|
|
|
this.canvasId = canvasId
|
|
|
|
|
|
this.ctx = ctx
|
|
|
|
|
|
}
|
|
|
|
|
|
set width(w) {
|
|
|
|
|
|
this.com.offscreenWidth = w
|
|
|
|
|
|
}
|
|
|
|
|
|
set height(h) {
|
|
|
|
|
|
this.com.offscreenHeight = h
|
|
|
|
|
|
}
|
|
|
|
|
|
get width() {
|
|
|
|
|
|
return this.com.offscreenWidth || 0
|
|
|
|
|
|
}
|
|
|
|
|
|
get height() {
|
|
|
|
|
|
return this.com.offscreenHeight || 0
|
|
|
|
|
|
}
|
|
|
|
|
|
getContext(type) {
|
|
|
|
|
|
return this.ctx
|
|
|
|
|
|
}
|
|
|
|
|
|
getImageData() {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
this.com.$nextTick(() => {
|
|
|
|
|
|
uni.canvasGetImageData(
|
|
|
|
|
|
{
|
|
|
|
|
|
x: 0,
|
|
|
|
|
|
y: 0,
|
|
|
|
|
|
width: this.com.offscreenWidth,
|
|
|
|
|
|
height: this.com.offscreenHeight,
|
|
|
|
|
|
canvasId: this.canvasId,
|
|
|
|
|
|
success: res => {
|
|
|
|
|
|
resolve(res)
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: err => {
|
|
|
|
|
|
reject(err)
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
this.com
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
export class Canvas {
|
2025-08-14 11:22:53 +08:00
|
|
|
|
constructor(ctx, com, isNew, canvasNode = {}) {
|
|
|
|
|
|
cacheChart[com.canvasId] = { ctx }
|
|
|
|
|
|
this.canvasId = com.canvasId
|
|
|
|
|
|
this.chart = null
|
|
|
|
|
|
this.isNew = isNew
|
|
|
|
|
|
this.tagName = 'canvas'
|
|
|
|
|
|
this.canvasNode = canvasNode
|
|
|
|
|
|
this.com = com
|
|
|
|
|
|
if (!isNew) {
|
|
|
|
|
|
this._initStyle(ctx)
|
|
|
|
|
|
}
|
|
|
|
|
|
this._initEvent()
|
|
|
|
|
|
this._ee = new EventEmit()
|
|
|
|
|
|
}
|
|
|
|
|
|
getContext(type) {
|
|
|
|
|
|
if (type === '2d') {
|
|
|
|
|
|
return this.ctx
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
setAttribute(key, value) {
|
|
|
|
|
|
if (key === 'aria-label') {
|
|
|
|
|
|
this.com['ariaLabel'] = value
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
setChart(chart) {
|
|
|
|
|
|
this.chart = chart
|
|
|
|
|
|
}
|
|
|
|
|
|
createOffscreenCanvas(param) {
|
|
|
|
|
|
if (!this.children) {
|
|
|
|
|
|
this.com.isOffscreenCanvas = true
|
|
|
|
|
|
this.com.offscreenWidth = param.width || 300
|
|
|
|
|
|
this.com.offscreenHeight = param.height || 300
|
|
|
|
|
|
const com = this.com
|
|
|
|
|
|
const canvasId = this.com.offscreenCanvasId
|
|
|
|
|
|
const context = uni.createCanvasContext(canvasId, this.com)
|
|
|
|
|
|
this._initStyle(context)
|
|
|
|
|
|
this.children = new OffscreenCanvas(context, com, canvasId)
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.children
|
|
|
|
|
|
}
|
|
|
|
|
|
appendChild(child) {
|
|
|
|
|
|
console.log('child', child)
|
|
|
|
|
|
}
|
|
|
|
|
|
dispatchEvent(type, e) {
|
|
|
|
|
|
if (typeof type == 'object') {
|
|
|
|
|
|
this._ee.emit(type.type, type)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this._ee.emit(type, e)
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
attachEvent() {}
|
|
|
|
|
|
detachEvent() {}
|
|
|
|
|
|
addEventListener(type, listener) {
|
|
|
|
|
|
this._ee.on(type, listener)
|
|
|
|
|
|
}
|
|
|
|
|
|
removeEventListener(type, listener) {
|
|
|
|
|
|
this._ee.off(type, listener)
|
|
|
|
|
|
}
|
|
|
|
|
|
_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, font);
|
|
|
|
|
|
// });
|
|
|
|
|
|
}
|
|
|
|
|
|
_initStyle(ctx, child) {
|
|
|
|
|
|
const styles = [
|
|
|
|
|
|
'fillStyle',
|
|
|
|
|
|
'strokeStyle',
|
|
|
|
|
|
'fontSize',
|
|
|
|
|
|
'globalAlpha',
|
|
|
|
|
|
'opacity',
|
|
|
|
|
|
'textAlign',
|
|
|
|
|
|
'textBaseline',
|
|
|
|
|
|
'shadow',
|
|
|
|
|
|
'lineWidth',
|
|
|
|
|
|
'lineCap',
|
|
|
|
|
|
'lineJoin',
|
|
|
|
|
|
'lineDash',
|
|
|
|
|
|
'miterLimit',
|
|
|
|
|
|
// 'font'
|
|
|
|
|
|
]
|
|
|
|
|
|
const colorReg = /#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\b/g
|
|
|
|
|
|
styles.forEach(style => {
|
|
|
|
|
|
Object.defineProperty(ctx, style, {
|
|
|
|
|
|
set: value => {
|
|
|
|
|
|
// if (style === 'font' && fontSizeReg.test(value)) {
|
|
|
|
|
|
// const match = fontSizeReg.exec(value);
|
|
|
|
|
|
// ctx.setFontSize(match[1]);
|
|
|
|
|
|
// return;
|
|
|
|
|
|
// }
|
|
|
|
|
|
if (style === 'opacity') {
|
|
|
|
|
|
ctx.setGlobalAlpha(value)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (
|
|
|
|
|
|
(style !== 'fillStyle' && style !== 'strokeStyle') ||
|
|
|
|
|
|
(value !== 'none' && value !== null)
|
|
|
|
|
|
) {
|
|
|
|
|
|
// #ifdef H5 || APP-PLUS || MP-BAIDU
|
|
|
|
|
|
if (typeof value == 'object') {
|
|
|
|
|
|
if (value.hasOwnProperty('colorStop') || value.hasOwnProperty('colors')) {
|
|
|
|
|
|
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value)
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
// #ifdef MP-TOUTIAO
|
|
|
|
|
|
if (colorReg.test(value)) {
|
|
|
|
|
|
value = value.replace(colorReg, '#$1$1$2$2$3$3')
|
|
|
|
|
|
}
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
if (!this.isNew && !child) {
|
|
|
|
|
|
ctx.uniDrawImage = ctx.drawImage
|
|
|
|
|
|
ctx.drawImage = (...a) => {
|
|
|
|
|
|
a[0] = a[0].src
|
|
|
|
|
|
ctx.uniDrawImage(...a)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!ctx.createRadialGradient) {
|
|
|
|
|
|
ctx.createRadialGradient = function () {
|
|
|
|
|
|
return ctx.createCircularGradient(...[...arguments].slice(-3))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 字节不支持
|
|
|
|
|
|
if (!ctx.strokeText) {
|
|
|
|
|
|
ctx.strokeText = (...a) => {
|
|
|
|
|
|
ctx.fillText(...a)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 钉钉不支持
|
|
|
|
|
|
if (!ctx.measureText) {
|
|
|
|
|
|
const strLen = str => {
|
|
|
|
|
|
let len = 0
|
|
|
|
|
|
for (let i = 0; i < str.length; i++) {
|
|
|
|
|
|
if (str.charCodeAt(i) > 0 && str.charCodeAt(i) < 128) {
|
|
|
|
|
|
len++
|
|
|
|
|
|
} else {
|
|
|
|
|
|
len += 2
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return len
|
|
|
|
|
|
}
|
|
|
|
|
|
ctx.measureText = (text, font) => {
|
|
|
|
|
|
let fontSize = ctx?.state?.fontSize || 12
|
|
|
|
|
|
if (font) {
|
|
|
|
|
|
fontSize = parseInt(font.match(/([\d\.]+)px/)[1])
|
|
|
|
|
|
}
|
|
|
|
|
|
fontSize /= 2
|
|
|
|
|
|
let isBold = fontSize >= 16
|
|
|
|
|
|
const widthFactor = isBold ? 1.3 : 1
|
|
|
|
|
|
return {
|
|
|
|
|
|
width: strLen(text) * fontSize * widthFactor,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
|
2025-08-14 11:22:53 +08:00
|
|
|
|
_initEvent(e) {
|
|
|
|
|
|
this.event = {}
|
|
|
|
|
|
const eventNames = [
|
|
|
|
|
|
{
|
|
|
|
|
|
wxName: 'touchStart',
|
|
|
|
|
|
ecName: 'mousedown',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
wxName: 'touchMove',
|
|
|
|
|
|
ecName: 'mousemove',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
wxName: 'touchEnd',
|
|
|
|
|
|
ecName: 'mouseup',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
wxName: 'touchEnd',
|
|
|
|
|
|
ecName: 'click',
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
2023-12-05 16:45:28 +08:00
|
|
|
|
|
2025-08-14 11:22:53 +08:00
|
|
|
|
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,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
|
2025-08-14 11:22:53 +08:00
|
|
|
|
set width(w) {
|
|
|
|
|
|
this.canvasNode.width = w
|
|
|
|
|
|
}
|
|
|
|
|
|
set height(h) {
|
|
|
|
|
|
this.canvasNode.height = h
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
|
2025-08-14 11:22:53 +08:00
|
|
|
|
get width() {
|
|
|
|
|
|
return this.canvasNode.width || 0
|
|
|
|
|
|
}
|
|
|
|
|
|
get height() {
|
|
|
|
|
|
return this.canvasNode.height || 0
|
|
|
|
|
|
}
|
|
|
|
|
|
get ctx() {
|
|
|
|
|
|
return cacheChart[this.canvasId]['ctx'] || null
|
|
|
|
|
|
}
|
|
|
|
|
|
set chart(chart) {
|
|
|
|
|
|
cacheChart[this.canvasId]['chart'] = chart
|
|
|
|
|
|
}
|
|
|
|
|
|
get chart() {
|
|
|
|
|
|
return cacheChart[this.canvasId]['chart'] || null
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-14 11:22:53 +08:00
|
|
|
|
export function dispatch(name, { x, y, wheelDelta }) {
|
|
|
|
|
|
this.dispatch(name, {
|
|
|
|
|
|
zrX: x,
|
|
|
|
|
|
zrY: y,
|
|
|
|
|
|
zrDelta: wheelDelta,
|
|
|
|
|
|
preventDefault: () => {},
|
|
|
|
|
|
stopPropagation: () => {},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
export function setCanvasCreator(echarts, { canvas, node }) {
|
|
|
|
|
|
// echarts.setCanvasCreator(() => canvas);
|
|
|
|
|
|
if (echarts && !echarts.registerPreprocessor) {
|
|
|
|
|
|
return console.warn('echarts 版本不对或未传入echarts,vue3请使用esm格式')
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
function loadImage(src, onload, onerror) {
|
|
|
|
|
|
let img = null
|
|
|
|
|
|
if (node && node.createImage) {
|
|
|
|
|
|
img = node.createImage()
|
|
|
|
|
|
img.onload = onload.bind(img)
|
|
|
|
|
|
img.onerror = onerror.bind(img)
|
|
|
|
|
|
img.src = src
|
|
|
|
|
|
return img
|
|
|
|
|
|
} else {
|
|
|
|
|
|
img = new Image()
|
|
|
|
|
|
img.onload = onload.bind(img)
|
|
|
|
|
|
img.onerror = onerror.bind(img)
|
|
|
|
|
|
img.src = src
|
|
|
|
|
|
return img
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (echarts.setPlatformAPI) {
|
|
|
|
|
|
echarts.setPlatformAPI({
|
|
|
|
|
|
loadImage: canvas.setChart ? loadImage : null,
|
|
|
|
|
|
createCanvas() {
|
|
|
|
|
|
const key = 'createOffscreenCanvas'
|
|
|
|
|
|
return uni.canIUse(key) && uni[key] ? uni[key]({ type: '2d' }) : canvas
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2023-12-05 16:45:28 +08:00
|
|
|
|
}
|