Open amandakelake opened 5 years ago
用户自行选择背景图、头像,手动输入昵称、文案 根据以上元素合成图片,上传到后端,拿到图片在线链接并分享出去 保存图片到本地
toDataURL
meta
1、移动端暂不支持webp格式图片,仅仅后缀名不代表真正的格式,可以通过网络请求看header里面的Content-Type 2、用户选择图片的时候,加载过的url,后面canvas绘制同一张图片的url时,因为缓存,不会重新load,会导致绘制失败,解决办法是绘制时给图片链接加时间戳,破坏缓存 3、分享链接出去给用户时,制作短链,短链本身为一个中间页,利用php动态添加meta头,配置og:image og:title og:description等属性,分享可以动态生成预览信息 分享到FB twitter 4、H5本地下载图片
Content-Type
og:image
og:title
og:description
this.dpr = window.devicePixelRatio ? window.devicePixelRatio : 2;
// 这里的计算方法根据具体项目的px/rem/em等进行转换 calcuSize(px) { // 计算的时候 需要算上dpr 屏幕分辨率 if (!this.winWidth) { const winWidth = window.innerWidth; this.winWidth = winWidth; return ((winWidth * px) / 750) * this.dpr; } else { return ((this.winWidth * px) / 750) * this.dpr; } },
drawImage(ctx, url, width, height, posX, posY) { return new Promise((resolve, reject) => { const image = new Image(); // 这行是canvas绘制图片的跨域关键 image.setAttribute("crossOrigin", "anonymous"); image.onload = () => { ctx.drawImage( image, this.calcuSize(posX), this.calcuSize(posY), this.calcuSize(width), this.calcuSize(height) ); // 貌似ctx.drawImage也是异步的,暂时找不到回调,暂等一个循环 setTimeout(() => { resolve(true); }, 0); }; image.onerror = () => { reject("image load fail"); }; // 加时间戳 破坏缓存 image.src = `${url}?time=${new Date().getTime()}`; }); },
设置文案大小,也是根据各自的css单位换算规则+dpr
const htmlFS = document.querySelector("html").style.fontSize; const htmlFSPx = htmlFS.split("px")[0]; ctx.font = `${0.22 * this.dpr * htmlFSPx}px serif`;
drawName(ctx) { const htmlFS = document.querySelector("html").style.fontSize; const htmlFSPx = htmlFS.split("px")[0]; ctx.font = `${0.22 * this.dpr * htmlFSPx}px serif`; return new Promise(resolve => { if (this.username) { // 画背景,用很粗的线条 const nameWidth = ctx.measureText(this.username).width; const x = (this.calcuSize(696) - nameWidth) / 2; const y = this.calcuSize(576); ctx.fillStyle = "rgba(73,107,193,0.8)"; ctx.strokeStyle = "rgba(73,107,193,0.8)"; // 线条颜色 ctx.lineCap = "round"; // 线条圆角端点 ctx.lineWidth = this.calcuSize(37); ctx.beginPath(); ctx.moveTo(x, y + this.calcuSize(20)); ctx.lineTo(x + nameWidth + this.calcuSize(0), y + this.calcuSize(20)); ctx.stroke(); // 绘制文案 // 这里的居中,是指根据当前x位置两边分布 ctx.textAlign = "center"; ctx.fillStyle = "#fff"; ctx.fillText( this.username, this.calcuSize(348), // 这里可以直接用canvas宽度的一半 this.calcuSize(601), this.calcuSize(600) ); setTimeout(() => { resolve(true); }, 0); } else { resolve(true); } }); },
利用ctx.measureText(str)方法计算文案宽度,可以实现换行 但要预先设置好文案的lineHeight,这样才知道下一行文字的y轴位置
ctx.measureText(str)
lineHeight
canvasTextAutoLine(str, ctx, initX, initY, lineHeight, totalWidth) { let lineWidth = 0; let canvasWidth = totalWidth; let lastSubStrIndex = 0; for (let i = 0; i < str.length; i++) { lineWidth += ctx.measureText(str[i]).width; if (lineWidth > canvasWidth) { //减去initX,防止边界出现的问题 ctx.fillText(str.substring(lastSubStrIndex, i), initX, initY); initY += lineHeight; lineWidth = 0; lastSubStrIndex = i; } if (i == str.length - 1) { ctx.fillText(str.substring(lastSubStrIndex, i + 1), initX, initY); } } },
const base64Image = canvasEle.toDataURL("image/png", 1);
// 利用async/await 写出比较好看的异步流程代码 async dynamicCreateImage() { try { // 先动态创建canvas let canvasEle = document.createElement("canvas"); let ctx = canvasEle.getContext("2d"); canvasEle.width = this.calcuSize(696); canvasEle.height = this.calcuSize(750); // 绘制图片 await this.drawBgImg(ctx); // 画用户名字、名字背景 await this.drawName(ctx); // 用户头像 await this.drawHeadImg(ctx); // canvas转base64上传图片+分享 // 上传图片和分享功能,各自实现就好 const base64Image = canvasEle.toDataURL("image/png", 1); const imageOnlineUrl = await this.uploadImage(base64Image); this.shareImage(imageOnlineUrl); canvasEle = null; ctx = null; } catch (error) { // ... 错误处理 } },
javascript - 如何通过js实现canvas保存图片为png格式并下载到本地! - SegmentFault 思否 在浏览器端用JS创建和下载文件 | AlloyTeam javascript - Capture HTML Canvas as gif/jpg/png/pdf? - Stack Overflow canvas 微信海报分享(个人爬坑) - 掘金 这个很多踩坑经验 canvas文本绘制自动换行、字间距、竖排等实现 « 张鑫旭-鑫空间-鑫生活
good!!!
一、业务场景
用户自行选择背景图、头像,手动输入昵称、文案 根据以上元素合成图片,上传到后端,拿到图片在线链接并分享出去 保存图片到本地
二、功能以及难点分解
toDataURL
转成base64格式meta
头配置三、踩坑
1、移动端暂不支持webp格式图片,仅仅后缀名不代表真正的格式,可以通过网络请求看header里面的
Content-Type
2、用户选择图片的时候,加载过的url,后面canvas绘制同一张图片的url时,因为缓存,不会重新load,会导致绘制失败,解决办法是绘制时给图片链接加时间戳,破坏缓存 3、分享链接出去给用户时,制作短链,短链本身为一个中间页,利用php动态添加meta
头,配置og:image
og:title
og:description
等属性,分享可以动态生成预览信息 分享到FB twitter 4、H5本地下载图片四、难点伪代码
1、计算绘制canvas时的具体位置
2、绘制一张图片
3、绘制文案
设置文案大小,也是根据各自的css单位换算规则+dpr
4、文案换行
利用
ctx.measureText(str)
方法计算文案宽度,可以实现换行 但要预先设置好文案的lineHeight
,这样才知道下一行文字的y轴位置5、canvas转base64图片
6、汇总:异步合成图片,并上传
参考
javascript - 如何通过js实现canvas保存图片为png格式并下载到本地! - SegmentFault 思否 在浏览器端用JS创建和下载文件 | AlloyTeam javascript - Capture HTML Canvas as gif/jpg/png/pdf? - Stack Overflow canvas 微信海报分享(个人爬坑) - 掘金 这个很多踩坑经验 canvas文本绘制自动换行、字间距、竖排等实现 « 张鑫旭-鑫空间-鑫生活