关于html2canvas转pdf,html断开并且根据id自由断开

目录

 源码:

调用:

node版本:14.16.1 || 16.19.0包版本:


 源码:

import html2canvas from "html2canvas";
import "./fonts/NotoSansCJKjp-Regular-normal";
import JsPDF from "jspdf";
var _ = require("lodash");
const adhereHeight = 300; // 假如你希望有个元素距离底部${adhereHeight}换行
let print = ({
    id,
    name,
    needSave = true,
    fullCover = false,
    addPageIDArr = [],
    callback = () => {},
    drawPaddingHeight = 0,
    drawCallback = () => {},
    fileCallback = () => {}
}) => {
    if (!id) {
        throw new Error("element id is required");
    }
    // 计算需要截断的id元素
    const marks = [];
    addPageIDArr.map(item => {
        try {
            let e = document.getElementById(item);
            let style = getComputedStyle(e, null);
            let markHeight =
                e.offsetTop -
                item.offsetHeight -
                style.getPropertyValue("border-top-width").slice(0, -2) * 1 -
                style.getPropertyValue("padding-top").slice(0, -2) * 1;
            marks.push(markHeight);
        } catch (error) {}
    });
    // 计算需要截断的className 是addPage的元素
    let addPage = document.getElementsByClassName("addPage");
    for (const item of addPage) {
        let style = getComputedStyle(item, null);
        let markHeight =
            item.offsetTop -
            // style.offsetHeight -
            style.getPropertyValue("border-top-width").slice(0, -2) * 1 -
            style.getPropertyValue("padding-top").slice(0, -2) * 1 -
            style.getPropertyValue("margin-top").slice(0, -2) * 1;

        marks.push(markHeight);
    }
    // 去重,防止有地方算了两遍,截断两次,生成一个空白页
    const breakPositionArr = [...new Set(marks)];
    breakPositionArr.sort((a, b) => {
        return a - b;
    });

    const adhereArr = [];
    let adhereMax = 0;
    let adhere = document.getElementsByClassName("adhere");
    for (const item of adhere) {
        let adhereheight = Number(item.dataset.adhereheight) || 300;

        let style = getComputedStyle(item, null);
        let markHeight =
            item.offsetTop -
            // item.offsetHeight -
            style.getPropertyValue("border-top-width").slice(0, -2) * 1 -
            style.getPropertyValue("padding-top").slice(0, -2) * 1 -
            style.getPropertyValue("margin-top").slice(0, -2) * 1;
        let option = {
            mark: markHeight,
            adhereheight
        };
        adhereArr.push(option);
    }
    adhereArr.sort((a, b) => {
        return a.mark - b.mark;
    });

    const ele = document.getElementById(id);

    html2canvas(ele, {
        // dpi: 900,
        scale: 1,
        width: 900,
        useCORS: true, //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
        background: "#fff", //如果指定的div没有设置背景色会默认成黑色,这里是个坑
        allowTaint: true // 允许请求图片地址
    }).then(canvas => {
        let H = canvas.height;
        //未生成pdf的html页面高度
        var pdfWidth = canvas.width;
        var pdfHeight = canvas.height;
        var a4Width = 595.28;
        var a4Height = 841.89;
        let paddingHeight = drawPaddingHeight;
        let paddingWidth = (
            (paddingHeight / 841.89).toFixed(4) * 595.28
        ).toFixed(0);
        const radio = 0.8;
        //一页pdf显示html页面生成的canvas高度;

        //pdf页面偏移
        var position = 0;

        var pageData = canvas.toDataURL("image/png", 1.0);
        // canvas.toBlob(blob => {
        //     console.log(URL.createObjectURL(blob));
        // });
        var pdf = new JsPDF("x", "pt", "a4");
        // pdf.addFont(
        //     "./fonts/NotoSansCJKjp-Regular.ttf",
        //     "NotoSansCJKjp-Regular",
        //     "normal"
        // );
        pdf.setFont("NotoSansCJKjp-Regular");

        pdf.setDisplayMode("fullwidth", "continuous", "FullScreen");

        var pdfName = `${name}-${new Date().getTime()}`;
        var a4HeightRef = Math.floor((pdfWidth / a4Width) * a4Height);
        let num = 0;
        function createImpl(canvas) {
            let canvas1 = document.createElement("canvas"),
                height;
            if (fullCover && num == 0) {
                a4HeightRef = Math.floor((pdfWidth / a4Width) * a4Height);
            } else {
                a4HeightRef =
                    Math.floor((pdfWidth / a4Width) * a4Height) -
                    paddingHeight * 0;
            }

            let context = canvas.getContext("2d", { willReadFrequently: true });

            if (pdfHeight > 0) {
                // if (pdfHeight > a4HeightRef) { // 如果只剩一页,就直接打印,影响了id分页,所以注释
                if (true) {
                    let i, isAdhere, isAdhereFilterArr;
                    isAdhereFilterArr = adhereArr.filter(item => {
                        return (
                            item.mark > position &&
                            item.mark <= position + a4HeightRef &&
                            position + a4HeightRef - item.mark <=
                                Number(item.adhereheight)
                        );
                    });

                    isAdhere = isAdhereFilterArr.length > 0;
                    let isInSection =
                        breakPositionArr.filter(item => {
                            return (
                                item >= position &&
                                item <= position + a4HeightRef
                            );
                        }).length > 0;

                    let clip = false;
                    if (isAdhere) {
                        for (i = position; i <= position + a4HeightRef; i++) {
                            let here = adhereArr.filter(item => {
                                return item.mark == i;
                            });

                            if (here.length > 0 && here[0].mark == i) {
                                let line =
                                    (here[0] && here[0].adhereheight) ||
                                    adhereHeight;
                                if (position + a4HeightRef - i <= line) {
                                    adhereArr.splice(here, 1);
                                    clip = true;
                                    break;
                                }
                            }
                        }
                        i -= 1;
                    }
                    if (isInSection) {
                        for (i = position; i <= position + a4HeightRef; i++) {
                            if (breakPositionArr.includes(i)) {
                                breakPositionArr.splice(
                                    breakPositionArr.indexOf(i),
                                    1
                                );

                                clip = true;
                                break;
                            }
                        }
                        // i += 2;
                    }

                    if (!clip) {
                        for (i = position + a4HeightRef; i >= position; i--) {
                            let data = context.getImageData(0, i, pdfWidth, 1)
                                .data;
                            let chunkData = _.chunk(data, 4);
                            // 计算一行像素中出现最多的颜色和次数
                            let obj = {};
                            let max = 0;
                            let maxItem = "";
                            chunkData.map(item => {
                                let key = `${item[0]},${item[1]},${item[2]},${item[3]}`;
                                if (obj[key] !== undefined) {
                                    obj[key]++;
                                    if (obj[key] > max) {
                                        max = obj[key];
                                        maxItem = key;
                                    }
                                } else {
                                    obj[key] = 1;
                                }
                            });
                            // if (i >= H - 20 && i <= H) {
                            //     console.log(i, H);
                            //     console.log(max);
                            //     console.log(maxItem);
                            // }
                            /**
                             * @param {0:Number} param - R
                             * @param {1:Number} param - G
                             * @param {2:Number} param - B
                             * @param {3:Number} param - A
                             */
                            let maxItemArr = maxItem.split(",");
                            // 假如一行中出现次数最多的是白色,必须全行都是白色,假如不是白色,超过90%就截断
                            // 虽然统计出来了颜色,但是实际并没有用到,因为不需要对特殊颜色特殊处理

                            if (
                                maxItemArr[0] == 255 &&
                                maxItemArr[1] == 255 &&
                                maxItemArr[2] == 255
                            ) {
                                if (max >= chunkData.length - 8) {
                                    clip = true;
                                    break;
                                }
                            } else if (
                                maxItemArr[0] == 53 &&
                                maxItemArr[1] == 198 &&
                                maxItemArr[2] == 191
                            ) {
                                if (max >= chunkData.length * 0.92) {
                                    clip = true;
                                    break;
                                }
                            } else {
                                if (max == chunkData.length) {
                                    clip = true;
                                    break;
                                }
                            }
                        }
                        i += 1;
                    }
                    // console.log(i - position);
                    // i += 1;
                    // 计算这次截断的图片高度,下边要减去,计算还剩多高的图片没有处理
                    // height = Math.round(i - position);
                    height =
                        Math.round(i - position) ||
                        Math.min(pdfHeight, a4HeightRef);
                    if (height <= 0) {
                        height = a4HeightRef;
                    }
                } else {
                    height = pdfHeight;
                }

                canvas1.width = pdfWidth;
                canvas1.height = height;

                var ctx = canvas1.getContext("2d");
                // 填充白色,默认生成A4纸大小的canvas,然后把切开的图片放上去,如果没有放上去,会变成透明背景,透明色在pdf上显示为黑色
                ctx.fillStyle = "#fff";
                ctx.fillRect(0, 0, canvas1.width, canvas1.height);
                ctx.drawImage(
                    canvas,
                    0,
                    position,
                    pdfWidth,
                    height,
                    0,
                    0,
                    pdfWidth,
                    height
                );
                // canvas1.toBlob(file => {
                //     console.log(URL.createObjectURL(file));
                // });
                let same = true;
                for (let index = 0; index < canvas1.height; index++) {
                    let data = context.getImageData(0, index, canvas1.width, 1)
                        .data;

                    let result = data.find(item => {
                        return item !== 255;
                    });
                    if (result !== undefined) {
                        same = false;
                        break;
                    }
                }

                if (position != 0 && !same) {
                    pdf.addPage();
                }
                if (!same) {
                    num += 1;
                    drawImageToPdf({
                        canvas: canvas1,
                        a4Width,
                        a4Height,
                        paddingWidth,
                        paddingHeight,
                        fullCover,
                        pdf,
                        pageNum: num,
                        callback,
                        drawCallback
                    });
                }

                // pdf.setFontSize(20);
                // pdf.setTextColor(255, 0, 0);
                // pdf.text('end!', 10, (a4Width / canvas1.width) * height - 0);
                // console.log(pdfHeight, height);
                pdfHeight -= height;
                position += height;
                if (pdfHeight > 0) {
                    setTimeout(createImpl, 0, canvas);
                    // return createImpl(canvas);
                } else {
                    callback(-1);
                    const filename = `${pdfName}.pdf`;
                    const blob = pdf.output("blob", { filename });
                    const file = new File([blob], filename, {
                        type: blob.type
                    });
                    fileCallback(file);
                    if (needSave) {
                        pdf.save(filename);
                    }
                }
            }
        }

        //当内容未超过pdf一页显示的范围,无需分页
        if (pdfHeight < a4HeightRef) {
            drawImageToPdf({
                canvas,
                a4Width,
                a4Height,
                paddingWidth,
                paddingHeight,
                fullCover,
                pdf,
                pageNum: 1,
                callback,
                drawCallback
            });

            const filename = `${pdfName}.pdf`;
            const blob = pdf.output("blob", { filename });
            const file = new File([blob], filename, {
                type: blob.type
            });
            fileCallback(file);
            if (needSave) {
                pdf.save(pdfName + ".pdf");
            }
        } else {
            try {
                pdf.deletePage(0);
                setTimeout(createImpl, 0, canvas);
                // createImpl(canvas);
            } catch (err) {
                console.log(err);
            }
        }
    });
};

function drawImageToPdf({
    canvas,
    a4Width,
    a4Height,
    paddingWidth,
    paddingHeight,
    fullCover,
    pdf,
    pageNum,
    callback,
    drawCallback
}) {
    let R =
        1 -
        (
            (paddingWidth * 2) /
            ((a4Width / canvas.width) * canvas.width)
        ).toFixed(4);
    let x1 = 0 + paddingWidth,
        y1 = 0 + paddingHeight,
        // x2 = a4Width - paddingWidth * 2,
        x2 = (a4Width / canvas.width) * canvas.width - paddingWidth * 2,
        y2 = (a4Width / canvas.width) * canvas.height * R;
    // y2 = (a4Width / canvas1.width) * height;
    if (fullCover && pageNum == 1) {
        x1 = 0;
        y1 = 0;
        x2 = a4Width;
        y2 = (a4Width / canvas.width) * canvas.height;
    }
    pdf.addImage(canvas.toDataURL("image/jpeg", 1.0), "JPEG", x1, y1, x2, y2);
    callback(-1);
    drawCallback({
        page: pageNum,
        a4Width: 595.28,
        a4Height: 841.89,
        imgWidth: x2,
        imgHeight: y2,
        pdf: pdf,
        paddingHeight: paddingHeight,
        paddingWidth: (a4Width - x2) / 2
    });
}
export default print;

调用:

可以传入id或者在需要的地方添加'addPage'class类名

<template>
	<div @click="P">打印</div>
	<div id="box">
		<div
			v-for="(item, index) in 27"
			:class="[
				'item',
				`item${index + 1}`,
				(index + 1) % 5 == 0 ? `addPage` : '',
			]"
			:id="`item${index + 1}`"
		>
			{{ item }}
		</div>
		<div v-for="(item, index) in 25" :class="['item']">
			{{ item }}
		</div>
	</div>
</template>

假如需要在低于xx像素使一个换页,则加入 adhere 类名,默认高度为300(可以在js第5行修改),假如需要自定义高度,则在当前元素上添加 data-adhereheight 的自定义属性。

<div
    class="result adhere"
    data-adhereheight="100"
>
    123
</div>

let P = () => {
	/**
	 * @param {string} 'box' - 打印的盒子id
	 * @param {string} '1' - 保存的文件名(1.pdf)
	 * @param {string[]} param - 假如中间有表格需要另起一页,传入他的id,他将在下一页打印
	 * @param {function:void} param - 当前打印第几页,假如单页打印,会直接返回-1,表示图片处理完毕,开始生成pdf文件
	 */
	_print({
                id: "healthPalnPDF",
                name: "健康管理方案",
                fullCover: true,
                drawPaddingHeight:0, // 需要在pdf上下每边留出多大空白
                addPageIDArr: [], // 需要分页的id类名
                callback: num => {
                    console.log(num);
                    // console.log("正在打印" + num + "页");
                    if (num !== -1) {
                        that.loadingText = `正在生成第${num}页`;
                        loading.setText(`正在生成第${num}页`);
                    } else {
                        that.loadingText = `正在生成PDF`;
                        // setTimeout(() => {
                        that.loading = false;
                        // }, 500);
                        loading.close();
                    }
                },
                drawCallback: ({
                    page,
                    a4Width,
                    a4Height,
                    imgWidth,
                    imgHeight,
                    pdf
                }) => {
                    pdf.setFontSize(14);
                    pdf.setTextColor(0, 0, 0);
                    let text = `第 ${page} 页`;
                    pdf.text(text, a4Width - 50, a4Height - 20);
                },
                needSave: v ? true : false,
                fileCallback: file => {
                    // console.log(file);
                }
            });
};

node版本:14.16.1 || 16.19.0
包版本:

"html2canvas": "^1.4.1",
"jspdf": "^2.5.1",

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值