You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
8.8 KiB
393 lines
8.8 KiB
const cacheChart = {} |
|
const fontSizeReg = /([\d\.]+)px/; |
|
class EventEmit { |
|
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--; |
|
} |
|
} |
|
} |
|
} |
|
class Image { |
|
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 |
|
} |
|
} |
|
class OffscreenCanvas { |
|
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) |
|
}) |
|
}) |
|
} |
|
} |
|
export class Canvas { |
|
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', |
|
// #ifdef H5 |
|
'font', |
|
// #endif |
|
]; |
|
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 => { |
|
// #ifdef H5 |
|
if (style === 'font' && fontSizeReg.test(value)) { |
|
const match = fontSizeReg.exec(value); |
|
ctx.setFontSize(match[1]); |
|
return; |
|
} |
|
// #endif |
|
|
|
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 || uni.getSystemInfoSync().osName == 'harmonyos') { |
|
ctx._measureText = 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; |
|
// ctx._measureText(text, (res) => {}) |
|
return { |
|
width: strLen(text) * fontSize * widthFactor |
|
}; |
|
} |
|
} |
|
} |
|
|
|
_initEvent(e) { |
|
this.event = {}; |
|
const eventNames = [{ |
|
wxName: 'touchStart', |
|
ecName: 'mousedown' |
|
}, { |
|
wxName: 'touchMove', |
|
ecName: 'mousemove' |
|
}, { |
|
wxName: 'touchEnd', |
|
ecName: 'mouseup' |
|
}, { |
|
wxName: 'touchEnd', |
|
ecName: 'click' |
|
}]; |
|
|
|
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 |
|
}); |
|
}; |
|
}); |
|
} |
|
|
|
set width(w) { |
|
this.canvasNode.width = w |
|
} |
|
set height(h) { |
|
this.canvasNode.height = h |
|
} |
|
|
|
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 |
|
} |
|
} |
|
|
|
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 |
|
} |
|
}) |
|
} |
|
} |