fix: calc 引起的 dancePalette 问题

This commit is contained in:
CPPAlien 2021-06-22 11:15:10 +08:00
parent 5c155bf66f
commit cdea1f2ba6
4 changed files with 369 additions and 412 deletions

View File

@ -1,4 +1,4 @@
var calculate = function (s) { module.exports = function (s) {
s = s.trim(); s = s.trim();
const stack = new Array(); const stack = new Array();
let preSign = '+'; let preSign = '+';
@ -46,5 +46,3 @@ var calculate = function (s) {
} }
return ans; return ans;
}; };
module.exports = calculate;

View File

@ -1,33 +1,38 @@
const QR = require('./qrcode.js'); const QR = require('./qrcode.js');
const GD = require('./gradient.js'); const GD = require('./gradient.js');
export const penCache = {
// 用于存储带 id 的 view 的 rect 信息
viewRect: {},
textLines: {},
};
export const clearPenCache = id => {
if (id) {
penCache.viewRect[id] = null;
penCache.textLines[id] = null;
} else {
penCache.viewRect = {};
penCache.textLines = {};
}
};
export default class Painter { export default class Painter {
constructor(ctx, data) { constructor(ctx, data) {
this.ctx = ctx; this.ctx = ctx;
this.data = data; this.data = data;
} }
isMoving = false; paint(callback) {
// 动态模板时的缓存,加速渲染
movingCache = {};
callbackInfo = {};
// 用于存储带 id 的 view 的 rect 信息
viewRect = {};
paint(callback, isMoving, movingCache) {
this.style = { this.style = {
width: this.data.width.toPx(), width: this.data.width.toPx(),
height: this.data.height.toPx(), height: this.data.height.toPx(),
}; };
if (isMoving) {
this.isMoving = true;
this.movingCache = movingCache;
}
this._background(); this._background();
for (const view of this.data.views) { for (const view of this.data.views) {
this._drawAbsolute(view); this._drawAbsolute(view);
} }
this.ctx.draw(false, () => { this.ctx.draw(false, () => {
callback && callback(this.callbackInfo); callback && callback();
}); });
} }
@ -262,14 +267,14 @@ export default class Painter {
width = Math.round(view.sWidth / ratio); width = Math.round(view.sWidth / ratio);
height = Math.round(view.sHeight / ratio); height = Math.round(view.sHeight / ratio);
} else if (view.css.width === 'auto') { } else if (view.css.width === 'auto') {
height = view.css.height.toPx(false, this.style.height, this.viewRect); height = view.css.height.toPx(false, this.style.height);
width = (view.sWidth / view.sHeight) * height; width = (view.sWidth / view.sHeight) * height;
} else if (view.css.height === 'auto') { } else if (view.css.height === 'auto') {
width = view.css.width.toPx(false, this.style.width, this.viewRect); width = view.css.width.toPx(false, this.style.width);
height = (view.sHeight / view.sWidth) * width; height = (view.sHeight / view.sWidth) * width;
} else { } else {
width = view.css.width.toPx(false, this.style.width, this.viewRect); width = view.css.width.toPx(false, this.style.width);
height = view.css.height.toPx(false, this.style.height, this.viewRect); height = view.css.height.toPx(false, this.style.height);
} }
break; break;
} }
@ -278,26 +283,29 @@ export default class Painter {
console.error('You should set width and height'); console.error('You should set width and height');
return; return;
} }
width = view.css.width.toPx(false, this.style.width, this.viewRect); width = view.css.width.toPx(false, this.style.width);
height = view.css.height.toPx(false, this.style.height, this.viewRect); height = view.css.height.toPx(false, this.style.height);
break; break;
} }
let x; let x;
if (view.css && view.css.right) { if (view.css && view.css.right) {
if (typeof view.css.right === 'string') { if (typeof view.css.right === 'string') {
x = this.style.width - view.css.right.toPx(true, this.style.width, this.viewRect); x = this.style.width - view.css.right.toPx(true, this.style.width);
} else { } else {
// 可以用数组方式,把文字长度计算进去 // 可以用数组方式,把文字长度计算进去
// [right, 文字id, 乘数(默认 1] // [right, 文字id, 乘数(默认 1]
const rights = view.css.right; const rights = view.css.right;
x = this.style.width - rights[0].toPx(true, this.style.width) - this.viewRect[rights[1]].width * (rights[2] || 1); x =
this.style.width -
rights[0].toPx(true, this.style.width) -
penCache.viewRect[rights[1]].width * (rights[2] || 1);
} }
} else if (view.css && view.css.left) { } else if (view.css && view.css.left) {
if (typeof view.css.left === 'string') { if (typeof view.css.left === 'string') {
x = view.css.left.toPx(true, this.style.width, this.viewRect); x = view.css.left.toPx(true, this.style.width);
} else { } else {
const lefts = view.css.left; const lefts = view.css.left;
x = lefts[0].toPx(true, this.style.width) + this.viewRect[lefts[1]].width * (lefts[2] || 1); x = lefts[0].toPx(true, this.style.width) + penCache.viewRect[lefts[1]].width * (lefts[2] || 1);
} }
} else { } else {
x = 0; x = 0;
@ -305,14 +313,14 @@ export default class Painter {
//const y = view.css && view.css.bottom ? this.style.height - height - view.css.bottom.toPx(true) : (view.css && view.css.top ? view.css.top.toPx(true) : 0); //const y = view.css && view.css.bottom ? this.style.height - height - view.css.bottom.toPx(true) : (view.css && view.css.top ? view.css.top.toPx(true) : 0);
let y; let y;
if (view.css && view.css.bottom) { if (view.css && view.css.bottom) {
y = this.style.height - height - view.css.bottom.toPx(true, this.style.height, this.viewRect); y = this.style.height - height - view.css.bottom.toPx(true, this.style.height);
} else { } else {
if (view.css && view.css.top) { if (view.css && view.css.top) {
if (typeof view.css.top === 'string') { if (typeof view.css.top === 'string') {
y = view.css.top.toPx(true, this.style.height, this.viewRect); y = view.css.top.toPx(true, this.style.height);
} else { } else {
const tops = view.css.top; const tops = view.css.top;
y = tops[0].toPx(true, this.style.height) + this.viewRect[tops[1]].height * (tops[2] || 1); y = tops[0].toPx(true, this.style.height) + penCache.viewRect[tops[1]].height * (tops[2] || 1);
} }
} else { } else {
y = 0; y = 0;
@ -396,7 +404,7 @@ export default class Painter {
} }
this._doShadow(view); this._doShadow(view);
if (view.id) { if (view.id) {
this.viewRect[view.id] = { penCache.viewRect[view.id] = {
width, width,
height, height,
left: x, left: x,
@ -520,10 +528,9 @@ export default class Painter {
this.ctx.save(); this.ctx.save();
const { width, height, extra } = this._preProcess(view, view.css.background && view.css.borderRadius); const { width, height, extra } = this._preProcess(view, view.css.background && view.css.borderRadius);
this.ctx.fillStyle = view.css.color || 'black'; this.ctx.fillStyle = view.css.color || 'black';
if (this.isMoving && JSON.stringify(this.movingCache) !== JSON.stringify({})) { if (view.id && penCache.textLines[view.id]) {
this.viewRect[view.id] = this.movingCache.viewRect;
this.ctx.textAlign = view.css.textAlign ? view.css.textAlign : 'left'; this.ctx.textAlign = view.css.textAlign ? view.css.textAlign : 'left';
for (const i of this.movingCache.lineArray) { for (const i of penCache.textLines[view.id]) {
const { measuredWith, text, x, y, textDecoration } = i; const { measuredWith, text, x, y, textDecoration } = i;
if (view.css.textStyle === 'stroke') { if (view.css.textStyle === 'stroke') {
this.ctx.strokeText(text, x, y, measuredWith); this.ctx.strokeText(text, x, y, measuredWith);
@ -550,12 +557,7 @@ export default class Painter {
const _w = this.ctx.measureText(textArray[i]).width; const _w = this.ctx.measureText(textArray[i]).width;
textWidth = _w > textWidth ? _w : textWidth; textWidth = _w > textWidth ? _w : textWidth;
} }
this.viewRect[view.id].width = width ? (textWidth < width ? textWidth : width) : textWidth; penCache.viewRect[view.id].width = width ? (textWidth < width ? textWidth : width) : textWidth;
if (!this.isMoving) {
Object.assign(this.callbackInfo, {
viewRect: this.viewRect[view.id],
});
}
} }
let lineIndex = 0; let lineIndex = 0;
for (let j = 0; j < textArray.length; ++j) { for (let j = 0; j < textArray.length; ++j) {
@ -619,6 +621,7 @@ export default class Painter {
lineX = x; lineX = x;
break; break;
} }
const y = const y =
-(height / 2) + -(height / 2) +
(lineIndex === 0 ? view.css.fontSize.toPx() : view.css.fontSize.toPx() + lineIndex * lineHeight); (lineIndex === 0 ? view.css.fontSize.toPx() : view.css.fontSize.toPx() + lineIndex * lineHeight);
@ -661,16 +664,16 @@ export default class Painter {
this.ctx.strokeStyle = view.css.color; this.ctx.strokeStyle = view.css.color;
this.ctx.stroke(); this.ctx.stroke();
} }
if (!this.isMoving) { if (view.id) {
this.callbackInfo.lineArray penCache.textLines[view.id]
? this.callbackInfo.lineArray.push({ ? penCache.textLines[view.id].push({
text, text,
x, x,
y, y,
measuredWith, measuredWith,
textDecoration, textDecoration,
}) })
: (this.callbackInfo.lineArray = [ : (penCache.textLines[view.id] = [
{ {
text, text,
x, x,

View File

@ -1,4 +1,4 @@
import Pen from './lib/pen'; import Pen, { penCache, clearPenCache } from './lib/pen';
import Downloader from './lib/downloader'; import Downloader from './lib/downloader';
import WxCanvas from './lib/wx-canvas'; import WxCanvas from './lib/wx-canvas';
@ -17,7 +17,6 @@ Component({
canvasNode: null, canvasNode: null,
paintCount: 0, paintCount: 0,
currentPalette: {}, currentPalette: {},
movingCache: {},
outterDisabled: false, outterDisabled: false,
isDisabled: false, isDisabled: false,
needClear: false, needClear: false,
@ -40,6 +39,7 @@ Component({
observer: function (newVal, oldVal) { observer: function (newVal, oldVal) {
if (this.isNeedRefresh(newVal, oldVal)) { if (this.isNeedRefresh(newVal, oldVal)) {
this.paintCount = 0; this.paintCount = 0;
clearPenCache();
this.startPaint(); this.startPaint();
} }
}, },
@ -48,6 +48,7 @@ Component({
type: Object, type: Object,
observer: function (newVal, oldVal) { observer: function (newVal, oldVal) {
if (!this.isEmpty(newVal) && !this.properties.use2D) { if (!this.isEmpty(newVal) && !this.properties.use2D) {
clearPenCache();
this.initDancePalette(newVal); this.initDancePalette(newVal);
} }
}, },
@ -55,11 +56,11 @@ Component({
// 缩放比,会在传入的 palette 中统一乘以该缩放比 // 缩放比,会在传入的 palette 中统一乘以该缩放比
scaleRatio: { scaleRatio: {
type: Number, type: Number,
value: 1 value: 1,
}, },
widthPixels: { widthPixels: {
type: Number, type: Number,
value: 0 value: 0,
}, },
// 启用脏检查,默认 false // 启用脏检查,默认 false
dirty: { dirty: {
@ -74,18 +75,16 @@ Component({
type: Object, type: Object,
observer: function (newVal, oldVal) { observer: function (newVal, oldVal) {
if (newVal && !this.isEmpty(newVal) && !this.properties.use2D) { if (newVal && !this.isEmpty(newVal) && !this.properties.use2D) {
this.doAction(newVal, (callbackInfo) => { this.doAction(newVal, null, false, true);
this.movingCache = callbackInfo
}, false, true)
} }
}, },
}, },
disableAction: { disableAction: {
type: Boolean, type: Boolean,
observer: function (isDisabled) { observer: function (isDisabled) {
this.outterDisabled = isDisabled this.outterDisabled = isDisabled;
this.isDisabled = isDisabled this.isDisabled = isDisabled;
} },
}, },
clearActionBox: { clearActionBox: {
type: Boolean, type: Boolean,
@ -96,12 +95,12 @@ Component({
this.frontContext.draw(); this.frontContext.draw();
}, 100); }, 100);
this.touchedView = {}; this.touchedView = {};
this.prevFindedIndex = this.findedIndex this.prevFindedIndex = this.findedIndex;
this.findedIndex = -1; this.findedIndex = -1;
} }
} }
this.needClear = needClear this.needClear = needClear;
} },
}, },
}, },
@ -112,7 +111,6 @@ Component({
}, },
methods: { methods: {
/** /**
* 判断一个 object 是否为 * 判断一个 object 是否为
* @param {object} object * @param {object} object
@ -141,28 +139,26 @@ Component({
top: `${rect.top}px`, top: `${rect.top}px`,
borderWidth: '4rpx', borderWidth: '4rpx',
borderColor: '#1A7AF8', borderColor: '#1A7AF8',
color: 'transparent' color: 'transparent',
} },
} };
if (type === 'text') { if (type === 'text') {
boxArea.css = Object.assign({}, boxArea.css, { boxArea.css = Object.assign({}, boxArea.css, {
borderStyle: 'dashed' borderStyle: 'dashed',
}) });
} }
if (this.properties.customActionStyle && this.properties.customActionStyle.border) { if (this.properties.customActionStyle && this.properties.customActionStyle.border) {
boxArea.css = Object.assign({}, boxArea.css, this.properties.customActionStyle.border) boxArea.css = Object.assign({}, boxArea.css, this.properties.customActionStyle.border);
} }
Object.assign(boxArea, { Object.assign(boxArea, {
id: 'box' id: 'box',
}) });
return boxArea return boxArea;
}, },
getScaleIcon(rect, type) { getScaleIcon(rect, type) {
let scaleArea = {} let scaleArea = {};
const { const { customActionStyle } = this.properties;
customActionStyle
} = this.properties
if (customActionStyle && customActionStyle.scale) { if (customActionStyle && customActionStyle.scale) {
scaleArea = { scaleArea = {
type: 'image', type: 'image',
@ -171,8 +167,8 @@ Component({
height: `${2 * ACTION_DEFAULT_SIZE}rpx`, height: `${2 * ACTION_DEFAULT_SIZE}rpx`,
width: `${2 * ACTION_DEFAULT_SIZE}rpx`, width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
borderRadius: `${ACTION_DEFAULT_SIZE}rpx`, borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
} },
} };
} else { } else {
scaleArea = { scaleArea = {
type: 'rect', type: 'rect',
@ -181,25 +177,26 @@ Component({
width: `${2 * ACTION_DEFAULT_SIZE}rpx`, width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
borderRadius: `${ACTION_DEFAULT_SIZE}rpx`, borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
color: '#0000ff', color: '#0000ff',
} },
} };
} }
scaleArea.css = Object.assign({}, scaleArea.css, { scaleArea.css = Object.assign({}, scaleArea.css, {
align: 'center', align: 'center',
left: `${rect.right + ACTION_OFFSET.toPx()}px`, left: `${rect.right + ACTION_OFFSET.toPx()}px`,
top: type === 'text' ? `${rect.top - ACTION_OFFSET.toPx() - scaleArea.css.height.toPx() / 2}px` : `${rect.bottom - ACTION_OFFSET.toPx() - scaleArea.css.height.toPx() / 2}px` top:
}) type === 'text'
? `${rect.top - ACTION_OFFSET.toPx() - scaleArea.css.height.toPx() / 2}px`
: `${rect.bottom - ACTION_OFFSET.toPx() - scaleArea.css.height.toPx() / 2}px`,
});
Object.assign(scaleArea, { Object.assign(scaleArea, {
id: 'scale' id: 'scale',
}) });
return scaleArea return scaleArea;
}, },
getDeleteIcon(rect) { getDeleteIcon(rect) {
let deleteArea = {} let deleteArea = {};
const { const { customActionStyle } = this.properties;
customActionStyle
} = this.properties
if (customActionStyle && customActionStyle.scale) { if (customActionStyle && customActionStyle.scale) {
deleteArea = { deleteArea = {
type: 'image', type: 'image',
@ -208,8 +205,8 @@ Component({
height: `${2 * ACTION_DEFAULT_SIZE}rpx`, height: `${2 * ACTION_DEFAULT_SIZE}rpx`,
width: `${2 * ACTION_DEFAULT_SIZE}rpx`, width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
borderRadius: `${ACTION_DEFAULT_SIZE}rpx`, borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
} },
} };
} else { } else {
deleteArea = { deleteArea = {
type: 'rect', type: 'rect',
@ -218,91 +215,96 @@ Component({
width: `${2 * ACTION_DEFAULT_SIZE}rpx`, width: `${2 * ACTION_DEFAULT_SIZE}rpx`,
borderRadius: `${ACTION_DEFAULT_SIZE}rpx`, borderRadius: `${ACTION_DEFAULT_SIZE}rpx`,
color: '#0000ff', color: '#0000ff',
} },
} };
} }
deleteArea.css = Object.assign({}, deleteArea.css, { deleteArea.css = Object.assign({}, deleteArea.css, {
align: 'center', align: 'center',
left: `${rect.left - ACTION_OFFSET.toPx()}px`, left: `${rect.left - ACTION_OFFSET.toPx()}px`,
top: `${rect.top - ACTION_OFFSET.toPx() - deleteArea.css.height.toPx() / 2}px` top: `${rect.top - ACTION_OFFSET.toPx() - deleteArea.css.height.toPx() / 2}px`,
}) });
Object.assign(deleteArea, { Object.assign(deleteArea, {
id: 'delete' id: 'delete',
}) });
return deleteArea return deleteArea;
}, },
doAction(action, callback, isMoving, overwrite) { doAction(action, callback, isMoving, overwrite) {
if (this.properties.use2D) { if (this.properties.use2D) {
return; return;
} }
let newVal = null let newVal = null;
if (action) { if (action) {
newVal = action.view newVal = action.view;
} }
if (newVal && newVal.id && this.touchedView.id !== newVal.id) { if (newVal && newVal.id && this.touchedView.id !== newVal.id) {
// 带 id 的动作给撤回时使用,不带 id表示对当前选中对象进行操作 // 带 id 的动作给撤回时使用,不带 id表示对当前选中对象进行操作
const { const { views } = this.currentPalette;
views
} = this.currentPalette;
for (let i = 0; i < views.length; i++) { for (let i = 0; i < views.length; i++) {
if (views[i].id === newVal.id) { if (views[i].id === newVal.id) {
// 跨层回撤,需要重新构建三层关系 // 跨层回撤,需要重新构建三层关系
this.touchedView = views[i]; this.touchedView = views[i];
this.findedIndex = i; this.findedIndex = i;
this.sliceLayers(); this.sliceLayers();
break break;
} }
} }
} }
const doView = this.touchedView const doView = this.touchedView;
if (!doView || this.isEmpty(doView)) { if (!doView || this.isEmpty(doView)) {
return return;
} }
if (newVal && newVal.css) { if (newVal && newVal.css) {
if (overwrite) { if (overwrite) {
doView.css = newVal.css doView.css = newVal.css;
} else if (Array.isArray(doView.css) && Array.isArray(newVal.css)) { } else if (Array.isArray(doView.css) && Array.isArray(newVal.css)) {
doView.css = Object.assign({}, ...doView.css, ...newVal.css) doView.css = Object.assign({}, ...doView.css, ...newVal.css);
} else if (Array.isArray(doView.css)) { } else if (Array.isArray(doView.css)) {
doView.css = Object.assign({}, ...doView.css, newVal.css) doView.css = Object.assign({}, ...doView.css, newVal.css);
} else if (Array.isArray(newVal.css)) { } else if (Array.isArray(newVal.css)) {
doView.css = Object.assign({}, doView.css, ...newVal.css) doView.css = Object.assign({}, doView.css, ...newVal.css);
} else { } else {
doView.css = Object.assign({}, doView.css, newVal.css) doView.css = Object.assign({}, doView.css, newVal.css);
} }
} }
if (newVal && newVal.rect) { if (newVal && newVal.rect) {
doView.rect = newVal.rect; doView.rect = newVal.rect;
} }
if (newVal && newVal.url && doView.url && newVal.url !== doView.url) { if (newVal && newVal.url && doView.url && newVal.url !== doView.url) {
downloader.download(newVal.url, this.properties.LRU).then((path) => { downloader
.download(newVal.url, this.properties.LRU)
.then(path => {
if (newVal.url.startsWith('https')) { if (newVal.url.startsWith('https')) {
doView.originUrl = newVal.url doView.originUrl = newVal.url;
} }
doView.url = path; doView.url = path;
wx.getImageInfo({ wx.getImageInfo({
src: path, src: path,
success: (res) => { success: res => {
doView.sHeight = res.height doView.sHeight = res.height;
doView.sWidth = res.width doView.sWidth = res.width;
this.reDraw(doView, callback, isMoving) this.reDraw(doView, callback, isMoving);
}, },
fail: () => { fail: () => {
this.reDraw(doView, callback, isMoving) this.reDraw(doView, callback, isMoving);
} },
});
}) })
}).catch((error) => { .catch(error => {
// 未下载成功,直接绘制 // 未下载成功,直接绘制
console.error(error) console.error(error);
this.reDraw(doView, callback, isMoving) this.reDraw(doView, callback, isMoving);
}) });
} else { } else {
(newVal && newVal.text && doView.text && newVal.text !== doView.text) && (doView.text = newVal.text); newVal && newVal.text && doView.text && newVal.text !== doView.text && (doView.text = newVal.text);
(newVal && newVal.content && doView.content && newVal.content !== doView.content) && (doView.content = newVal.content); newVal &&
this.reDraw(doView, callback, isMoving) newVal.content &&
doView.content &&
newVal.content !== doView.content &&
(doView.content = newVal.content);
this.reDraw(doView, callback, isMoving);
} }
}, },
@ -310,133 +312,104 @@ Component({
const draw = { const draw = {
width: this.currentPalette.width, width: this.currentPalette.width,
height: this.currentPalette.height, height: this.currentPalette.height,
views: this.isEmpty(doView) ? [] : [doView] views: this.isEmpty(doView) ? [] : [doView],
} };
const pen = new Pen(this.globalContext, draw); const pen = new Pen(this.globalContext, draw);
if (isMoving && doView.type === 'text') { pen.paint(callbackInfo => {
pen.paint((callbackInfo) => {
callback && callback(callbackInfo); callback && callback(callbackInfo);
this.triggerEvent('viewUpdate', { this.triggerEvent('viewUpdate', {
view: this.touchedView view: this.touchedView,
}); });
}, true, this.movingCache);
} else {
// 某些机型(华为 P20非移动和缩放场景下只绘制一遍会偶然性图片绘制失败
// if (!isMoving && !this.isScale) {
// pen.paint()
// }
pen.paint((callbackInfo) => {
callback && callback(callbackInfo);
this.triggerEvent('viewUpdate', {
view: this.touchedView
}); });
})
}
const { const { rect, css, type } = doView;
rect,
css,
type
} = doView
this.block = { this.block = {
width: this.currentPalette.width, width: this.currentPalette.width,
height: this.currentPalette.height, height: this.currentPalette.height,
views: this.isEmpty(doView) ? [] : [this.getBox(rect, doView.type)] views: this.isEmpty(doView) ? [] : [this.getBox(rect, doView.type)],
} };
if (css && css.scalable) { if (css && css.scalable) {
this.block.views.push(this.getScaleIcon(rect, type)) this.block.views.push(this.getScaleIcon(rect, type));
} }
if (css && css.deletable) { if (css && css.deletable) {
this.block.views.push(this.getDeleteIcon(rect)) this.block.views.push(this.getDeleteIcon(rect));
} }
const topBlock = new Pen(this.frontContext, this.block) const topBlock = new Pen(this.frontContext, this.block);
topBlock.paint(); topBlock.paint();
}, },
isInView(x, y, rect) { isInView(x, y, rect) {
return (x > rect.left && return x > rect.left && y > rect.top && x < rect.right && y < rect.bottom;
y > rect.top &&
x < rect.right &&
y < rect.bottom
)
}, },
isInDelete(x, y) { isInDelete(x, y) {
for (const view of this.block.views) { for (const view of this.block.views) {
if (view.id === 'delete') { if (view.id === 'delete') {
return (x > view.rect.left && return x > view.rect.left && y > view.rect.top && x < view.rect.right && y < view.rect.bottom;
y > view.rect.top &&
x < view.rect.right &&
y < view.rect.bottom)
} }
} }
return false return false;
}, },
isInScale(x, y) { isInScale(x, y) {
for (const view of this.block.views) { for (const view of this.block.views) {
if (view.id === 'scale') { if (view.id === 'scale') {
return (x > view.rect.left && return x > view.rect.left && y > view.rect.top && x < view.rect.right && y < view.rect.bottom;
y > view.rect.top &&
x < view.rect.right &&
y < view.rect.bottom)
} }
} }
return false return false;
}, },
touchedView: {}, touchedView: {},
findedIndex: -1, findedIndex: -1,
onClick() { onClick() {
const x = this.startX const x = this.startX;
const y = this.startY const y = this.startY;
const totalLayerCount = this.currentPalette.views.length const totalLayerCount = this.currentPalette.views.length;
let canBeTouched = [] let canBeTouched = [];
let isDelete = false let isDelete = false;
let deleteIndex = -1 let deleteIndex = -1;
for (let i = totalLayerCount - 1; i >= 0; i--) { for (let i = totalLayerCount - 1; i >= 0; i--) {
const view = this.currentPalette.views[i] const view = this.currentPalette.views[i];
const { const { rect } = view;
rect
} = view
if (this.touchedView && this.touchedView.id && this.touchedView.id === view.id && this.isInDelete(x, y, rect)) { if (this.touchedView && this.touchedView.id && this.touchedView.id === view.id && this.isInDelete(x, y, rect)) {
canBeTouched.length = 0 canBeTouched.length = 0;
deleteIndex = i deleteIndex = i;
isDelete = true isDelete = true;
break break;
} }
if (this.isInView(x, y, rect)) { if (this.isInView(x, y, rect)) {
canBeTouched.push({ canBeTouched.push({
view, view,
index: i index: i,
}) });
} }
} }
this.touchedView = {} this.touchedView = {};
if (canBeTouched.length === 0) { if (canBeTouched.length === 0) {
this.findedIndex = -1 this.findedIndex = -1;
} else { } else {
let i = 0 let i = 0;
const touchAble = canBeTouched.filter(item => Boolean(item.view.id)) const touchAble = canBeTouched.filter(item => Boolean(item.view.id));
if (touchAble.length === 0) { if (touchAble.length === 0) {
this.findedIndex = canBeTouched[0].index this.findedIndex = canBeTouched[0].index;
} else { } else {
for (i = 0; i < touchAble.length; i++) { for (i = 0; i < touchAble.length; i++) {
if (this.findedIndex === touchAble[i].index) { if (this.findedIndex === touchAble[i].index) {
i++ i++;
break break;
} }
} }
if (i === touchAble.length) { if (i === touchAble.length) {
i = 0 i = 0;
} }
this.touchedView = touchAble[i].view this.touchedView = touchAble[i].view;
this.findedIndex = touchAble[i].index this.findedIndex = touchAble[i].index;
this.triggerEvent('viewClicked', { this.triggerEvent('viewClicked', {
view: this.touchedView view: this.touchedView,
}) });
} }
} }
if (this.findedIndex < 0 || (this.touchedView && !this.touchedView.id)) { if (this.findedIndex < 0 || (this.touchedView && !this.touchedView.id)) {
@ -446,47 +419,43 @@ Component({
this.triggerEvent('touchEnd', { this.triggerEvent('touchEnd', {
view: this.currentPalette.views[deleteIndex], view: this.currentPalette.views[deleteIndex],
index: deleteIndex, index: deleteIndex,
type: 'delete' type: 'delete',
}) });
this.doAction() this.doAction();
} else if (this.findedIndex < 0) { } else if (this.findedIndex < 0) {
this.triggerEvent('viewClicked', {}) this.triggerEvent('viewClicked', {});
} }
this.findedIndex = -1 this.findedIndex = -1;
this.prevFindedIndex = -1 this.prevFindedIndex = -1;
} else if (this.touchedView && this.touchedView.id) { } else if (this.touchedView && this.touchedView.id) {
this.sliceLayers(); this.sliceLayers();
} }
}, },
sliceLayers() { sliceLayers() {
const bottomLayers = this.currentPalette.views.slice(0, this.findedIndex) const bottomLayers = this.currentPalette.views.slice(0, this.findedIndex);
const topLayers = this.currentPalette.views.slice(this.findedIndex + 1) const topLayers = this.currentPalette.views.slice(this.findedIndex + 1);
const bottomDraw = { const bottomDraw = {
width: this.currentPalette.width, width: this.currentPalette.width,
height: this.currentPalette.height, height: this.currentPalette.height,
background: this.currentPalette.background, background: this.currentPalette.background,
views: bottomLayers views: bottomLayers,
} };
const topDraw = { const topDraw = {
width: this.currentPalette.width, width: this.currentPalette.width,
height: this.currentPalette.height, height: this.currentPalette.height,
views: topLayers views: topLayers,
} };
if (this.prevFindedIndex < this.findedIndex) { if (this.prevFindedIndex < this.findedIndex) {
new Pen(this.bottomContext, bottomDraw).paint(); new Pen(this.bottomContext, bottomDraw).paint();
this.doAction(null, (callbackInfo) => { this.doAction();
this.movingCache = callbackInfo
})
new Pen(this.topContext, topDraw).paint(); new Pen(this.topContext, topDraw).paint();
} else { } else {
new Pen(this.topContext, topDraw).paint(); new Pen(this.topContext, topDraw).paint();
this.doAction(null, (callbackInfo) => { this.doAction();
this.movingCache = callbackInfo
})
new Pen(this.bottomContext, bottomDraw).paint(); new Pen(this.bottomContext, bottomDraw).paint();
} }
this.prevFindedIndex = this.findedIndex this.prevFindedIndex = this.findedIndex;
}, },
startX: 0, startX: 0,
@ -497,116 +466,105 @@ Component({
startTimeStamp: 0, startTimeStamp: 0,
onTouchStart(event) { onTouchStart(event) {
if (this.isDisabled) { if (this.isDisabled) {
return return;
} }
const { const { x, y } = event.touches[0];
x, this.startX = x;
y this.startY = y;
} = event.touches[0] this.startTimeStamp = new Date().getTime();
this.startX = x
this.startY = y
this.startTimeStamp = new Date().getTime()
if (this.touchedView && !this.isEmpty(this.touchedView)) { if (this.touchedView && !this.isEmpty(this.touchedView)) {
const { const { rect } = this.touchedView;
rect
} = this.touchedView
if (this.isInScale(x, y, rect)) { if (this.isInScale(x, y, rect)) {
this.isScale = true this.isScale = true;
this.movingCache = {} this.startH = rect.bottom - rect.top;
this.startH = rect.bottom - rect.top this.startW = rect.right - rect.left;
this.startW = rect.right - rect.left
} else { } else {
this.isScale = false this.isScale = false;
} }
} else { } else {
this.isScale = false this.isScale = false;
} }
}, },
onTouchEnd(e) { onTouchEnd(e) {
if (this.isDisabled) { if (this.isDisabled) {
return return;
} }
const current = new Date().getTime() const current = new Date().getTime();
if ((current - this.startTimeStamp) <= 500 && !this.hasMove) { if (current - this.startTimeStamp <= 500 && !this.hasMove) {
!this.isScale && this.onClick(e) !this.isScale && this.onClick(e);
} else if (this.touchedView && !this.isEmpty(this.touchedView)) { } else if (this.touchedView && !this.isEmpty(this.touchedView)) {
this.triggerEvent('touchEnd', { this.triggerEvent('touchEnd', {
view: this.touchedView, view: this.touchedView,
}) });
} }
this.hasMove = false this.hasMove = false;
}, },
onTouchCancel(e) { onTouchCancel(e) {
if (this.isDisabled) { if (this.isDisabled) {
return return;
} }
this.onTouchEnd(e) this.onTouchEnd(e);
}, },
hasMove: false, hasMove: false,
onTouchMove(event) { onTouchMove(event) {
if (this.isDisabled) { if (this.isDisabled) {
return return;
} }
this.hasMove = true this.hasMove = true;
if (!this.touchedView || (this.touchedView && !this.touchedView.id)) { if (!this.touchedView || (this.touchedView && !this.touchedView.id)) {
return return;
} }
const { const { x, y } = event.touches[0];
x, const offsetX = x - this.startX;
y const offsetY = y - this.startY;
} = event.touches[0] const { rect, type } = this.touchedView;
const offsetX = x - this.startX let css = {};
const offsetY = y - this.startY
const {
rect,
type
} = this.touchedView
let css = {}
if (this.isScale) { if (this.isScale) {
const newW = this.startW + offsetX > 1 ? this.startW + offsetX : 1 clearPenCache(this.touchedView.id);
const newW = this.startW + offsetX > 1 ? this.startW + offsetX : 1;
if (this.touchedView.css && this.touchedView.css.minWidth) { if (this.touchedView.css && this.touchedView.css.minWidth) {
if (newW < this.touchedView.css.minWidth.toPx()) { if (newW < this.touchedView.css.minWidth.toPx()) {
return return;
} }
} }
if (this.touchedView.rect && this.touchedView.rect.minWidth) { if (this.touchedView.rect && this.touchedView.rect.minWidth) {
if (newW < this.touchedView.rect.minWidth) { if (newW < this.touchedView.rect.minWidth) {
return return;
} }
} }
const newH = this.startH + offsetY > 1 ? this.startH + offsetY : 1 const newH = this.startH + offsetY > 1 ? this.startH + offsetY : 1;
css = { css = {
width: `${newW}px`, width: `${newW}px`,
} };
if (type !== 'text') { if (type !== 'text') {
if (type === 'image') { if (type === 'image') {
css.height = `${(newW) * this.startH / this.startW }px` css.height = `${(newW * this.startH) / this.startW}px`;
} else { } else {
css.height = `${newH}px` css.height = `${newH}px`;
} }
} }
} else { } else {
this.startX = x this.startX = x;
this.startY = y this.startY = y;
css = { css = {
left: `${rect.x + offsetX}px`, left: `${rect.x + offsetX}px`,
top: `${rect.y + offsetY}px`, top: `${rect.y + offsetY}px`,
right: undefined, right: undefined,
bottom: undefined bottom: undefined,
};
} }
} this.doAction(
this.doAction({ {
view: { view: {
css css,
} },
}, (callbackInfo) => { },
if (this.isScale) { null,
this.movingCache = callbackInfo !this.isScale,
} );
}, !this.isScale)
}, },
initScreenK() { initScreenK() {
@ -631,12 +589,9 @@ Component({
} }
this.isDisabled = true; this.isDisabled = true;
this.initScreenK(); this.initScreenK();
this.downloadImages(this.properties.dancePalette).then(async (palette) => { this.downloadImages(this.properties.dancePalette).then(async palette => {
this.currentPalette = palette this.currentPalette = palette;
const { const { width, height } = palette;
width,
height
} = palette;
if (!width || !height) { if (!width || !height) {
console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`); console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`);
@ -663,10 +618,7 @@ Component({
startPaint() { startPaint() {
this.initScreenK(); this.initScreenK();
const { const { width, height } = this.properties.palette;
width,
height
} = this.properties.palette;
if (!width || !height) { if (!width || !height) {
console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`); console.error(`You should set width and height correctly for painter, width: ${width}, height: ${height}`);
@ -680,17 +632,19 @@ Component({
needScale = this.properties.use2D; needScale = this.properties.use2D;
} }
if (this.properties.widthPixels) { if (this.properties.widthPixels) {
setStringPrototype(this.screenK, this.properties.widthPixels / this.canvasWidthInPx) setStringPrototype(this.screenK, this.properties.widthPixels / this.canvasWidthInPx);
this.canvasWidthInPx = this.properties.widthPixels this.canvasWidthInPx = this.properties.widthPixels;
} }
if (this.canvasHeightInPx !== height.toPx()) { if (this.canvasHeightInPx !== height.toPx()) {
this.canvasHeightInPx = height.toPx(); this.canvasHeightInPx = height.toPx();
needScale = needScale || this.properties.use2D; needScale = needScale || this.properties.use2D;
} }
this.setData({ this.setData(
{
photoStyle: `width:${this.canvasWidthInPx}px;height:${this.canvasHeightInPx}px;`, photoStyle: `width:${this.canvasWidthInPx}px;height:${this.canvasHeightInPx}px;`,
}, function () { },
function () {
this.downloadImages(this.properties.palette).then(async palette => { this.downloadImages(this.properties.palette).then(async palette => {
if (!this.photoContext) { if (!this.photoContext) {
this.photoContext = await this.getCanvasContext(this.properties.use2D, 'photo'); this.photoContext = await this.getCanvasContext(this.properties.use2D, 'photo');
@ -706,7 +660,8 @@ Component({
}); });
setStringPrototype(this.screenK, this.properties.scaleRatio); setStringPrototype(this.screenK, this.properties.scaleRatio);
}); });
}); },
);
}, },
downloadImages(palette) { downloadImages(palette) {
@ -716,38 +671,42 @@ Component({
const paletteCopy = JSON.parse(JSON.stringify(palette)); const paletteCopy = JSON.parse(JSON.stringify(palette));
if (paletteCopy.background) { if (paletteCopy.background) {
preCount++; preCount++;
downloader.download(paletteCopy.background, this.properties.LRU).then((path) => { downloader.download(paletteCopy.background, this.properties.LRU).then(
path => {
paletteCopy.background = path; paletteCopy.background = path;
completeCount++; completeCount++;
if (preCount === completeCount) { if (preCount === completeCount) {
resolve(paletteCopy); resolve(paletteCopy);
} }
}, () => { },
() => {
completeCount++; completeCount++;
if (preCount === completeCount) { if (preCount === completeCount) {
resolve(paletteCopy); resolve(paletteCopy);
} }
}); },
);
} }
if (paletteCopy.views) { if (paletteCopy.views) {
for (const view of paletteCopy.views) { for (const view of paletteCopy.views) {
if (view && view.type === 'image' && view.url) { if (view && view.type === 'image' && view.url) {
preCount++; preCount++;
/* eslint-disable no-loop-func */ /* eslint-disable no-loop-func */
downloader.download(view.url, this.properties.LRU).then((path) => { downloader.download(view.url, this.properties.LRU).then(
path => {
view.originUrl = view.url; view.originUrl = view.url;
view.url = path; view.url = path;
wx.getImageInfo({ wx.getImageInfo({
src: path, src: path,
success: (res) => { success: res => {
// 获得一下图片信息,供后续裁减使用 // 获得一下图片信息,供后续裁减使用
view.sWidth = res.width; view.sWidth = res.width;
view.sHeight = res.height; view.sHeight = res.height;
}, },
fail: (error) => { fail: error => {
// 如果图片坏了,则直接置空,防止坑爹的 canvas 画崩溃了 // 如果图片坏了,则直接置空,防止坑爹的 canvas 画崩溃了
console.warn(`getImageInfo ${view.originUrl} failed, ${JSON.stringify(error)}`); console.warn(`getImageInfo ${view.originUrl} failed, ${JSON.stringify(error)}`);
view.url = ""; view.url = '';
}, },
complete: () => { complete: () => {
completeCount++; completeCount++;
@ -756,12 +715,14 @@ Component({
} }
}, },
}); });
}, () => { },
() => {
completeCount++; completeCount++;
if (preCount === completeCount) { if (preCount === completeCount) {
resolve(paletteCopy); resolve(paletteCopy);
} }
}); },
);
} }
} }
} }
@ -774,34 +735,37 @@ Component({
saveImgToLocal() { saveImgToLocal() {
const that = this; const that = this;
setTimeout(() => { setTimeout(() => {
wx.canvasToTempFilePath({ wx.canvasToTempFilePath(
{
canvasId: 'photo', canvasId: 'photo',
canvas: that.properties.use2D ? that.canvasNode : null, canvas: that.properties.use2D ? that.canvasNode : null,
destWidth: that.canvasWidthInPx * getApp().systemInfo.pixelRatio, destWidth: that.canvasWidthInPx,
destHeight: that.canvasHeightInPx * getApp().systemInfo.pixelRatio, destHeight: that.canvasHeightInPx,
success: function (res) { success: function (res) {
that.getImageInfo(res.tempFilePath); that.getImageInfo(res.tempFilePath);
}, },
fail: function (error) { fail: function (error) {
console.error(`canvasToTempFilePath failed, ${JSON.stringify(error)}`); console.error(`canvasToTempFilePath failed, ${JSON.stringify(error)}`);
that.triggerEvent('imgErr', { that.triggerEvent('imgErr', {
error: error error: error,
}); });
}, },
}, this); },
this,
);
}, 300); }, 300);
}, },
getCanvasContext(use2D, id) { getCanvasContext(use2D, id) {
const that = this; const that = this;
return new Promise(resolve => { return new Promise(resolve => {
if (use2D) { if (use2D) {
const query = wx.createSelectorQuery().in(that); const query = wx.createSelectorQuery().in(that);
const selectId = `#${id}`; const selectId = `#${id}`;
query.select(selectId) query
.select(selectId)
.fields({ node: true, size: true }) .fields({ node: true, size: true })
.exec((res) => { .exec(res => {
that.canvasNode = res[0].node; that.canvasNode = res[0].node;
const ctx = that.canvasNode.getContext('2d'); const ctx = that.canvasNode.getContext('2d');
const wxCanvas = new WxCanvas('2d', ctx, id, true, that.canvasNode); const wxCanvas = new WxCanvas('2d', ctx, id, true, that.canvasNode);
@ -811,36 +775,41 @@ Component({
const temp = wx.createCanvasContext(id, that); const temp = wx.createCanvasContext(id, that);
resolve(new WxCanvas('mina', temp, id, true)); resolve(new WxCanvas('mina', temp, id, true));
} }
}) });
}, },
getImageInfo(filePath) { getImageInfo(filePath) {
const that = this; const that = this;
wx.getImageInfo({ wx.getImageInfo({
src: filePath, src: filePath,
success: (infoRes) => { success: infoRes => {
if (that.paintCount > MAX_PAINT_COUNT) { if (that.paintCount > MAX_PAINT_COUNT) {
const error = `The result is always fault, even we tried ${MAX_PAINT_COUNT} times`; const error = `The result is always fault, even we tried ${MAX_PAINT_COUNT} times`;
console.error(error); console.error(error);
that.triggerEvent('imgErr', { that.triggerEvent('imgErr', {
error: error error: error,
}); });
return; return;
} }
// 比例相符时才证明绘制成功,否则进行强制重绘制 // 比例相符时才证明绘制成功,否则进行强制重绘制
if (Math.abs((infoRes.width * that.canvasHeightInPx - that.canvasWidthInPx * infoRes.height) / (infoRes.height * that.canvasHeightInPx)) < 0.01) { if (
Math.abs(
(infoRes.width * that.canvasHeightInPx - that.canvasWidthInPx * infoRes.height) /
(infoRes.height * that.canvasHeightInPx),
) < 0.01
) {
that.triggerEvent('imgOK', { that.triggerEvent('imgOK', {
path: filePath path: filePath,
}); });
} else { } else {
that.startPaint(); that.startPaint();
} }
that.paintCount++; that.paintCount++;
}, },
fail: (error) => { fail: error => {
console.error(`getImageInfo failed, ${JSON.stringify(error)}`); console.error(`getImageInfo failed, ${JSON.stringify(error)}`);
that.triggerEvent('imgErr', { that.triggerEvent('imgErr', {
error: error error: error,
}); });
}, },
}); });
@ -848,15 +817,13 @@ Component({
}, },
}); });
function setStringPrototype(screenK, scale) { function setStringPrototype(screenK, scale) {
/* eslint-disable no-extend-native */ /* eslint-disable no-extend-native */
/** /**
* string 到对应的 px * string 到对应的 px
* @param {Number} baseSize 当设置了 % 号时设置的基准值 * @param {Number} baseSize 当设置了 % 号时设置的基准值
* @param {Object} relativeViewRect 所相对的 view 的信息
*/ */
String.prototype.toPx = function toPx(_, baseSize, relativeViewRect) { String.prototype.toPx = function toPx(_, baseSize) {
if (this === '0') { if (this === '0') {
return 0; return 0;
} }
@ -881,14 +848,13 @@ function setStringPrototype(screenK, scale) {
} }
return res; return res;
}; };
const formula = /^calc\((.+)\)$/.exec(this) const formula = /^calc\((.+)\)$/.exec(this);
if (formula && formula[1]) { if (formula && formula[1]) {
// 进行 calc 计算 // 进行 calc 计算
const afterOne = formula[1].replace(/([^\s]+)\.(left|right|bottom|top|width|height)/g, (word) => { const afterOne = formula[1].replace(/([^\s]+)\.(left|right|bottom|top|width|height)/g, word => {
const [id, attr] = word.split('.'); const [id, attr] = word.split('.');
return relativeViewRect[id][attr] return penCache.viewRect[id][attr];
} });
);
const afterTwo = afterOne.replace(new RegExp(REG, 'g'), parsePx); const afterTwo = afterOne.replace(new RegExp(REG, 'g'), parsePx);
return calc(afterTwo); return calc(afterTwo);
} else { } else {

View File

@ -1,6 +1,7 @@
<view style='position: relative;{{customStyle}};{{painterStyle}}'> <view style='position: relative;{{customStyle}};{{painterStyle}}'>
<block wx:if="{{!use2D}}"> <block wx:if="{{!use2D}}">
<canvas canvas-id="photo" style="{{photoStyle}};position: absolute; left: -9999px; top: -9999rpx;" /> <canvas canvas-id="photo" style="{{photoStyle}};position: absolute; left: -9999px; top: -9999rpx;" />
<block wx:if="{{dancePalette}}">
<canvas canvas-id="bottom" style="{{painterStyle}};position: absolute;" /> <canvas canvas-id="bottom" style="{{painterStyle}};position: absolute;" />
<canvas canvas-id="k-canvas" style="{{painterStyle}};position: absolute;" /> <canvas canvas-id="k-canvas" style="{{painterStyle}};position: absolute;" />
<canvas canvas-id="top" style="{{painterStyle}};position: absolute;" /> <canvas canvas-id="top" style="{{painterStyle}};position: absolute;" />
@ -13,19 +14,8 @@
bindtouchcancel="onTouchCancel" bindtouchcancel="onTouchCancel"
disable-scroll="{{true}}" /> disable-scroll="{{true}}" />
</block> </block>
</block>
<block wx:if="{{use2D}}"> <block wx:if="{{use2D}}">
<canvas type="2d" id="photo" style="{{photoStyle}};" /> <canvas type="2d" id="photo" style="{{photoStyle}};" />
<!-- <canvas type="2d" id="bottom" style="{{painterStyle}};position: absolute;" />
<canvas type="2d" id="k-canvas" style="{{painterStyle}};position: absolute;" />
<canvas type="2d" id="top" style="{{painterStyle}};position: absolute;" />
<canvas
type="2d"
id="front"
style="{{painterStyle}};position: absolute;"
bindtouchstart="onTouchStart"
bindtouchmove="onTouchMove"
bindtouchend="onTouchEnd"
bindtouchcancel="onTouchCancel"
disable-scroll="{{true}}" /> -->
</block> </block>
</view> </view>