painter 增加dpr

This commit is contained in:
yulixing 2019-06-25 10:36:11 +08:00
parent 9fb9d209ab
commit 10e0399f60
2 changed files with 154 additions and 160 deletions

View File

@ -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