We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
用户自行选择背景图、头像,手动输入昵称、文案 根据以上元素合成图片,上传到后端,拿到图片在线链接并分享出去 保存图片到本地
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文本绘制自动换行、字间距、竖排等实现 « 张鑫旭-鑫空间-鑫生活
The text was updated successfully, but these errors were encountered:
good!!!
Sorry, something went wrong.
No branches or pull requests
一、业务场景
用户自行选择背景图、头像,手动输入昵称、文案
根据以上元素合成图片,上传到后端,拿到图片在线链接并分享出去
保存图片到本地
二、功能以及难点分解
toDataURL
转成base64格式meta
头配置三、踩坑
1、移动端暂不支持webp格式图片,仅仅后缀名不代表真正的格式,可以通过网络请求看header里面的
Content-Type
2、用户选择图片的时候,加载过的url,后面canvas绘制同一张图片的url时,因为缓存,不会重新load,会导致绘制失败,解决办法是绘制时给图片链接加时间戳,破坏缓存
3、分享链接出去给用户时,制作短链,短链本身为一个中间页,利用php动态添加
meta
头,配置og:image
og:title
og:description
等属性,分享可以动态生成预览信息 分享到FB twitter4、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文本绘制自动换行、字间距、竖排等实现 « 张鑫旭-鑫空间-鑫生活
The text was updated successfully, but these errors were encountered: