本次需求需要用户选择照片或拍照后,把图片用canvas绘制(添加其他的文字,背景图等),生成照片让用户可以保存。
开始限制用户不能传gif图片。所有 input
实际开发中,发现ios没问题。但是安卓只能用相册,无法唤起相机。
所有这里只能用
用户传gif后,只是绘制gif图第一帧。
other
记录下本次主要绘制的核心代码
drawPoster函数绘制一个数组,由于照片要等待加载好之后才能继续绘制,不然有遮挡。
然后绘制用户照片的时候,可能用户传的照片与实际绘制区域不一样,用coverImg转换。实现img的cover效果
util.js
/**
* @param {Number} box_w 固定盒子的宽, box_h 固定盒子的高
* @param {Number} source_w 原图片的宽, source_h 原图片的高
* @return {Object} {截取的图片信息},对应drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)参数
*/
function coverImg(sx, sy, box_w, box_h, source_w, source_h) {
var sx = 0,
sy = 0,
sWidth = source_w,
sHeight = source_h;
if (source_w > source_h || (source_w == source_h && box_w < box_h)) {
sWidth = box_w * sHeight / box_h;
sx = (source_w - sWidth) / 2;
} else if (source_w < source_h || (source_w == source_h && box_w > box_h)) {
sHeight = box_h * sWidth / box_w;
sy = (source_h - sHeight) / 2;
}
return {
sx,
sy,
sWidth,
sHeight
}
}
export function drawPoster(obj, list) {
return new Promise(async (resolve, reject) => {
const oCanvas = document.createElement('canvas');
const context = oCanvas.getContext('2d');
oCanvas.width = obj.w;
oCanvas.height = obj.h;
for (var i in list) {
await addPoster(list[i])
}
async function addPoster(item) {
return new Promise((_res, _rej) => {
if (item.type == 2) {
QRCode.toDataURL(item.keyword).then((qrCodeSrc) => {
const qrCodeImg = new Image();
qrCodeImg.crossOrigin = 'anonymous';
qrCodeImg.src = qrCodeSrc;
qrCodeImg.onload = () => {
// 计算x、y值
const x = item.dx;
const y = item.dy;
console.log(111, item);
// 绘制
context.drawImage(qrCodeImg, x, y, item.width, item.height);
_res(2222);
};
}).catch((err) => {
_rej(err);
throw new Error('海报生成失败');
});
} else if (item.type == 3) {
const otherImg = new Image();
otherImg.crossOrigin = 'anonymous';
otherImg.src = item.keyword;
otherImg.onload = () => {
// 计算x、y值
const x = item.dx;
const y = item.dy;
if (item.origin) {
context.drawImage(otherImg, x, y, item.width, item.height)
_res(3333);
return false
}
var imgRect = coverImg(0, 0, item.width, item.height, otherImg.width, otherImg.height);
context.drawImage(otherImg, imgRect.sx, imgRect.sy, imgRect.sWidth, imgRect.sHeight, x, y, item.width, item.height)
_res(3333);
};
} else {
context.font = `400 ${item.fontSize}px PingFang SC`;
if (item.color) {
context.fillStyle = item.color;
// context.strokeStyle = '#fff';
}
context.fillText(item.keyword, item.dx, item.dy);
// context.strokeText(item.keyword, item.dx + 1, item.dy + 1);
context.stroke();
_res(11111);
}
});
}
resolve(oCanvas.toDataURL('image/png', 1));
});
}
实际调用的代码(由于展示,有些东西需要自己修改)
async canvasP1() {
// 第一个参数:绘制区域大小 , 第二个参数:需要绘制的内容
const r = await drawPoster({ w: 696, h: 1074 }, [
{
type: 3,
keyword: this.headImg,
dx: 60,
dy: 60,
width: 600,
height: 700,
},
{
type: 3,
keyword:
'https://daxueui-oss.koocdn.com/images/fe_upload/2021/8/2021-8-27-1630055577557.png',
dx: 0,
dy: 0,
width: 696,
height: 1074,
origin: true,
},
{
color: '#011232',
fontSize: 26,
keyword: this.label,
dx: 510,
dy: 778,
},
{
color: '#fff',
fontSize: 23,
keyword: '@' + this.name,
dx: 100 - this.nameX,
dy: 115,
},
{
color: '#005256',
fontSize: 22,
keyword: this.declarationObj.declaration1,
dx: 30,
dy: 865,
},
{
color: '#005256',
fontSize: 22,
keyword: this.declarationObj.declaration2,
dx: 30,
dy: 925,
},
{
color: '#005256',
fontSize: 22,
keyword: this.declarationObj.declaration3,
dx: 30,
dy: 985,
},
])
this.canvasSrc1 = r
},
...