<template> <canvas v-if="use2dCanvas" :id="canvasId" type="2d" :style="style"></canvas> <canvas v-else :canvas-id="canvasId" :style="style" :id="canvasId" :width="boardWidth * dpr" :height="boardHeight * dpr"></canvas> </template> <script> import { toPx, base64ToPath, compareVersion} from './utils'; import { Draw } from './draw'; import { adaptor } from './canvas'; export default { // version: '1.5.9.2', name: 'l-painter', props: { board: Object, fileType: { type: String, default: 'png' }, width: [Number, String], height: [Number, String], pixelRatio: Number, customStyle: String, isRenderImage: Boolean, isBase64ToPath: Boolean, isH5PathToBase64: Boolean, sleep: { type: Number, default: 1000/30 }, type: { type: String, default: '2d', } }, data() { return { // #ifndef MP-WEIXIN || MP-QQ canvasId: `l-painter_${this._uid}`, // #endif // #ifdef MP-WEIXIN || MP-QQ canvasId: `l-painter`, // #endif // #ifdef MP-WEIXIN use2dCanvas: true, // #endif // #ifndef MP-WEIXIN use2dCanvas: false, // #endif draw: null, ctx: null }; }, computed: { newboard() { return this.board && JSON.parse(JSON.stringify(this.board)) }, style() { return `width:${this.boardWidth}px; height: ${this.boardHeight}px; ${this.customStyle}`; }, dpr() { return this.pixelRatio || uni.getSystemInfoSync().pixelRatio; }, boardWidth() { const { width = 200 } = this.board || {}; return toPx(this.width || width); }, boardHeight() { const { height = 200 } = this.board || {}; return toPx(this.height || height); } }, mounted() { // #ifdef MP-WEIXIN const {SDKVersion} = wx.getSystemInfoSync() this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '2.9.2') >= 0; // #endif this.$watch('newboard', async (val, old) => { if (JSON.stringify(val) === '{}' || !val) return; const {width: w, height: h} = val || {}; const {width: ow, height: oh} = old || {}; if(w !== ow || h !== oh) { this.inited = false; } this.render(); }, { deep: true, immediate: true, }) }, methods: { async render(args = {}, single = false) { const ctx = await this.getContext() const { use2dCanvas, boardWidth, boardHeight, board, canvas, isBase64ToPath, isH5PathToBase64, sleep } = this; if (use2dCanvas && !canvas) { return Promise.reject(new Error('render: fail canvas has not been created')); } if(!this.boundary) { this.boundary = { top: 0, left: 0, width: boardWidth, height: boardHeight, sleep } } if(!single) { ctx.clearRect(0, 0, boardWidth, boardHeight); } if(!this.draw) { this.draw = new Draw(ctx, canvas, use2dCanvas, isH5PathToBase64, this.boundary); } if(JSON.stringify(args) != '{}' || board && JSON.stringify(board) != '{}') { await this.draw.drawBoard(JSON.stringify(args) != '{}' ? args : board); } await new Promise(resolve => this.$nextTick(resolve)) if (!use2dCanvas && !single) { await this.canvasDraw(ctx); } this.$emit('done') if(this.isRenderImage && !single) { this.canvasToTempFilePath() .then(async res => { if(/^data:image\/(\w+);base64/.test(res.tempFilePath) && isBase64ToPath) { const img = await base64ToPath(res.tempFilePath) this.$emit('success', img) } else { this.$emit('success', res.tempFilePath) } }) .catch(err => { this.$emit('fail', err) new Error(JSON.stringify(err)) console.error(JSON.stringify(err)) }) } return Promise.resolve({ctx, draw: this.draw}); }, async custom(cb) { const {ctx, draw} = await this.render({}, true) ctx.save() await cb(ctx, draw) ctx.restore() return Promise.resolve(true); }, async single(args = {}) { await this.render(args, true) return Promise.resolve(true); }, canvasDraw(flag = false) { const {ctx} = this return new Promise(resolve => { ctx.draw(flag, () => { resolve(true); }); }); }, async getContext() { if(this.ctx && this.inited) { return Promise.resolve(this.ctx) }; const { type, use2dCanvas, dpr, boardWidth, boardHeight } = this; const _getContext = () => { return new Promise(resolve => { uni.createSelectorQuery() .in(this) .select('#' + this.canvasId) .boundingClientRect() .exec(res => { if(res) { const ctx = uni.createCanvasContext(this.canvasId, this); if (!this.inited) { this.inited = true; this.use2dCanvas = false; this.canvas = res } // #ifdef MP-ALIPAY ctx.scale(dpr, dpr); // #endif this.ctx = ctx resolve(ctx); } }) }) } // #ifndef MP-WEIXIN return _getContext() // #endif if(!use2dCanvas) { return _getContext() } return new Promise(resolve => { uni.createSelectorQuery() .in(this) .select('#l-painter') .node() .exec(res => { const canvas = res[0].node; if(!canvas) { this.use2dCanvas = false; return this.getContext() } const ctx = canvas.getContext(type); if (!this.inited) { this.inited = true; canvas.width = boardWidth * dpr; canvas.height = boardHeight * dpr; this.use2dCanvas = true; this.canvas = canvas ctx.scale(dpr, dpr); } this.ctx = adaptor(ctx) resolve(adaptor(ctx)); }); }); }, canvasToTempFilePath(args = {}) { const {use2dCanvas, canvasId} = this return new Promise((resolve, reject) => { let { top = 0, left = 0, width, height } = this.boundary || this let destWidth = width * this.dpr let destHeight = height * this.dpr // #ifdef MP-ALIPAY width = width * this.dpr height = height * this.dpr // #endif const copyArgs = { x: left, y: top, width, height, destWidth, destHeight, canvasId, fileType: args.fileType || this.fileType, quality: args.quality || 1, success: resolve, fail: reject } if (use2dCanvas) { delete copyArgs.canvasId copyArgs.canvas = this.canvas } uni.canvasToTempFilePath(copyArgs, this) }) } } }; </script> <style></style>