painter 增加dpr
This commit is contained in:
parent
9fb9d209ab
commit
10e0399f60
@ -1,91 +1,96 @@
|
||||
const { registerFont, createCanvas, loadImage } = require('canvas');
|
||||
const {registerFont, createCanvas, loadImage} = require('canvas')
|
||||
const path = require('path')
|
||||
|
||||
const drawRoundRect = require('./round_rect');
|
||||
const wrapText = require('./wrap_text');
|
||||
const fillTextVertical = require('./vertical_text');
|
||||
const drawRoundRect = require('./round_rect')
|
||||
const wrapText = require('./wrap_text')
|
||||
const fillTextVertical = require('./vertical_text')
|
||||
|
||||
const drawShadow = require('./shadow');
|
||||
const transformBase64 = require('./transfer_base64');
|
||||
const drawShadow = require('./shadow')
|
||||
const transformBase64 = require('./transfer_base64')
|
||||
|
||||
const config = require('../../../config/config');
|
||||
const config = require('../../../config/config')
|
||||
|
||||
// 处理图片在移动端模糊情况
|
||||
const dpr = 2
|
||||
|
||||
module.exports = async function paint(opt) {
|
||||
// 获取画布基本信息
|
||||
const baseInfo = opt.baseInfo;
|
||||
const views = opt.views;
|
||||
const canvasW = parseInt(baseInfo.width);
|
||||
const canvasH = parseInt(baseInfo.height);
|
||||
const baseInfo = opt.baseInfo
|
||||
const views = opt.views
|
||||
const canvasW = parseInt(baseInfo.width) * dpr
|
||||
const canvasH = parseInt(baseInfo.height) * dpr
|
||||
|
||||
//引入字体
|
||||
const fonts = baseInfo.fonts;
|
||||
|
||||
const fonts = baseInfo.fonts
|
||||
|
||||
if (fonts.length > 0) {
|
||||
for (let i = 0; i < fonts.length; i++) {
|
||||
const fontName = fonts[i].name;
|
||||
registerFont(__dirname+'../../../'+ fonts[i].loc_path, { family: `${fontName}` });
|
||||
const fontName = fonts[i].name
|
||||
registerFont(__dirname + '../../../' + fonts[i].loc_path, {
|
||||
family: `${fontName}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 提取图片
|
||||
const imgs = [];
|
||||
const imgs = []
|
||||
views.map((view, index) => {
|
||||
if (view.type === 'image' && view.url) {
|
||||
imgs.push({
|
||||
url: view.url,
|
||||
index: index
|
||||
});
|
||||
index: index,
|
||||
})
|
||||
}
|
||||
});
|
||||
const imgsInfo = await _absorbImgs(imgs);
|
||||
})
|
||||
const imgsInfo = await _absorbImgs(imgs)
|
||||
|
||||
// 创建画布与画笔
|
||||
const canvas = createCanvas(canvasW, canvasH);
|
||||
const ctx = canvas.getContext('2d');
|
||||
const canvas = createCanvas(canvasW, canvasH)
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
// 画笔功能扩展
|
||||
ctx.wrapText = wrapText;
|
||||
ctx.fillTextVertical = fillTextVertical;
|
||||
ctx.drawRoundRect = drawRoundRect;
|
||||
ctx.wrapText = wrapText
|
||||
ctx.fillTextVertical = fillTextVertical
|
||||
ctx.drawRoundRect = drawRoundRect
|
||||
|
||||
// 绘制背景
|
||||
_drawBg(ctx, baseInfo);
|
||||
_drawBg(ctx, baseInfo)
|
||||
|
||||
//绘制元素
|
||||
|
||||
for (let i = 0; i < views.length; i++) {
|
||||
const view = views[i];
|
||||
_drawView(ctx, view, i, imgsInfo);
|
||||
const view = views[i]
|
||||
_drawView(ctx, view, i, imgsInfo)
|
||||
}
|
||||
|
||||
// 生成图片
|
||||
const tempName = `${new Date().getTime()}.png`;
|
||||
const tempPath = path.join(__dirname, '../../temp/' + tempName);
|
||||
transformBase64(canvas.toDataURL(), tempPath);
|
||||
const tempName = `${new Date().getTime()}.png`
|
||||
const tempPath = path.join(__dirname, '../../temp/' + tempName)
|
||||
transformBase64(canvas.toDataURL(), tempPath)
|
||||
|
||||
return tempName;
|
||||
};
|
||||
return tempName
|
||||
}
|
||||
|
||||
function _drawBg(ctx, info) {
|
||||
ctx.save();
|
||||
const w = parseInt(info.width);
|
||||
const h = parseInt(info.height);
|
||||
const bg = info['back-ground'];
|
||||
let bdr = info['border-radius'] || 0;
|
||||
ctx.save()
|
||||
const w = parseInt(info.width) * dpr
|
||||
const h = parseInt(info.height) * dpr
|
||||
const bg = info['back-ground']
|
||||
let bdr = info['border-radius'] || 0
|
||||
|
||||
// 圆角剪切
|
||||
if (bdr && bdr.endsWith('px')) {
|
||||
bdr = parseInt(bdr);
|
||||
bdr = parseInt(bdr) * dpr
|
||||
} else if (bdr && bdr.endsWith('%')) {
|
||||
bdr = (w * parseInt(bdr)) / 100;
|
||||
bdr = (w * parseInt(bdr)) / 100
|
||||
}
|
||||
ctx.drawRoundRect(0, 0, w, h, bdr, false, false);
|
||||
ctx.clip();
|
||||
ctx.drawRoundRect(0, 0, w, h, bdr, false, false)
|
||||
ctx.clip()
|
||||
|
||||
if (!bg) {
|
||||
// 默认背景
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.fillRect(0, 0, w, h)
|
||||
} else if (
|
||||
bg.startsWith('#') ||
|
||||
bg.startsWith('rgba') ||
|
||||
@ -93,210 +98,199 @@ function _drawBg(ctx, info) {
|
||||
bg.toLowerCase() === 'transparent'
|
||||
) {
|
||||
// 纯色填充
|
||||
ctx.fillStyle = bg;
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
ctx.fillStyle = bg
|
||||
ctx.fillRect(0, 0, w, h)
|
||||
}
|
||||
// TODO: 渐变填充
|
||||
ctx.restore();
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
function _drawView(ctx, view, index, imgsInfo) {
|
||||
switch (view.type) {
|
||||
case 'image':
|
||||
_drawImg(ctx, view, index, imgsInfo);
|
||||
break;
|
||||
_drawImg(ctx, view, index, imgsInfo)
|
||||
break
|
||||
case 'text':
|
||||
_drawText(ctx, view);
|
||||
break;
|
||||
_drawText(ctx, view)
|
||||
break
|
||||
case 'rect':
|
||||
_drawRect(ctx, view);
|
||||
break;
|
||||
_drawRect(ctx, view)
|
||||
break
|
||||
// case 'qrcode':
|
||||
// _drawQRCode(ctx, view);
|
||||
// break;
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
|
||||
// TODO: ???
|
||||
// ctx.beginPath();
|
||||
// ctx.arc(100, 75, 50, 0, 2 * Math.PI);
|
||||
}
|
||||
|
||||
function _drawImg(ctx, view, index, imgsInfo) {
|
||||
const img = imgsInfo[index];
|
||||
const style = view.style;
|
||||
const w = parseInt(style.width);
|
||||
const h = parseInt(style.height);
|
||||
let x = parseInt(style.left);
|
||||
let y = parseInt(style.top);
|
||||
const hasBd = style['border'] ? true : false;
|
||||
let bdr = style['border-radius'] || 0;
|
||||
const img = imgsInfo[index]
|
||||
const style = view.style
|
||||
const w = parseInt(style.width) * dpr
|
||||
const h = parseInt(style.height) * dpr
|
||||
let x = parseInt(style.left) * dpr
|
||||
let y = parseInt(style.top) * dpr
|
||||
const hasBd = style['border'] ? true : false
|
||||
let bdr = style['border-radius'] || 0
|
||||
|
||||
if (!img) return;
|
||||
ctx.save();
|
||||
if (!img) return
|
||||
ctx.save()
|
||||
|
||||
// 圆角剪切
|
||||
if (bdr && bdr.endsWith('px')) {
|
||||
bdr = parseInt(bdr);
|
||||
bdr = parseInt(bdr) * dpr
|
||||
} else if (bdr && bdr.endsWith('%')) {
|
||||
bdr = (w * parseInt(bdr)) / 100;
|
||||
bdr = (w * parseInt(bdr)) / 100
|
||||
}
|
||||
|
||||
// 是否旋转
|
||||
if (style['transform']) {
|
||||
ctx.translate(x + w / 2, y + h / 2);
|
||||
const rotateDeg = _rotateDeg(style['transform']);
|
||||
x = -w / 2;
|
||||
y = -h / 2;
|
||||
ctx.rotate(rotateDeg);
|
||||
ctx.translate(x + w / 2, y + h / 2)
|
||||
const rotateDeg = _rotateDeg(style['transform'])
|
||||
x = -w / 2
|
||||
y = -h / 2
|
||||
ctx.rotate(rotateDeg)
|
||||
}
|
||||
|
||||
if (hasBd) {
|
||||
const borderInfo = style['border'].split(' ');
|
||||
ctx.lineWidth = parseInt(borderInfo[0]);
|
||||
ctx.strokeStyle = borderInfo[2];
|
||||
const borderInfo = style['border'].split(' ')
|
||||
ctx.lineWidth = parseInt(borderInfo[0]) * dpr
|
||||
ctx.strokeStyle = borderInfo[2]
|
||||
}
|
||||
|
||||
ctx.drawRoundRect(x, y, w, h, bdr, false, hasBd);
|
||||
drawShadow(ctx, style);
|
||||
ctx.clip();
|
||||
ctx.drawRoundRect(x, y, w, h, bdr, false, hasBd)
|
||||
drawShadow(ctx, style)
|
||||
ctx.clip()
|
||||
|
||||
// 绘画区域比例
|
||||
const cp = w / h;
|
||||
const cp = w / h
|
||||
// 原图比例
|
||||
const op = img.width / img.height;
|
||||
const op = img.width / img.height
|
||||
if (cp >= op) {
|
||||
const r = img.width / w;
|
||||
const nW = img.width / r;
|
||||
const nH = img.height / r;
|
||||
const nY = y - (nH - h) / 2;
|
||||
ctx.drawImage(img, x, nY, nW, nH);
|
||||
const r = img.width / w
|
||||
const nW = img.width / r
|
||||
const nH = img.height / r
|
||||
const nY = y - (nH - h) / 2
|
||||
ctx.drawImage(img, x, nY, nW, nH)
|
||||
} else {
|
||||
const r = img.height / h;
|
||||
const nW = img.width / r;
|
||||
const nH = img.height / r;
|
||||
const nX = x - (nW - w) / 2;
|
||||
ctx.drawImage(img, nX, y, nW, nH);
|
||||
const r = img.height / h
|
||||
const nW = img.width / r
|
||||
const nH = img.height / r
|
||||
const nX = x - (nW - w) / 2
|
||||
ctx.drawImage(img, nX, y, nW, nH)
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
function _drawText(ctx, view) {
|
||||
const style = view.style;
|
||||
let x = parseInt(style.left);
|
||||
let y = parseInt(style.top);
|
||||
const maxWidth = parseInt(style['max-width']);
|
||||
const lineHeight = parseInt(style['line-height']);
|
||||
const fontWeight = parseInt(style['font-weight']) || 500;
|
||||
const style = view.style
|
||||
let x = parseInt(style.left) * dpr
|
||||
let y = parseInt(style.top) * dpr
|
||||
const width = parseInt(style['width']) * dpr
|
||||
const height = parseInt(style['height']) * dpr
|
||||
const lineHeight = parseInt(style['line-height']) * dpr
|
||||
const fontWeight = parseInt(style['font-weight']) || 500
|
||||
const fontSize = parseInt(style['font-size']) * dpr + 'px'
|
||||
|
||||
ctx.save();
|
||||
ctx.save()
|
||||
// 字体颜色
|
||||
ctx.fillStyle = style.color || '#000';
|
||||
ctx.fillStyle = style.color || '#000'
|
||||
|
||||
// 字体对齐方式
|
||||
ctx.textAlign = style['text-align'] || 'left';
|
||||
ctx.textAlign = style['text-align'] || 'left'
|
||||
|
||||
// 字体大小和fontface
|
||||
ctx.font = style['font-family']
|
||||
? `${fontWeight} ${style['font-size']} "${style['font-family']}"`
|
||||
: `${fontWeight} ${style['font-size']} "Sans"`;
|
||||
? `${fontWeight} ${fontSize} "${style['font-family']}"`
|
||||
: `${fontWeight} ${fontSize} "Sans"`
|
||||
// 文字对齐方式
|
||||
if (style['writing-mode'] === 'vertical-rl') {
|
||||
// 竖排文字
|
||||
ctx.fillTextVertical(
|
||||
view.text,
|
||||
x,
|
||||
y,
|
||||
parseInt(style['max-height']),
|
||||
parseInt(style['line-height']),
|
||||
view
|
||||
);
|
||||
ctx.fillTextVertical(view.text, x, y, height, lineHeight, view)
|
||||
} else {
|
||||
// 横排文字
|
||||
|
||||
// 是否旋转
|
||||
if (style['transform']) {
|
||||
ctx.translate(x + maxWidth / 2, y + lineHeight / 2);
|
||||
const rotateDeg = _rotateDeg(style['transform']);
|
||||
x = -maxWidth / 2;
|
||||
y = -lineHeight / 2;
|
||||
ctx.rotate(rotateDeg);
|
||||
ctx.translate(x + width / 2, y + lineHeight / 2)
|
||||
const rotateDeg = _rotateDeg(style['transform'])
|
||||
x = -width / 2
|
||||
y = -lineHeight / 2
|
||||
ctx.rotate(rotateDeg)
|
||||
}
|
||||
|
||||
ctx.wrapText(
|
||||
view.text,
|
||||
x,
|
||||
y,
|
||||
parseInt(style['max-width']),
|
||||
parseInt(style['line-height']),
|
||||
view
|
||||
);
|
||||
ctx.wrapText(view.text, x, y, width, lineHeight, view)
|
||||
}
|
||||
ctx.restore();
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
function _drawRect(ctx, view) {
|
||||
const style = view.style;
|
||||
const w = parseInt(style.width);
|
||||
const h = parseInt(style.height);
|
||||
let x = parseInt(style.left);
|
||||
let y = parseInt(style.top);
|
||||
const hasBd = style['border'] ? true : false;
|
||||
let bdr = style['border-radius'] || 0;
|
||||
const style = view.style
|
||||
const w = parseInt(style.width) * dpr
|
||||
const h = parseInt(style.height) * dpr
|
||||
let x = parseInt(style.left) * dpr
|
||||
let y = parseInt(style.top) * dpr
|
||||
const hasBd = style['border'] ? true : false
|
||||
let bdr = style['border-radius'] || 0
|
||||
|
||||
ctx.save();
|
||||
ctx.save()
|
||||
|
||||
// 圆角剪切
|
||||
if (bdr && bdr.endsWith('px')) {
|
||||
bdr = parseInt(bdr);
|
||||
bdr = parseInt(bdr) * dpr
|
||||
} else if (bdr && bdr.endsWith('%')) {
|
||||
bdr = (w * parseInt(bdr)) / 100;
|
||||
bdr = (w * parseInt(bdr)) / 100
|
||||
}
|
||||
|
||||
// 是否旋转
|
||||
if (style['transform']) {
|
||||
ctx.translate(x + w / 2, y + h / 2);
|
||||
const rotateDeg = _rotateDeg(style['transform']);
|
||||
x = -w / 2;
|
||||
y = -h / 2;
|
||||
ctx.rotate(rotateDeg);
|
||||
ctx.translate(x + w / 2, y + h / 2)
|
||||
const rotateDeg = _rotateDeg(style['transform'])
|
||||
x = -w / 2
|
||||
y = -h / 2
|
||||
ctx.rotate(rotateDeg)
|
||||
}
|
||||
|
||||
if (hasBd) {
|
||||
const borderInfo = style['border'].split(' ');
|
||||
ctx.lineWidth = parseInt(borderInfo[0]);
|
||||
ctx.strokeStyle = borderInfo[2];
|
||||
const borderInfo = style['border'].split(' ')
|
||||
ctx.lineWidth = parseInt(borderInfo[0])
|
||||
ctx.strokeStyle = borderInfo[2]
|
||||
}
|
||||
ctx.fillStyle = style['back-ground'];
|
||||
ctx.fillStyle = style['back-ground']
|
||||
|
||||
ctx.drawRoundRect(x, y, w, h, bdr, true, hasBd);
|
||||
drawShadow(ctx, style);
|
||||
ctx.fill();
|
||||
ctx.clip();
|
||||
ctx.restore();
|
||||
ctx.drawRoundRect(x, y, w, h, bdr, true, hasBd)
|
||||
drawShadow(ctx, style)
|
||||
ctx.fill()
|
||||
ctx.clip()
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
function _absorbImgs(imgs) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const imgsNum = imgs.length;
|
||||
const result = {};
|
||||
const imgsNum = imgs.length
|
||||
const result = {}
|
||||
try {
|
||||
for (let i = 0; i < imgsNum; i++) {
|
||||
const imgInfo = imgs[i];
|
||||
const imgData = await loadImage(imgInfo.url);
|
||||
result[imgInfo.index] = imgData;
|
||||
const imgInfo = imgs[i]
|
||||
const imgData = await loadImage(imgInfo.url)
|
||||
result[imgInfo.index] = imgData
|
||||
}
|
||||
resolve(result);
|
||||
resolve(result)
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
reject(err)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// 提取旋转角度
|
||||
function _rotateDeg(str) {
|
||||
const reg = /^rotate\((-?\d*)deg\)$/;
|
||||
const result = reg.exec(str)[1];
|
||||
return (parseInt(result) * Math.PI) / 180 || 0;
|
||||
const reg = /^rotate\((-?\d*)deg\)$/
|
||||
const result = reg.exec(str)[1]
|
||||
return (parseInt(result) * Math.PI) / 180 || 0
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 144 KiB |
Loading…
x
Reference in New Issue
Block a user