Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
beb630d5a2 | ||
|
|
eeb4f8a0fa | ||
|
|
cdea1f2ba6 | ||
|
|
5c155bf66f | ||
|
|
660da94ea3 | ||
|
|
25bb201c9e | ||
|
|
40c9453978 | ||
|
|
eef194d397 | ||
|
|
4970430fb7 | ||
|
|
4f7456f0f3 | ||
|
|
1d0d056e9b | ||
|
|
d9e8775dab | ||
|
|
1e4580eaf8 | ||
|
|
bf8e091f37 | ||
|
|
fe55b40129 | ||
|
|
087fac8a52 | ||
|
|
18c539645b | ||
|
|
40fec6fe9a | ||
|
|
453a1ca18b | ||
|
|
8b6942e0ac |
54
lib/calc.js
Normal file
54
lib/calc.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// 四则运算
|
||||||
|
|
||||||
|
!(function () {
|
||||||
|
var calculate = function (s) {
|
||||||
|
s = s.trim();
|
||||||
|
const stack = new Array();
|
||||||
|
let preSign = '+';
|
||||||
|
let numStr = '';
|
||||||
|
const n = s.length;
|
||||||
|
for (let i = 0; i < n; ++i) {
|
||||||
|
if (s[i] === '.' || (!isNaN(Number(s[i])) && s[i] !== ' ')) {
|
||||||
|
numStr += s[i];
|
||||||
|
} else if (s[i] === '(') {
|
||||||
|
let isClose = 1;
|
||||||
|
let j = i;
|
||||||
|
while (isClose > 0) {
|
||||||
|
j += 1;
|
||||||
|
if (s[j] === '(') isClose += 1;
|
||||||
|
if (s[j] === ')') isClose -= 1;
|
||||||
|
}
|
||||||
|
numStr = `${calculate(s.slice(i + 1, j))}`;
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
if ((isNaN(Number(s[i])) && s[i] !== '.') || i === n - 1) {
|
||||||
|
let num = parseFloat(numStr);
|
||||||
|
switch (preSign) {
|
||||||
|
case '+':
|
||||||
|
stack.push(num);
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
stack.push(-num);
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
stack.push(stack.pop() * num);
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
stack.push(stack.pop() / num);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
preSign = s[i];
|
||||||
|
numStr = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ans = 0;
|
||||||
|
while (stack.length) {
|
||||||
|
ans += stack.pop();
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
};
|
||||||
|
module.exports = calculate;
|
||||||
|
})();
|
||||||
292
lib/pen.js
292
lib/pen.js
@ -1,40 +1,44 @@
|
|||||||
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;
|
||||||
this.globalWidth = {};
|
|
||||||
this.globalHeight = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isMoving = false
|
paint(callback) {
|
||||||
movingCache = {}
|
|
||||||
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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_background() {
|
_background() {
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
const {
|
const { width, height } = this.style;
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = this.style;
|
|
||||||
const bg = this.data.background;
|
const bg = this.data.background;
|
||||||
this.ctx.translate(width / 2, height / 2);
|
this.ctx.translate(width / 2, height / 2);
|
||||||
|
|
||||||
@ -60,7 +64,7 @@ export default class Painter {
|
|||||||
_drawAbsolute(view) {
|
_drawAbsolute(view) {
|
||||||
if (!(view && view.type)) {
|
if (!(view && view.type)) {
|
||||||
// 过滤无效 view
|
// 过滤无效 view
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
// 证明 css 为数组形式,需要合并
|
// 证明 css 为数组形式,需要合并
|
||||||
if (view.css && view.css.length) {
|
if (view.css && view.css.length) {
|
||||||
@ -85,20 +89,14 @@ export default class Painter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_border({
|
_border({ borderRadius = 0, width, height, borderWidth = 0, borderStyle = 'solid' }) {
|
||||||
borderRadius = 0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
borderWidth = 0,
|
|
||||||
borderStyle = 'solid'
|
|
||||||
}) {
|
|
||||||
let r1 = 0,
|
let r1 = 0,
|
||||||
r2 = 0,
|
r2 = 0,
|
||||||
r3 = 0,
|
r3 = 0,
|
||||||
r4 = 0
|
r4 = 0;
|
||||||
const minSize = Math.min(width, height);
|
const minSize = Math.min(width, height);
|
||||||
if (borderRadius) {
|
if (borderRadius) {
|
||||||
const border = borderRadius.split(/\s+/)
|
const border = borderRadius.split(/\s+/);
|
||||||
if (border.length === 4) {
|
if (border.length === 4) {
|
||||||
r1 = Math.min(border[0].toPx(false, minSize), width / 2, height / 2);
|
r1 = Math.min(border[0].toPx(false, minSize), width / 2, height / 2);
|
||||||
r2 = Math.min(border[1].toPx(false, minSize), width / 2, height / 2);
|
r2 = Math.min(border[1].toPx(false, minSize), width / 2, height / 2);
|
||||||
@ -111,30 +109,42 @@ export default class Painter {
|
|||||||
const lineWidth = borderWidth && borderWidth.toPx(false, minSize);
|
const lineWidth = borderWidth && borderWidth.toPx(false, minSize);
|
||||||
this.ctx.lineWidth = lineWidth;
|
this.ctx.lineWidth = lineWidth;
|
||||||
if (borderStyle === 'dashed') {
|
if (borderStyle === 'dashed') {
|
||||||
this.ctx.setLineDash([lineWidth * 4 / 3, lineWidth * 4 / 3]);
|
this.ctx.setLineDash([(lineWidth * 4) / 3, (lineWidth * 4) / 3]);
|
||||||
// this.ctx.lineDashOffset = 2 * lineWidth
|
// this.ctx.lineDashOffset = 2 * lineWidth
|
||||||
} else if (borderStyle === 'dotted') {
|
} else if (borderStyle === 'dotted') {
|
||||||
this.ctx.setLineDash([lineWidth, lineWidth]);
|
this.ctx.setLineDash([lineWidth, lineWidth]);
|
||||||
}
|
}
|
||||||
const notSolid = borderStyle !== 'solid'
|
const notSolid = borderStyle !== 'solid';
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
|
|
||||||
notSolid && r1 === 0 && this.ctx.moveTo(-width / 2 - lineWidth, -height / 2 - lineWidth / 2) // 顶边虚线规避重叠规则
|
notSolid && r1 === 0 && this.ctx.moveTo(-width / 2 - lineWidth, -height / 2 - lineWidth / 2); // 顶边虚线规避重叠规则
|
||||||
r1 !== 0 && this.ctx.arc(-width / 2 + r1, -height / 2 + r1, r1 + lineWidth / 2, 1 * Math.PI, 1.5 * Math.PI); //左上角圆弧
|
r1 !== 0 && this.ctx.arc(-width / 2 + r1, -height / 2 + r1, r1 + lineWidth / 2, 1 * Math.PI, 1.5 * Math.PI); //左上角圆弧
|
||||||
this.ctx.lineTo(r2 === 0 ? notSolid ? width / 2 : width / 2 + lineWidth / 2 : width / 2 - r2, -height / 2 - lineWidth / 2); // 顶边线
|
this.ctx.lineTo(
|
||||||
|
r2 === 0 ? (notSolid ? width / 2 : width / 2 + lineWidth / 2) : width / 2 - r2,
|
||||||
|
-height / 2 - lineWidth / 2,
|
||||||
|
); // 顶边线
|
||||||
|
|
||||||
notSolid && r2 === 0 && this.ctx.moveTo(width / 2 + lineWidth / 2, -height / 2 - lineWidth) // 右边虚线规避重叠规则
|
notSolid && r2 === 0 && this.ctx.moveTo(width / 2 + lineWidth / 2, -height / 2 - lineWidth); // 右边虚线规避重叠规则
|
||||||
r2 !== 0 && this.ctx.arc(width / 2 - r2, -height / 2 + r2, r2 + lineWidth / 2, 1.5 * Math.PI, 2 * Math.PI); // 右上角圆弧
|
r2 !== 0 && this.ctx.arc(width / 2 - r2, -height / 2 + r2, r2 + lineWidth / 2, 1.5 * Math.PI, 2 * Math.PI); // 右上角圆弧
|
||||||
this.ctx.lineTo(width / 2 + lineWidth / 2, r3 === 0 ? notSolid ? height / 2 : height / 2 + lineWidth / 2 : height / 2 - r3); // 右边线
|
this.ctx.lineTo(
|
||||||
|
width / 2 + lineWidth / 2,
|
||||||
|
r3 === 0 ? (notSolid ? height / 2 : height / 2 + lineWidth / 2) : height / 2 - r3,
|
||||||
|
); // 右边线
|
||||||
|
|
||||||
notSolid && r3 === 0 && this.ctx.moveTo(width / 2 + lineWidth, height / 2 + lineWidth / 2) // 底边虚线规避重叠规则
|
notSolid && r3 === 0 && this.ctx.moveTo(width / 2 + lineWidth, height / 2 + lineWidth / 2); // 底边虚线规避重叠规则
|
||||||
r3 !== 0 && this.ctx.arc(width / 2 - r3, height / 2 - r3, r3 + lineWidth / 2, 0, 0.5 * Math.PI); // 右下角圆弧
|
r3 !== 0 && this.ctx.arc(width / 2 - r3, height / 2 - r3, r3 + lineWidth / 2, 0, 0.5 * Math.PI); // 右下角圆弧
|
||||||
this.ctx.lineTo(r4 === 0 ? notSolid ? -width / 2 : -width / 2 - lineWidth / 2 : -width / 2 + r4, height / 2 + lineWidth / 2); // 底边线
|
this.ctx.lineTo(
|
||||||
|
r4 === 0 ? (notSolid ? -width / 2 : -width / 2 - lineWidth / 2) : -width / 2 + r4,
|
||||||
|
height / 2 + lineWidth / 2,
|
||||||
|
); // 底边线
|
||||||
|
|
||||||
notSolid && r4 === 0 && this.ctx.moveTo(-width / 2 - lineWidth / 2, height / 2 + lineWidth) // 左边虚线规避重叠规则
|
notSolid && r4 === 0 && this.ctx.moveTo(-width / 2 - lineWidth / 2, height / 2 + lineWidth); // 左边虚线规避重叠规则
|
||||||
r4 !== 0 && this.ctx.arc(-width / 2 + r4, height / 2 - r4, r4 + lineWidth / 2, 0.5 * Math.PI, 1 * Math.PI); // 左下角圆弧
|
r4 !== 0 && this.ctx.arc(-width / 2 + r4, height / 2 - r4, r4 + lineWidth / 2, 0.5 * Math.PI, 1 * Math.PI); // 左下角圆弧
|
||||||
this.ctx.lineTo(-width / 2 - lineWidth / 2, r1 === 0 ? notSolid ? -height / 2 : -height / 2 - lineWidth / 2 : -height / 2 + r1); // 左边线
|
this.ctx.lineTo(
|
||||||
notSolid && r1 === 0 && this.ctx.moveTo(-width / 2 - lineWidth, -height / 2 - lineWidth / 2) // 顶边虚线规避重叠规则
|
-width / 2 - lineWidth / 2,
|
||||||
|
r1 === 0 ? (notSolid ? -height / 2 : -height / 2 - lineWidth / 2) : -height / 2 + r1,
|
||||||
|
); // 左边线
|
||||||
|
notSolid && r1 === 0 && this.ctx.moveTo(-width / 2 - lineWidth, -height / 2 - lineWidth / 2); // 顶边虚线规避重叠规则
|
||||||
|
|
||||||
if (!notSolid) {
|
if (!notSolid) {
|
||||||
this.ctx.closePath();
|
this.ctx.closePath();
|
||||||
@ -154,13 +164,11 @@ export default class Painter {
|
|||||||
borderRadius,
|
borderRadius,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
borderStyle
|
borderStyle,
|
||||||
})
|
});
|
||||||
this.ctx.fill();
|
this.ctx.fill();
|
||||||
// 在 ios 的 6.6.6 版本上 clip 有 bug,禁掉此类型上的 clip,也就意味着,在此版本微信的 ios 设备下无法使用 border 属性
|
// 在 ios 的 6.6.6 版本上 clip 有 bug,禁掉此类型上的 clip,也就意味着,在此版本微信的 ios 设备下无法使用 border 属性
|
||||||
if (!(getApp().systemInfo &&
|
if (!(getApp().systemInfo && getApp().systemInfo.version <= '6.6.6' && getApp().systemInfo.platform === 'ios')) {
|
||||||
getApp().systemInfo.version <= '6.6.6' &&
|
|
||||||
getApp().systemInfo.platform === 'ios')) {
|
|
||||||
this.ctx.clip();
|
this.ctx.clip();
|
||||||
}
|
}
|
||||||
this.ctx.globalAlpha = 1;
|
this.ctx.globalAlpha = 1;
|
||||||
@ -174,25 +182,20 @@ export default class Painter {
|
|||||||
if (!view.css) {
|
if (!view.css) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {
|
const { borderRadius, borderWidth, borderColor, borderStyle } = view.css;
|
||||||
borderRadius,
|
|
||||||
borderWidth,
|
|
||||||
borderColor,
|
|
||||||
borderStyle
|
|
||||||
} = view.css;
|
|
||||||
if (!borderWidth) {
|
if (!borderWidth) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
this._preProcess(view, true);
|
this._preProcess(view, true);
|
||||||
this.ctx.strokeStyle = (borderColor || 'black');
|
this.ctx.strokeStyle = borderColor || 'black';
|
||||||
this._border({
|
this._border({
|
||||||
borderRadius,
|
borderRadius,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
borderWidth,
|
borderWidth,
|
||||||
borderStyle
|
borderStyle,
|
||||||
})
|
});
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
}
|
}
|
||||||
@ -204,26 +207,30 @@ export default class Painter {
|
|||||||
const paddings = this._doPaddings(view);
|
const paddings = this._doPaddings(view);
|
||||||
switch (view.type) {
|
switch (view.type) {
|
||||||
case 'text': {
|
case 'text': {
|
||||||
const textArray = view.text.split('\n');
|
const textArray = String(view.text).split('\n');
|
||||||
// 处理多个连续的'\n'
|
// 处理多个连续的'\n'
|
||||||
for (let i = 0; i < textArray.length; ++i) {
|
for (let i = 0; i < textArray.length; ++i) {
|
||||||
if (textArray[i] === '') {
|
if (textArray[i] === '') {
|
||||||
textArray[i] = ' ';
|
textArray[i] = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fontWeight = view.css.fontWeight === 'bold' ? 'bold' : 'normal';
|
const fontWeight = view.css.fontWeight || '400';
|
||||||
const textStyle = view.css.textStyle === 'italic' ? 'italic' : 'normal';
|
const textStyle = view.css.textStyle || 'normal';
|
||||||
if (!view.css.fontSize) {
|
if (!view.css.fontSize) {
|
||||||
view.css.fontSize = '20rpx';
|
view.css.fontSize = '20rpx';
|
||||||
}
|
}
|
||||||
this.ctx.font = `${textStyle} ${fontWeight} ${view.css.fontSize.toPx()}px "${view.css.fontFamily || 'sans-serif'}"`;
|
this.ctx.font = `${textStyle} ${fontWeight} ${view.css.fontSize.toPx()}px "${
|
||||||
|
view.css.fontFamily || 'sans-serif'
|
||||||
|
}"`;
|
||||||
// 计算行数
|
// 计算行数
|
||||||
let lines = 0;
|
let lines = 0;
|
||||||
const linesArray = [];
|
const linesArray = [];
|
||||||
for (let i = 0; i < textArray.length; ++i) {
|
for (let i = 0; i < textArray.length; ++i) {
|
||||||
const textLength = this.ctx.measureText(textArray[i]).width;
|
const textLength = this.ctx.measureText(textArray[i]).width;
|
||||||
const minWidth = view.css.fontSize.toPx() + paddings[1] + paddings[3];
|
const minWidth = view.css.fontSize.toPx() + paddings[1] + paddings[3];
|
||||||
let partWidth = view.css.width ? view.css.width.toPx(false, this.style.width) - paddings[1] - paddings[3] : textLength;
|
let partWidth = view.css.width
|
||||||
|
? view.css.width.toPx(false, this.style.width) - paddings[1] - paddings[3]
|
||||||
|
: textLength;
|
||||||
if (partWidth < minWidth) {
|
if (partWidth < minWidth) {
|
||||||
partWidth = minWidth;
|
partWidth = minWidth;
|
||||||
}
|
}
|
||||||
@ -261,10 +268,10 @@ export default class Painter {
|
|||||||
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);
|
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);
|
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);
|
width = view.css.width.toPx(false, this.style.width);
|
||||||
height = view.css.height.toPx(false, this.style.height);
|
height = view.css.height.toPx(false, this.style.height);
|
||||||
@ -288,14 +295,17 @@ export default class Painter {
|
|||||||
// 可以用数组方式,把文字长度计算进去
|
// 可以用数组方式,把文字长度计算进去
|
||||||
// [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.globalWidth[rights[1]] * (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);
|
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.globalWidth[lefts[1]] * (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;
|
||||||
@ -310,16 +320,16 @@ export default class Painter {
|
|||||||
y = view.css.top.toPx(true, this.style.height);
|
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.globalHeight[tops[1]] * (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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const angle = view.css && view.css.rotate ? this._getAngle(view.css.rotate) : 0;
|
const angle = view.css && view.css.rotate ? this._getAngle(view.css.rotate) : 0;
|
||||||
// 当设置了 right 时,默认 align 用 right,反之用 left
|
// 当设置了 right 时,默认 align 用 right,反之用 left
|
||||||
const align = view.css && view.css.align ? view.css.align : (view.css && view.css.right ? 'right' : 'left');
|
const align = view.css && view.css.align ? view.css.align : view.css && view.css.right ? 'right' : 'left';
|
||||||
const verticalAlign = view.css && view.css.verticalAlign ? view.css.verticalAlign : 'top';
|
const verticalAlign = view.css && view.css.verticalAlign ? view.css.verticalAlign : 'top';
|
||||||
// 记录绘制时的画布
|
// 记录绘制时的画布
|
||||||
let xa = 0;
|
let xa = 0;
|
||||||
@ -350,17 +360,17 @@ export default class Painter {
|
|||||||
// 记录该 view 的有效点击区域
|
// 记录该 view 的有效点击区域
|
||||||
// TODO ,旋转和裁剪的判断
|
// TODO ,旋转和裁剪的判断
|
||||||
// 记录在真实画布上的左侧
|
// 记录在真实画布上的左侧
|
||||||
let left = x
|
let left = x;
|
||||||
if (align === 'center') {
|
if (align === 'center') {
|
||||||
left = x - width / 2
|
left = x - width / 2;
|
||||||
} else if (align === 'right') {
|
} else if (align === 'right') {
|
||||||
left = x - width
|
left = x - width;
|
||||||
}
|
}
|
||||||
var top = y;
|
var top = y;
|
||||||
if (verticalAlign === 'center') {
|
if (verticalAlign === 'center') {
|
||||||
top = y - height / 2;
|
top = y - height / 2;
|
||||||
} else if (verticalAlign === 'bottom') {
|
} else if (verticalAlign === 'bottom') {
|
||||||
top = y - height
|
top = y - height;
|
||||||
}
|
}
|
||||||
if (view.rect) {
|
if (view.rect) {
|
||||||
view.rect.left = left;
|
view.rect.left = left;
|
||||||
@ -376,7 +386,7 @@ export default class Painter {
|
|||||||
right: left + width,
|
right: left + width,
|
||||||
bottom: top + height,
|
bottom: top + height,
|
||||||
x: view.css && view.css.right ? x - width : x,
|
x: view.css && view.css.right ? x - width : x,
|
||||||
y: y
|
y: y,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,8 +404,14 @@ export default class Painter {
|
|||||||
}
|
}
|
||||||
this._doShadow(view);
|
this._doShadow(view);
|
||||||
if (view.id) {
|
if (view.id) {
|
||||||
this.globalWidth[view.id] = width;
|
penCache.viewRect[view.id] = {
|
||||||
this.globalHeight[view.id] = height;
|
width,
|
||||||
|
height,
|
||||||
|
left: x,
|
||||||
|
top: y,
|
||||||
|
right: x + width,
|
||||||
|
bottom: y + height,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
width: width,
|
width: width,
|
||||||
@ -407,9 +423,7 @@ export default class Painter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_doPaddings(view) {
|
_doPaddings(view) {
|
||||||
const {
|
const { padding } = view.css ? view.css : {};
|
||||||
padding,
|
|
||||||
} = view.css;
|
|
||||||
let pd = [0, 0, 0, 0];
|
let pd = [0, 0, 0, 0];
|
||||||
if (padding) {
|
if (padding) {
|
||||||
const pdg = padding.split(/\s+/);
|
const pdg = padding.split(/\s+/);
|
||||||
@ -442,19 +456,14 @@ export default class Painter {
|
|||||||
// 画文字的背景图片
|
// 画文字的背景图片
|
||||||
_doBackground(view) {
|
_doBackground(view) {
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
const {
|
const { width: rawWidth, height: rawHeight } = this._preProcess(view, true);
|
||||||
width: rawWidth,
|
|
||||||
height: rawHeight,
|
|
||||||
} = this._preProcess(view, true);
|
|
||||||
|
|
||||||
const {
|
const { background } = view.css;
|
||||||
background,
|
|
||||||
} = view.css;
|
|
||||||
let pd = this._doPaddings(view);
|
let pd = this._doPaddings(view);
|
||||||
const width = rawWidth + pd[1] + pd[3];
|
const width = rawWidth + pd[1] + pd[3];
|
||||||
const height = rawHeight + pd[0] + pd[2];
|
const height = rawHeight + pd[0] + pd[2];
|
||||||
|
|
||||||
this._doClip(view.css.borderRadius, width, height, view.css.borderStyle)
|
this._doClip(view.css.borderRadius, width, height, view.css.borderStyle);
|
||||||
if (GD.api.isGradient(background)) {
|
if (GD.api.isGradient(background)) {
|
||||||
GD.api.doGradient(background, width, height, this.ctx);
|
GD.api.doGradient(background, width, height, this.ctx);
|
||||||
} else {
|
} else {
|
||||||
@ -467,10 +476,7 @@ export default class Painter {
|
|||||||
|
|
||||||
_drawQRCode(view) {
|
_drawQRCode(view) {
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
const {
|
const { width, height } = this._preProcess(view);
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = this._preProcess(view);
|
|
||||||
QR.api.draw(view.content, this.ctx, -width / 2, -height / 2, width, height, view.css.background, view.css.color);
|
QR.api.draw(view.content, this.ctx, -width / 2, -height / 2, width, height, view.css.background, view.css.color);
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
this._doBorder(view, width, height);
|
this._doBorder(view, width, height);
|
||||||
@ -481,10 +487,7 @@ export default class Painter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
const {
|
const { width, height } = this._preProcess(view);
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = this._preProcess(view);
|
|
||||||
// 获得缩放到图片大小级别的裁减框
|
// 获得缩放到图片大小级别的裁减框
|
||||||
let rWidth = view.sWidth;
|
let rWidth = view.sWidth;
|
||||||
let rHeight = view.sHeight;
|
let rHeight = view.sHeight;
|
||||||
@ -514,7 +517,6 @@ export default class Painter {
|
|||||||
this._doBorder(view, width, height);
|
this._doBorder(view, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
callbackInfo = {}
|
|
||||||
_fillAbsText(view) {
|
_fillAbsText(view) {
|
||||||
if (!view.text) {
|
if (!view.text) {
|
||||||
return;
|
return;
|
||||||
@ -524,23 +526,12 @@ export default class Painter {
|
|||||||
this._doBackground(view);
|
this._doBackground(view);
|
||||||
}
|
}
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
const {
|
const { width, height, extra } = this._preProcess(view, view.css.background && view.css.borderRadius);
|
||||||
width,
|
this.ctx.fillStyle = view.css.color || 'black';
|
||||||
height,
|
if (view.id && penCache.textLines[view.id]) {
|
||||||
extra,
|
|
||||||
} = this._preProcess(view, view.css.background && view.css.borderRadius);
|
|
||||||
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.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 {
|
const { measuredWith, text, x, y, textDecoration } = i;
|
||||||
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);
|
||||||
} else {
|
} else {
|
||||||
@ -558,25 +549,15 @@ export default class Painter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const {
|
const { lines, lineHeight, textArray, linesArray } = extra;
|
||||||
lines,
|
|
||||||
lineHeight,
|
|
||||||
textArray,
|
|
||||||
linesArray,
|
|
||||||
} = extra;
|
|
||||||
// 如果设置了id,则保留 text 的长度
|
// 如果设置了id,则保留 text 的长度
|
||||||
if (view.id) {
|
if (view.id) {
|
||||||
let textWidth = 0;
|
let textWidth = 0;
|
||||||
for (let i = 0; i < textArray.length; ++i) {
|
for (let i = 0; i < textArray.length; ++i) {
|
||||||
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.globalWidth[view.id] = width ? (textWidth < width ? textWidth : width) : textWidth;
|
penCache.viewRect[view.id].width = width ? (textWidth < width ? textWidth : width) : textWidth;
|
||||||
if (!this.isMoving) {
|
|
||||||
Object.assign(this.callbackInfo, {
|
|
||||||
globalWidth: this.globalWidth[view.id]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let lineIndex = 0;
|
let lineIndex = 0;
|
||||||
for (let j = 0; j < textArray.length; ++j) {
|
for (let j = 0; j < textArray.length; ++j) {
|
||||||
@ -594,7 +575,10 @@ export default class Painter {
|
|||||||
let measuredWith = this.ctx.measureText(text).width;
|
let measuredWith = this.ctx.measureText(text).width;
|
||||||
// 如果测量大小小于width一个字符的大小,则进行补齐,如果测量大小超出 width,则进行减除
|
// 如果测量大小小于width一个字符的大小,则进行补齐,如果测量大小超出 width,则进行减除
|
||||||
// 如果已经到文本末尾,也不要进行该循环
|
// 如果已经到文本末尾,也不要进行该循环
|
||||||
while ((start + alreadyCount <= textArray[j].length) && (width - measuredWith > view.css.fontSize.toPx() || measuredWith - width > view.css.fontSize.toPx())) {
|
while (
|
||||||
|
start + alreadyCount <= textArray[j].length &&
|
||||||
|
(width - measuredWith > view.css.fontSize.toPx() || measuredWith - width > view.css.fontSize.toPx())
|
||||||
|
) {
|
||||||
if (measuredWith < width) {
|
if (measuredWith < width) {
|
||||||
text = textArray[j].substr(start, ++alreadyCount);
|
text = textArray[j].substr(start, ++alreadyCount);
|
||||||
} else {
|
} else {
|
||||||
@ -607,7 +591,7 @@ export default class Painter {
|
|||||||
}
|
}
|
||||||
measuredWith = this.ctx.measureText(text).width;
|
measuredWith = this.ctx.measureText(text).width;
|
||||||
}
|
}
|
||||||
start += text.length
|
start += text.length;
|
||||||
// 如果是最后一行了,发现还有未绘制完的内容,则加...
|
// 如果是最后一行了,发现还有未绘制完的内容,则加...
|
||||||
if (lineIndex === lines - 1 && (j < textArray.length - 1 || start < textArray[j].length)) {
|
if (lineIndex === lines - 1 && (j < textArray.length - 1 || start < textArray[j].length)) {
|
||||||
while (this.ctx.measureText(`${text}...`).width > width) {
|
while (this.ctx.measureText(`${text}...`).width > width) {
|
||||||
@ -629,7 +613,7 @@ export default class Painter {
|
|||||||
lineX = x - measuredWith / 2;
|
lineX = x - measuredWith / 2;
|
||||||
break;
|
break;
|
||||||
case 'right':
|
case 'right':
|
||||||
x = (width / 2);
|
x = width / 2;
|
||||||
lineX = x - measuredWith;
|
lineX = x - measuredWith;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -637,7 +621,10 @@ export default class Painter {
|
|||||||
lineX = x;
|
lineX = x;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const y = -(height / 2) + (lineIndex === 0 ? view.css.fontSize.toPx() : (view.css.fontSize.toPx() + lineIndex * lineHeight));
|
|
||||||
|
const y =
|
||||||
|
-(height / 2) +
|
||||||
|
(lineIndex === 0 ? view.css.fontSize.toPx() : view.css.fontSize.toPx() + lineIndex * lineHeight);
|
||||||
lineIndex++;
|
lineIndex++;
|
||||||
if (view.css.textStyle === 'stroke') {
|
if (view.css.textStyle === 'stroke') {
|
||||||
this.ctx.strokeText(text, x, y, measuredWith);
|
this.ctx.strokeText(text, x, y, measuredWith);
|
||||||
@ -654,43 +641,47 @@ export default class Painter {
|
|||||||
this.ctx.lineTo(lineX + measuredWith, y);
|
this.ctx.lineTo(lineX + measuredWith, y);
|
||||||
textDecoration = {
|
textDecoration = {
|
||||||
moveTo: [lineX, y],
|
moveTo: [lineX, y],
|
||||||
lineTo: [lineX + measuredWith, y]
|
lineTo: [lineX + measuredWith, y],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (/\boverline\b/.test(view.css.textDecoration)) {
|
if (/\boverline\b/.test(view.css.textDecoration)) {
|
||||||
this.ctx.moveTo(lineX, y - fontSize);
|
this.ctx.moveTo(lineX, y - fontSize);
|
||||||
this.ctx.lineTo(lineX + measuredWith, y - fontSize);
|
this.ctx.lineTo(lineX + measuredWith, y - fontSize);
|
||||||
textDecoration = {
|
textDecoration = {
|
||||||
moveTo: [lineX, y - fontSize],
|
moveTo: [lineX, y - fontSize],
|
||||||
lineTo: [lineX + measuredWith, y - fontSize]
|
lineTo: [lineX + measuredWith, y - fontSize],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
if (/\bline-through\b/.test(view.css.textDecoration)) {
|
if (/\bline-through\b/.test(view.css.textDecoration)) {
|
||||||
this.ctx.moveTo(lineX, y - fontSize / 3);
|
this.ctx.moveTo(lineX, y - fontSize / 3);
|
||||||
this.ctx.lineTo(lineX + measuredWith, y - fontSize / 3);
|
this.ctx.lineTo(lineX + measuredWith, y - fontSize / 3);
|
||||||
textDecoration = {
|
textDecoration = {
|
||||||
moveTo: [lineX, y - fontSize / 3],
|
moveTo: [lineX, y - fontSize / 3],
|
||||||
lineTo: [lineX + measuredWith, y - fontSize / 3]
|
lineTo: [lineX + measuredWith, y - fontSize / 3],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
this.ctx.closePath();
|
this.ctx.closePath();
|
||||||
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 ? this.callbackInfo.lineArray.push({
|
penCache.textLines[view.id]
|
||||||
text,
|
? penCache.textLines[view.id].push({
|
||||||
x,
|
text,
|
||||||
y,
|
x,
|
||||||
measuredWith,
|
y,
|
||||||
textDecoration
|
measuredWith,
|
||||||
}) : this.callbackInfo.lineArray = [{
|
textDecoration,
|
||||||
text,
|
})
|
||||||
x,
|
: (penCache.textLines[view.id] = [
|
||||||
y,
|
{
|
||||||
measuredWith,
|
text,
|
||||||
textDecoration
|
x,
|
||||||
}]
|
y,
|
||||||
|
measuredWith,
|
||||||
|
textDecoration,
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -701,27 +692,20 @@ export default class Painter {
|
|||||||
|
|
||||||
_drawAbsRect(view) {
|
_drawAbsRect(view) {
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
const {
|
const { width, height } = this._preProcess(view);
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = this._preProcess(view);
|
|
||||||
if (GD.api.isGradient(view.css.color)) {
|
if (GD.api.isGradient(view.css.color)) {
|
||||||
GD.api.doGradient(view.css.color, width, height, this.ctx);
|
GD.api.doGradient(view.css.color, width, height, this.ctx);
|
||||||
} else {
|
} else {
|
||||||
this.ctx.fillStyle = view.css.color;
|
this.ctx.fillStyle = view.css.color;
|
||||||
}
|
}
|
||||||
const {
|
const { borderRadius, borderStyle, borderWidth } = view.css;
|
||||||
borderRadius,
|
|
||||||
borderStyle,
|
|
||||||
borderWidth
|
|
||||||
} = view.css
|
|
||||||
this._border({
|
this._border({
|
||||||
borderRadius,
|
borderRadius,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
borderWidth,
|
borderWidth,
|
||||||
borderStyle
|
borderStyle,
|
||||||
})
|
});
|
||||||
this.ctx.fill();
|
this.ctx.fill();
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
this._doBorder(view, width, height);
|
this._doBorder(view, width, height);
|
||||||
@ -735,7 +719,7 @@ export default class Painter {
|
|||||||
}
|
}
|
||||||
const box = view.css.shadow.replace(/,\s+/g, ',').split(/\s+/);
|
const box = view.css.shadow.replace(/,\s+/g, ',').split(/\s+/);
|
||||||
if (box.length > 4) {
|
if (box.length > 4) {
|
||||||
console.error('shadow don\'t spread option');
|
console.error("shadow don't spread option");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.ctx.shadowOffsetX = parseInt(box[0], 10);
|
this.ctx.shadowOffsetX = parseInt(box[0], 10);
|
||||||
@ -745,6 +729,6 @@ export default class Painter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getAngle(angle) {
|
_getAngle(angle) {
|
||||||
return Number(angle) * Math.PI / 180;
|
return (Number(angle) * Math.PI) / 180;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
704
painter.js
704
painter.js
File diff suppressed because it is too large
Load Diff
36
painter.wxml
36
painter.wxml
@ -1,31 +1,21 @@
|
|||||||
<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;" />
|
||||||
<canvas canvas-id="bottom" style="{{painterStyle}};position: absolute;" />
|
<block wx:if="{{dancePalette}}">
|
||||||
<canvas canvas-id="k-canvas" style="{{painterStyle}};position: absolute;" />
|
<canvas canvas-id="bottom" style="{{painterStyle}};position: absolute;" />
|
||||||
<canvas canvas-id="top" style="{{painterStyle}};position: absolute;" />
|
<canvas canvas-id="k-canvas" style="{{painterStyle}};position: absolute;" />
|
||||||
<canvas
|
<canvas canvas-id="top" style="{{painterStyle}};position: absolute;" />
|
||||||
canvas-id="front"
|
<canvas
|
||||||
style="{{painterStyle}};position: absolute;"
|
canvas-id="front"
|
||||||
bindtouchstart="onTouchStart"
|
style="{{painterStyle}};position: absolute;"
|
||||||
bindtouchmove="onTouchMove"
|
bindtouchstart="onTouchStart"
|
||||||
bindtouchend="onTouchEnd"
|
bindtouchmove="onTouchMove"
|
||||||
bindtouchcancel="onTouchCancel"
|
bindtouchend="onTouchEnd"
|
||||||
disable-scroll="{{true}}" />
|
bindtouchcancel="onTouchCancel"
|
||||||
|
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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user