js生成书法字帖
偶然的机会看到一个生成书法字帖的工具网站,一时之间不知道田字格是怎么生成的,写个demo记录下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script src="./plugins/jquery/jquery-1.9.1.js"></script>
<script src="./plugins/html2canvas.min.js"></script>
<script src="./plugins/jspdf/dist/jspdf.umd.min.js"></script>
<script src="./plugins/hanzi-writer.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
* {
margin: 0;
padding: 0;
}
body {
font-family: "msyh";
letter-spacing: 0.01px; /*解决jspdf的英文字母间隔问题 */
}
table {
font-size: 2.5rem;
border-spacing: 0.5rem;
border: 1px solid black;
}
table.tb_cn tr td {
width: 3.45rem;
height: 3.45rem;
line-height: 3.45rem;
text-align: center;
color: rgb(153, 153, 153);
border: 1px solid black;
}
table.tb_cn tr td :nth-child(odd) {
height: 2rem;
line-height: 2rem;
}
table tr td:first-child {
color: black;
}
table tr td.with-line {
position: relative;
}
table tr td.with-line::before {
content: "";
position: absolute;
height: 1px;
width: 100%;
left: 0;
top: 50%;
transform: translateY(-50%);
border-top: 1px dashed silver;
z-index: -1;
}
table tr td.with-line::after {
content: "";
position: absolute;
height: 100%;
width: 1px;
left: 50%;
top: 0;
transform: translateX(-50%);
border-left: 1px dashed silver;
z-index: -1;
}
table.tb_img tr td {
width: 6 * 3.45rem;
height: 4 * 3.45rem;
line-height: 6 * 3.45rem;
text-align: center;
border: 1px solid black;
padding: 0.25rem;
}
#container {
display: flex;
}
#content-con {
width: 210mm;
}
#content-con .content {
height: 300.5mm;
}
</style>
</head>
<body>
<div id="container">
<div id="content-con"></div>
<div id="toolbar">
<textarea id="text" type="textarea" onchange="changeContent()" rows="5">你好世界你好世界你好世界你好世界你好世界</textarea>
<br />
<button onclick="exportPdf('content-con')">export Pdf</button>
</div>
</div>
</body>
<script>
const gbl_param = {
page_num: 3,
cn_col_num: 12,
cn_row_num: 8,
char_size: 36,
img_bg_id: 0,
img_row_num: 2,
img_col_num: 3,
};
function exportPdf(domId) {
html2canvas(document.getElementById(domId)).then((canvas) => {
const contentWidth = canvas.width;
const contentHeight = canvas.height;
console.log("content", contentWidth, contentHeight);
const s_w = 595.28;
const s_h = 841.89;
const sd_w = 7;
const pageHeight = (contentWidth / s_w) * s_h; //每页pdf的canvas高度;
console.log("page", pageHeight);
let leftHeight = contentHeight; //未生成pdf的canvas高度
let position = 10; //pdf页面偏移
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
const imgWidth = s_w - sd_w;
// const imgWidth = s_w;
const imgHeight = (imgWidth / contentWidth) * contentHeight; //canvas转为图片后适配A4纸宽度的高度
console.log("img", imgWidth, imgHeight);
const pageData = canvas.toDataURL("image/jpeg", 1.0);
const pdf = new jspdf.jsPDF("p", "pt", "a4");
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
pdf.addImage(pageData, "JPEG", sd_w, position, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, "JPEG", sd_w, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= s_h;
console.log("leftHeight", leftHeight);
// console.log("position", position);
//避免添加空白页
if (leftHeight > 0) {
pdf.addPage();
}
}
let targetPage = pdf.internal.getNumberOfPages();
pdf.deletePage(targetPage); // 删除最后一页
}
// pdf.save("xxxx.pdf");
window.open(pdf.output("bloburl")); //预览pdf
});
}
function renderFanningStrokes(target, strokes) {
const char_size = gbl_param.char_size;
// createElementNS() 方法可创建带有指定命名空间的元素节点
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.style.width = char_size + "px";
svg.style.height = char_size + "px";
svg.style.border = "1px solid #EEE";
svg.style.marginRight = "3px";
target.appendChild(svg);
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
const transformData = HanziWriter.getScalingTransform(char_size, char_size);
group.setAttributeNS(null, "transform", transformData.transform);
svg.appendChild(group);
strokes.forEach(function (strokePath) {
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttributeNS(null, "d", strokePath);
path.style.fill = "#555";
group.appendChild(path);
});
}
function getCharOrder(domId, char_cn) {
HanziWriter.loadCharacterData(char_cn).then(function (charData) {
const target = document.getElementById(domId);
target.innerHTML = "";
for (let i = 0; i < charData.strokes.length; i++) {
const strokesPortion = charData.strokes.slice(0, i + 1);
renderFanningStrokes(target, strokesPortion);
}
});
}
function changeContent() {
let str = $("#text").val();
if (str.length < gbl_param.page_num * Math.floor(gbl_param.cn_row_num / 2) || containsNonChineseChar(str)) return;
const cn_char_num = Math.floor(gbl_param.cn_row_num / 2);
let cnt = 0;
for (let i = 0; i < gbl_param.page_num; i++) {
for (let j = 0; j < cn_char_num; j++) {
getCharOrder("charOrder" + cnt, str[cnt]);
$(".content .tb_cn")
.eq(i)
.find("tr")
.eq(2 * j + 1)
.find("td")
.text(str[cnt]);
cnt++;
}
}
}
initPage();
changeContent();
function initPage() {
const cn_col_num = gbl_param.cn_col_num;
const cn_row_num = gbl_param.cn_row_num;
$("#content-con").html("");
let cnt = -1;
for (let i = 0; i < gbl_param.page_num; i++) {
$("#content-con").append(
"<div class='content'><table class='tb_cn'>" +
Array(cn_row_num)
.fill(null)
.map((val, idx) => {
if (idx % 2 == 0) {
cnt++;
return `<tr><td id="charOrder${cnt}" colspan="${cn_col_num}"></td></tr>`;
} else {
return `<tr>${Array(cn_col_num)
.fill(null)
.map((val, idx) => {
return `<td class="with-line"></td>`;
})
.join("")}
</tr>`;
}
})
.join("") +
"</table><table class='tb_img'>" +
Array(gbl_param.img_row_num)
.fill(null)
.map((val, idx) => {
gbl_param.img_bg_id += 1;
const url = "./images/test_svg/" + "0".repeat(5 - gbl_param.img_bg_id.toString().length) + gbl_param.img_bg_id + ".svg";
return `<tr>${Array(gbl_param.img_col_num)
.fill(null)
.map((val, idx) => {
return `<td><img src="${url}" width="240" height="240" ></td>`;
})
.join("")}</tr>`;
})
.join("") +
"</table></div>"
);
}
}
function containsNonChineseChar(str) {
return /[^\u4e00-\u9fa5]/.test(str);
}
</script>
</html>
抓svg图的python
import requests
import time
# 发送HTTP请求获取网页内容
response = requests.get(url_pre)
response.raise_for_status() # 检查请求是否成功
path = "../files/images/svg/"
i = 1440
while i <= 1440:
url = url_pre + "svg/0" + str(i).zfill(4) + ".svg"
print(url)
img_response = requests.get(url)
with open(path + url.split("/")[-1], "wb") as f:
f.write(img_response.content)
if i % 36 == 0:
# break
time.sleep(2)
i += 1
else:
print("done")
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix