IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Web前端实现自定义页面尺寸打印

    Veen Zhao发表于 2023-09-20 10:46:00
    love 0

    项目组最近有一个实现自定义页面尺寸打印的需求:

    打印纸张不同尺寸.png

    浏览了网上大部分的教程,基本总结如下:

    方案一:CSS

    使用 CSS 的 @page 规则:通过 @page 规则,可以定义打印页面的尺寸和边距。例如,可以使用 @page 规则来定义 A4 纸张的尺寸和边距,或者根据需要定义其他自定义尺寸的页面。

    @page {
      size: 210mm 297mm; /* A4 尺寸 */
      /* 或者自定义页面尺寸 */
    }

    方案二:Javascript

    使用 JavaScript 来控制页面的打印参数,包括页面尺寸、边距等。可以通过 JavaScript 改变打印样式表中的参数,实现自定义页面尺寸的打印。

    function setPrintStyles() {
      const iframe = document.createElement('iframe');
      //插入需要打印的目标元素
      document.querySelector(‘body‘).appendChild(iframe)
      //插入style
      …………
    }

    方案三:利用浏览器自带打印

    使用打印预览对话框进行手动设置:在打印预览对话框中,用户可以手动选择页面尺寸、边距等打印参数,从而实现自定义页面尺寸的打印。
    打印参数.png

    方案比较

    • 针对方案一,弊端在于部分低版本浏览器兼容性较差,不能很好的实现自定义打印效果。
    • 针对方案二,通过js来指定打印参数,麻烦在于需要将整个页面进行复制后(包括样式的复制),再进行打印,兼容性较好,但是代码多。
    • 针对方案三,不推荐,不同浏览器、不同操作系统会有不同的打印方案,难以控制保持一致性。

    最终解决方案:封装方案二

    最终决定封装一个javascript文件来实现自定义页面尺寸打印。核心部分包括:

    1. 创建一个iframe标签,来容纳页面内容。
    2. 复制页面中的原有style样式。包括head中的link stylesheet、style标签中的样式以及外部样式文件。
    3. 打印完成后移除iframe标签。

    完整代码实现:

    const defaultOptions = {
        el: '',              //打印目标dom节点
        debug: true,        //打开调试模式,会显示iframe,
        importCss: true,     //引入head 中的link stylesheet
        importStyle: true,   //引入style标签中的样式
        loadCss: [],         //需要载入的第三方样式表
        title: '',          //打印标题
        delay: 300,         //延迟打印时间,确保iframe中的静态资源加载完成
        beforePrintHandle: null,  //打开打印窗口前的钩子函数,可以针对打印文档进行自定义调整,接受一个document参数
        afterPrintHandle: null,   //打印完成的钩子函数
    }
    
    let iframe = null
    let dom = null
    
    const checkOptions = options => {
        if(!options.el) {
            throw new Error('el must be a nodeType')
        }
        return {
            ...defaultOptions,
            ...options
        }
    
    }
    const printf = options => {
        const op = checkOptions(options)
        dom = op.el.cloneNode(true)
        const handle = createIframe(op)
        if(op.beforePrintHandle) {
            op.beforePrintHandle(handle.contentDocument);
        }
        if (op.afterPrintHandle) {
            op.afterPrintHandle();
        }
        setTimeout(() => {
            handle.print();
            if (op.debug === false) {
                removeIframe();
            }
        }, op.delay)
    }
    
    const createIframe = (op) => {
        const { debug, importCss, importStyle, loadCss, title} = op
        removeIframe();
        iframe = document.createElement('iframe');
        if(debug === false) {
            iframe.style.display = 'none'
        }
        //插入需要打印的目标元素
        document.querySelector('body').appendChild(iframe)
        iframe.contentDocument.title = title;
        const { body, head } = iframe.contentDocument;
        const contentWindow = iframe.contentWindow;
        //插入head中的link stylesheet
        if(importCss) {
            const stylesheets = document.querySelectorAll("link[rel = 'stylesheet']")
            stylesheets.forEach(item => {
                head.appendChild(item.cloneNode(true))
            })
        }
        //插入style
        if (importStyle) {
            const stylesheets = document.querySelectorAll("style")
            stylesheets.forEach(item => {
                body.appendChild(item.cloneNode(true))
            })
        }
        //插入外部样式文件
        if (Array.isArray(loadCss) && loadCss.length > 0) {
            loadCss.forEach(item => {
                head.appendChild(item)
            })
        }
        body.appendChild(dom)
        return contentWindow;
    }
    const removeIframe = () => {
        if (iframe) {
            document.querySelector('body').removeChild(iframe)
            iframe = null
        }
    }
    
    export default printf

    在调用的时候这样使用即可:

    // 打印事件处理
    const printHandle = () => {
      const style = document.createElement('style');
      style.innerHTML = `@media print { @page {size:${宽}mm ${高}mm!important; margin: 0;padding: 0;} }`;
      window.document.head.appendChild(style);
      printf({
        el: printRef.value,      //打印目标dom节点
        debug: false,            //打开调试模式,会显示iframe,
        importCss: true,         //引入head 中的link stylesheet
        importStyle: true,       //引入style标签中的样式
        delay: 300,              //延迟打印时间,确保iframe中的静态资源加载完成
      });
    }


沪ICP备19023445号-2号
友情链接