H5 Canvas实现荣誉证书生成器

时间:2025-01-25 19:53:28
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> .preview { position: relative; display: inline-block; } img { width: 768px; } .name, .detail, .date, .orgs, .colon { position: absolute; font-family: "Microsoft YaHei" } .name { top: 183px; left: 102px; width: 255px; font-size: 27px; line-height: 36px; background-color: transparent; border-bottom-width: 2px; border-bottom-color: #999; border-top-width: 0; border-left-width: 0; border-right-width: 0; text-align: center; outline: none; } .colon { top: 190px; left: calc(107px + 255px); font-size: 27px; font-family: Arial, Helvetica, sans-serif; font-weight: bold; } .detail { top: 237px; left: 94px; height: 90px; width: 559px; background-color: transparent; resize: none; border: none; font-size: 22px; letter-spacing: 4px; outline: none; text-indent: 50px; } .date { top: 432px; right: 130px; font-size: 14px; line-height: 36px; background-color: transparent; border: none; outline: none; } .orgs { top: 356px; right: 147px; width: 180px; background-color: transparent; resize: none; border: none; font-size: 14px; outline: none; text-align: justify; height: 80px; } </style> </head> <body> <div class="preview"> <img src="./assets/"> <input class="name" type="text" placeholder="获奖人" /> <span class="colon">:</span> <textarea class="detail" placeholder="获奖成就"></textarea> <textarea class="orgs" placeholder="颁发机构"></textarea> <input class="date" type="text" placeholder="获奖日期" /> </div> <canvas id="seal" width="200" height="200" style="opacity: 0;"></canvas> <canvas id="preview" width="768" height="531"></canvas> <script> let certificate = document.getElementsByTagName('img').item(0); /** * 印章的生成 */ function seal(s) { // 防止转圈圈 if (!s) return; let c = document.getElementById("seal"); let ctx = c.getContext("2d"); ctx.clearRect(0, 0, c.clientWidth, c.clientHeight); ctx.fillStyle = "rgb(196,38,29)"; ctx.strokeStyle = "rgb(196,38,29)"; // 印章的外框 ctx.beginPath(); ctx.lineWidth = 4; ctx.arc(100, 100, 96, 0, 2 * Math.PI); ctx.stroke(); // 星星的生成 ctx.beginPath(); ctx.lineWidth = 1; let star_start_x = 76; let star_start_y = 88; ctx.moveTo(star_start_x, star_start_y) ctx.lineTo(star_start_x + 20, star_start_y) ctx.lineTo(star_start_x + 20 + 7, star_start_y - 20) ctx.lineTo(star_start_x + 20 + 7 + 6, star_start_y) ctx.lineTo(star_start_x + 20 + 7 + 6 + 20, star_start_y) ctx.lineTo(star_start_x + 20 + 7 + 6 + 20 - 16, star_start_y + 13) ctx.lineTo(star_start_x + 20 + 7 + 6 + 20 - 16, star_start_y + 13) ctx.lineTo(star_start_x + 20 + 7 + 6 + 20 - 16 + 5, star_start_y + 13 + 20) ctx.lineTo(star_start_x + 20 + 7 + 6 + 20 - 16 + 5 - 16, star_start_y + 20) ctx.lineTo(star_start_x + 20 + 7 + 6 + 20 - 16 + 5 - 16 - 18, star_start_y + 13 + 20) ctx.lineTo(star_start_x + 20 + 7 + 6 + 20 - 16 + 5 - 16 - 18 + 6, star_start_y + 13) ctx.closePath() ctx.fill(); // 印章上旋转的文字 ctx.translate(100, 100); ctx.font = '26px Helvetica'; let angle = 4 * Math.PI / (3 * (s.length - 1)); for (var i = 0; i < s.length; i++) { let c = s[i]; if (i == 0) ctx.rotate(.8 * Math.PI); else ctx.rotate(angle); ctx.save(); ctx.translate(70, 0); ctx.rotate(Math.PI / 2); ctx.fillText(c, 0, 5); ctx.restore(); } // 归位 ctx.rotate(-angle * (s.length - 1)); ctx.rotate(-.8 * Math.PI); ctx.translate(-100, -100); return c.toDataURL() } // 预览 function preview(name, detail, orgs, date) { let c = document.getElementById("preview"); let ctx = c.getContext("2d"); console.log(certificate); // 证书背景 ctx.drawImage(certificate, 0, 0, c.clientWidth, c.clientHeight); console.log(""); // 获奖人 ctx.font = "22px Microsoft YaHei"; ctx.textAlign = "center"; ctx.textBaseline = "top"; ctx.fillText(name, 102 + 255 / 2, 183); // 冒号 ctx.textAlign = "right"; ctx.fillText(":", 102 + 255, 183); // 获奖人底线 ctx.beginPath(); ctx.moveTo(102, 220) ctx.lineTo(102 + 245, 220) ctx.stroke(); // 获奖成就 let detailX = 147; let detailY = 237; ctx.font = "22px Microsoft YaHei"; ctx.textAlign = "left"; ctx.textBaseline = "top"; for (let i = 0; i < detail.length; i++) { detailX += 32; if (detailX > 640) { detailX = 115; detailY += 32; } ctx.fillText(detail[i], detailX, detailY); if (detail.charCodeAt(i) <= 255) detailX -= 16 } // 颁发机构 let orgX = c.clientWidth - 147 - 180; let orgY = 356 ctx.font = "14px Microsoft YaHei"; ctx.textAlign = "left"; ctx.textBaseline = "top"; let orgArr = orgs.split(/\n/).map(o => o.trim()); for (let i = 0; i < orgArr.length; i++) { ctx.fillText(orgArr[i], orgX, orgY); orgY += 30; let img = new Image(); img.src = seal(orgArr[i]); img.onload = function () { ctx.drawImage(img, c.clientWidth - 220 - i * 110, 350, 100, 100); } } // 绘制获奖日期 ctx.font = "14px Microsoft YaHei"; ctx.textAlign = "left"; ctx.textBaseline = "top"; ctx.fillText(date, c.clientWidth - 130 - 153, 450); } let timeoutId = null; let nameDom = document.querySelector('.name'); let detailDom = document.querySelector('.detail'); let orgsDom = document.querySelector('.orgs'); let dateDom = document.querySelector('.date'); nameDom.addEventListener('input', checkPreview) detailDom.addEventListener('input', checkPreview) orgsDom.addEventListener('input', checkPreview) dateDom.addEventListener('input', checkPreview) // 检查是否需要渲染 function checkPreview() { let name = nameDom.value; let detail = detailDom.value; let orgs = orgsDom.value; let date = dateDom.value; clearTimeout(timeoutId); // 允许渲染 if (name.trim() && detail.trim() && orgs.trim() && date.trim()) { timeoutId = setTimeout(() => { console.log("渲染"); console.log(name, detail, orgs, date); preview(name, detail, orgs, date) }, 1000); } } // 示例 function sample() { nameDom.value = `林一怂儿`; detailDom.textContent = "在2021年全国优秀文章中,你以优秀的错别字数量,以及非常离谱的Bug,遥遥领先。特发此证,以资鼓励。"; orgsDom.textContent = `全国文章错别字核查中心\n全国离谱到家委员会` dateDom.value = "2021年12月" checkPreview() } let previewDom = document.getElementById("preview"); // 下载 function download() { let a = document.createElement('a'); a.href = previewDom.toDataURL(); a.download = "荣誉证书.png"; a.click(); } previewDom.addEventListener('click', this.download) certificate.onload = function () { sample(); } </script> </body> </html>