diff --git a/lib/gradient.js b/lib/gradient.js index f0729e4..13c7258 100644 --- a/lib/gradient.js +++ b/lib/gradient.js @@ -35,7 +35,7 @@ function radialEffect(width, height, bg, ctx) { const colorPer = analizeGrad(bg.match(/radial-gradient\((.+)\)/)[1]); - const grd = ctx.createCircularGradient(0, 0, width < height ? height / 2 : width / 2); + const grd = ctx.createRadialGradient(0, 0, 0, 0, 0, width < height ? height / 2 : width / 2); for (let i = 0; i < colorPer.colors.length; i++) { grd.addColorStop(colorPer.percents[i], colorPer.colors[i]); } diff --git a/lib/pen.js b/lib/pen.js index aa290fa..bef8897 100644 --- a/lib/pen.js +++ b/lib/pen.js @@ -532,7 +532,7 @@ export default class Painter { this.ctx.fillStyle = (view.css.color || 'black'); if (this.isMoving && JSON.stringify(this.movingCache) !== JSON.stringify({})) { this.globalWidth[view.id] = this.movingCache.globalWidth - this.ctx.setTextAlign(view.css.textAlign ? view.css.textAlign : 'left'); + this.ctx.textAlign = view.css.textAlign ? view.css.textAlign : 'left'; for (const i of this.movingCache.lineArray) { const { measuredWith, @@ -620,7 +620,7 @@ export default class Painter { text += '...'; measuredWith = this.ctx.measureText(text).width; } - this.ctx.setTextAlign(view.css.textAlign ? view.css.textAlign : 'left'); + this.ctx.textAlign = view.css.textAlign ? view.css.textAlign : 'left'; let x; let lineX; switch (view.css.textAlign) { diff --git a/lib/qrcode.js b/lib/qrcode.js index 264c202..adf1b64 100644 --- a/lib/qrcode.js +++ b/lib/qrcode.js @@ -765,10 +765,10 @@ var frame = that.getFrame(str); var px = size / width; if (bg) { - ctx.setFillStyle(bg) + ctx.fillStyle = bg; ctx.fillRect(startX, startY, cavW, cavW); } - ctx.setFillStyle(color || 'black'); + ctx.fillStyle = color || 'black'; for (var i = 0; i < width; i++) { for (var j = 0; j < width; j++) { if (frame[j * width + i]) { diff --git a/lib/wx-canvas.js b/lib/wx-canvas.js new file mode 100644 index 0000000..4711390 --- /dev/null +++ b/lib/wx-canvas.js @@ -0,0 +1,611 @@ +// @ts-check +export default class WxCanvas { + ctx; + type; + canvasId; + canvasNode; + stepList = []; + canvasPrototype = {}; + + constructor(type, ctx, canvasId, isNew, canvasNode) { + this.ctx = ctx; + this.canvasId = canvasId; + this.type = type; + if (isNew) { + this.canvasNode = canvasNode || {}; + } + } + + set width(w) { + if (this.canvasNode) this.canvasNode.width = w; + } + + get width() { + if (this.canvasNode) return this.canvasNode.width; + return 0; + } + + set height(h) { + if (this.canvasNode) this.canvasNode.height = h; + } + + get height() { + if (this.canvasNode) return this.canvasNode.height; + return 0; + } + + set lineWidth(args) { + this.canvasPrototype.lineWidth = args; + this.stepList.push({ + action: "lineWidth", + args, + actionType: "set", + }); + } + + get lineWidth() { + return this.canvasPrototype.lineWidth; + } + + set lineCap(args) { + this.canvasPrototype.lineCap = args; + this.stepList.push({ + action: "lineCap", + args, + actionType: "set", + }); + } + + get lineCap() { + return this.canvasPrototype.lineCap; + } + + set lineJoin(args) { + this.canvasPrototype.lineJoin = args; + this.stepList.push({ + action: "lineJoin", + args, + actionType: "set", + }); + } + + get lineJoin() { + return this.canvasPrototype.lineJoin; + } + + set miterLimit(args) { + this.canvasPrototype.miterLimit = args; + this.stepList.push({ + action: "miterLimit", + args, + actionType: "set", + }); + } + + get miterLimit() { + return this.canvasPrototype.miterLimit; + } + + set lineDashOffset(args) { + this.canvasPrototype.lineDashOffset = args; + this.stepList.push({ + action: "lineDashOffset", + args, + actionType: "set", + }); + } + + get lineDashOffset() { + return this.canvasPrototype.lineDashOffset; + } + + set font(args) { + this.canvasPrototype.font = args; + this.ctx.font = args; + this.stepList.push({ + action: "font", + args, + actionType: "set", + }); + } + + get font() { + return this.canvasPrototype.font; + } + + set textAlign(args) { + this.canvasPrototype.textAlign = args; + this.stepList.push({ + action: "textAlign", + args, + actionType: "set", + }); + } + + get textAlign() { + return this.canvasPrototype.textAlign; + } + + set textBaseline(args) { + this.canvasPrototype.textBaseline = args; + this.stepList.push({ + action: "textBaseline", + args, + actionType: "set", + }); + } + + get textBaseline() { + return this.canvasPrototype.textBaseline; + } + + set fillStyle(args) { + this.canvasPrototype.fillStyle = args; + this.stepList.push({ + action: "fillStyle", + args, + actionType: "set", + }); + } + + get fillStyle() { + return this.canvasPrototype.fillStyle; + } + + set strokeStyle(args) { + this.canvasPrototype.strokeStyle = args; + this.stepList.push({ + action: "strokeStyle", + args, + actionType: "set", + }); + } + + get strokeStyle() { + return this.canvasPrototype.strokeStyle; + } + + set globalAlpha(args) { + this.canvasPrototype.globalAlpha = args; + this.stepList.push({ + action: "globalAlpha", + args, + actionType: "set", + }); + } + + get globalAlpha() { + return this.canvasPrototype.globalAlpha; + } + + set globalCompositeOperation(args) { + this.canvasPrototype.globalCompositeOperation = args; + this.stepList.push({ + action: "globalCompositeOperation", + args, + actionType: "set", + }); + } + + get globalCompositeOperation() { + return this.canvasPrototype.globalCompositeOperation; + } + + set shadowColor(args) { + this.canvasPrototype.shadowColor = args; + this.stepList.push({ + action: "shadowColor", + args, + actionType: "set", + }); + } + + get shadowColor() { + return this.canvasPrototype.shadowColor; + } + + set shadowOffsetX(args) { + this.canvasPrototype.shadowOffsetX = args; + this.stepList.push({ + action: "shadowOffsetX", + args, + actionType: "set", + }); + } + + get shadowOffsetX() { + return this.canvasPrototype.shadowOffsetX; + } + + set shadowOffsetY(args) { + this.canvasPrototype.shadowOffsetY = args; + this.stepList.push({ + action: "shadowOffsetY", + args, + actionType: "set", + }); + } + + get shadowOffsetY() { + return this.canvasPrototype.shadowOffsetY; + } + + set shadowBlur(args) { + this.canvasPrototype.shadowBlur = args; + this.stepList.push({ + action: "shadowBlur", + args, + actionType: "set", + }); + } + + get shadowBlur() { + return this.canvasPrototype.shadowBlur; + } + + save() { + this.stepList.push({ + action: "save", + args: null, + actionType: "func", + }); + } + + restore() { + this.stepList.push({ + action: "restore", + args: null, + actionType: "func", + }); + } + + setLineDash(...args) { + this.canvasPrototype.lineDash = args; + this.stepList.push({ + action: "setLineDash", + args, + actionType: "func", + }); + } + + moveTo(...args) { + this.stepList.push({ + action: "moveTo", + args, + actionType: "func", + }); + } + + closePath() { + this.stepList.push({ + action: "closePath", + args: null, + actionType: "func", + }); + } + + lineTo(...args) { + this.stepList.push({ + action: "lineTo", + args, + actionType: "func", + }); + } + + quadraticCurveTo(...args) { + this.stepList.push({ + action: "quadraticCurveTo", + args, + actionType: "func", + }); + } + + bezierCurveTo(...args) { + this.stepList.push({ + action: "bezierCurveTo", + args, + actionType: "func", + }); + } + + arcTo(...args) { + this.stepList.push({ + action: "arcTo", + args, + actionType: "func", + }); + } + + arc(...args) { + this.stepList.push({ + action: "arc", + args, + actionType: "func", + }); + } + + rect(...args) { + this.stepList.push({ + action: "rect", + args, + actionType: "func", + }); + } + + scale(...args) { + this.stepList.push({ + action: "scale", + args, + actionType: "func", + }); + } + + rotate(...args) { + this.stepList.push({ + action: "rotate", + args, + actionType: "func", + }); + } + + translate(...args) { + this.stepList.push({ + action: "translate", + args, + actionType: "func", + }); + } + + transform(...args) { + this.stepList.push({ + action: "transform", + args, + actionType: "func", + }); + } + + setTransform(...args) { + this.stepList.push({ + action: "setTransform", + args, + actionType: "func", + }); + } + + clearRect(...args) { + this.stepList.push({ + action: "clearRect", + args, + actionType: "func", + }); + } + + fillRect(...args) { + this.stepList.push({ + action: "fillRect", + args, + actionType: "func", + }); + } + + strokeRect(...args) { + this.stepList.push({ + action: "strokeRect", + args, + actionType: "func", + }); + } + + fillText(...args) { + this.stepList.push({ + action: "fillText", + args, + actionType: "func", + }); + } + + strokeText(...args) { + this.stepList.push({ + action: "strokeText", + args, + actionType: "func", + }); + } + + beginPath() { + this.stepList.push({ + action: "beginPath", + args: null, + actionType: "func", + }); + } + + fill() { + this.stepList.push({ + action: "fill", + args: null, + actionType: "func", + }); + } + + stroke() { + this.stepList.push({ + action: "stroke", + args: null, + actionType: "func", + }); + } + + drawFocusIfNeeded(...args) { + this.stepList.push({ + action: "drawFocusIfNeeded", + args, + actionType: "func", + }); + } + + clip() { + this.stepList.push({ + action: "clip", + args: null, + actionType: "func", + }); + } + + isPointInPath(...args) { + this.stepList.push({ + action: "isPointInPath", + args, + actionType: "func", + }); + } + + drawImage(...args) { + this.stepList.push({ + action: "drawImage", + args, + actionType: "func", + }); + } + + addHitRegion(...args) { + this.stepList.push({ + action: "addHitRegion", + args, + actionType: "func", + }); + } + + removeHitRegion(...args) { + this.stepList.push({ + action: "removeHitRegion", + args, + actionType: "func", + }); + } + + clearHitRegions(...args) { + this.stepList.push({ + action: "clearHitRegions", + args, + actionType: "func", + }); + } + + putImageData(...args) { + this.stepList.push({ + action: "putImageData", + args, + actionType: "func", + }); + } + + getLineDash() { + return this.canvasPrototype.lineDash; + } + + createLinearGradient(...args) { + return this.ctx.createLinearGradient(...args); + } + + createRadialGradient(...args) { + if (this.type === "2d") { + return this.ctx.createRadialGradient(...args); + } else { + return this.ctx.createCircularGradient(...args.slice(3, 6)); + } + } + + createPattern(...args) { + return this.ctx.createPattern(...args); + } + + measureText(...args) { + return this.ctx.measureText(...args); + } + + createImageData(...args) { + return this.ctx.createImageData(...args); + } + + getImageData(...args) { + return this.ctx.getImageData(...args); + } + + async draw(reserve, func) { + const realstepList = this.stepList.slice(); + this.stepList.length = 0; + if (this.type === "mina") { + if (realstepList.length > 0) { + for (const step of realstepList) { + this.implementMinaStep(step); + } + this.ctx.draw(reserve, func); + realstepList.length = 0; + } + } else if (this.type === "2d") { + if (!reserve) { + this.ctx.clearRect(0, 0, this.canvasNode.width, this.canvasNode.height); + } + if (realstepList.length > 0) { + for (const step of realstepList) { + await this.implement2DStep(step); + } + realstepList.length = 0; + } + if (func) { + func(); + } + } + realstepList.length = 0; + } + + implementMinaStep(step) { + switch (step.action) { + case "textAlign": { + this.ctx.setTextAlign(step.args); + break; + } + case "textBaseline": { + this.ctx.setTextBaseline(step.args); + break; + } + default: { + if (step.actionType === "set") { + this.ctx[step.action] = step.args; + } else if (step.actionType === "func") { + if (step.args) { + this.ctx[step.action](...step.args); + } else { + this.ctx[step.action](); + } + } + break; + } + } + } + + implement2DStep(step) { + return new Promise((resolve) => { + if (step.action === "drawImage") { + const img = this.canvasNode.createImage(); + img.src = step.args[0]; + img.onload = () => { + this.ctx.drawImage(img, ...step.args.slice(1)); + resolve(); + }; + } else { + if (step.actionType === "set") { + this.ctx[step.action] = step.args; + } else if (step.actionType === "func") { + if (step.args) { + this.ctx[step.action](...step.args); + } else { + this.ctx[step.action](); + } + } + resolve(); + } + }); + } +} diff --git a/painter.js b/painter.js index d0fa6ee..ec594fc 100644 --- a/painter.js +++ b/painter.js @@ -1,5 +1,6 @@ import Pen from './lib/pen'; import Downloader from './lib/downloader'; +import WxCanvas from './lib/wx-canvas'; const util = require('./lib/util'); @@ -22,6 +23,9 @@ Component({ * 组件的属性列表 */ properties: { + use2D: { + type: Boolean, + }, customStyle: { type: String, }, @@ -314,9 +318,9 @@ Component({ }, true, this.movingCache); } else { // 某些机型(华为 P20)非移动和缩放场景下,只绘制一遍会偶然性图片绘制失败 - if (!isMoving && !this.isScale) { - pen.paint() - } + // if (!isMoving && !this.isScale) { + // pen.paint() + // } pen.paint((callbackInfo) => { callback && callback(callbackInfo); this.triggerEvent('viewUpdate', { @@ -619,7 +623,7 @@ Component({ initDancePalette() { this.isDisabled = true; this.initScreenK(); - this.downloadImages(this.properties.dancePalette).then((palette) => { + this.downloadImages(this.properties.dancePalette).then(async (palette) => { this.currentPalette = palette const { width, @@ -633,11 +637,11 @@ Component({ this.setData({ painterStyle: `width:${width.toPx()}px;height:${height.toPx()}px;`, }); - this.frontContext || (this.frontContext = wx.createCanvasContext('front', this)); - this.bottomContext || (this.bottomContext = wx.createCanvasContext('bottom', this)); - this.topContext || (this.topContext = wx.createCanvasContext('top', this)); - this.globalContext || (this.globalContext = wx.createCanvasContext('k-canvas', this)); - new Pen(this.bottomContext, palette).paint(() => { + this.frontContext || (this.frontContext = await this.getCanvasContext(this.properties.use2D, 'front', width.toPx(), height.toPx())); + this.bottomContext || (this.bottomContext = await this.getCanvasContext(this.properties.use2D, 'bottom', width.toPx(), height.toPx())); + this.topContext || (this.topContext = await this.getCanvasContext(this.properties.use2D, 'top', width.toPx(), height.toPx())); + this.globalContext || (this.globalContext = await this.getCanvasContext(this.properties.use2D, 'k-canvas', width.toPx(), height.toPx())); + new Pen(this.bottomContext, palette, this.properties.use2D).paint(() => { this.isDisabled = false; this.isDisabled = this.outterDisabled; this.triggerEvent('didShow'); @@ -652,7 +656,7 @@ Component({ startPaint() { this.initScreenK(); - this.downloadImages(this.properties.palette).then((palette) => { + this.downloadImages(this.properties.palette).then(async (palette) => { const { width, height @@ -674,7 +678,7 @@ Component({ this.setData({ photoStyle: `width:${this.canvasWidthInPx}px;height:${this.canvasHeightInPx}px;`, }); - this.photoContext || (this.photoContext = wx.createCanvasContext('photo', this)); + this.photoContext || (this.photoContext = await this.getCanvasContext(this.properties.use2D, 'photo', width.toPx(), height.toPx())); new Pen(this.photoContext, palette).paint(() => { this.saveImgToLocal(); @@ -765,6 +769,31 @@ Component({ }, 300); }, + + getCanvasContext(use2D, id, width, height) { + return new Promise(resolve => { + if (use2D) { + const query = wx.createSelectorQuery().in(this); + const selectId = `#${id}`; + query.select(selectId) + .fields({ node: true, size: true }) + .exec((res) => { + const canvasNode = res[0].node; + const ctx = canvasNode.getContext('2d'); + const wxCanvas = new WxCanvas('2d', ctx, id, true, canvasNode); + const dpr = wx.getSystemInfoSync().pixelRatio + wxCanvas.width = res[0].width * dpr + wxCanvas.height = res[0].height * dpr + wxCanvas.scale(dpr, dpr) + resolve(wxCanvas); + }); + } else { + const temp = wx.createCanvasContext(id, this); + resolve(new WxCanvas('mina', temp, id, true)); + } + }) + }, + getImageInfo(filePath) { const that = this; wx.getImageInfo({ diff --git a/painter.wxml b/painter.wxml index c2f85fd..c53935a 100644 --- a/painter.wxml +++ b/painter.wxml @@ -1,14 +1,31 @@ - - - - - + + + + + + + + + + + + + +