关于javascript:手写JS打印导出pdf插件

6次阅读

共计 5005 个字符,预计需要花费 13 分钟才能阅读完成。

起因

Vue我的项目中,有一个打印(导出 pdf)需要,于是网上搜查了几个插件包含vue 插件js 插件 ,要么太重不够轻量级,要么性能不够欠缺,最终没有一款插件符合要求。
通过搜查插件和材料的过程,发现打印原理并不难,于是尝试本人写一个插件,成果十分棒,而且十分轻量级。

效果图

上面是理论我的项目中的局部打印截图:

应用

应用过程中,只须要传入要打印的 dom 元素即可,vue中应用 ref,须要留神的是要打印的元素css 款式不依赖于其余元素,必须是能够独立的展现的,比方,上面实例中 resume-container 元素不能依赖 box,否则会造成打印时款式局部失落的状况。
示例如下:

<template>
  <div class="box">
    <div class="resume-container" ref="resume">
      
    </div>
  </div>
</template>

<script>
// 引入打印插件
import printHtml from "@/utils/print.js"

export default {
  methods: {
    // 创立打印办法
    print() {
      // 传入要打印的 dom
      printHtml(this.$refs.resume, () => {location.reload();
      })
    },
  },
}
</script>

<style scoped rel="stylesheet/scss" lang="scss">
/* 设置打印格局 */
@page {
  margin: 0; /* this affects the margin in the printer settings */
  size: A4;
}
/* 设置打印款式 */
@media print {
  .resume-container {font-size: 1.473vh !important;}
}
.box {width: 100vw;}
/* 要打印的 dom 元素为最外层元素,款式不依赖于其余元素 */
.resume-container {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  font-size: 1.35vh;
  font-weight: 300;
  letter-spacing: 0.1vw;
  -webkit-print-color-adjust:exact;
  -moz-print-color-adjust:exact;
  -ms-print-color-adjust:exact;}
</style>

源代码

const VueHtmlToPaper = function(dom, cb) {if (!(this instanceof VueHtmlToPaper)) return new VueHtmlToPaper(dom, cb);
  if (!cb) cb = function() { return true;};
  if ((typeof dom) === "string") {this.dom = document.querySelector(dom);
  } else {this.isDOM(dom)
    this.dom = this.isDOM(dom) ? dom : dom.$el;
  }

  const styles = this.getStyle();
  const content = this.getHtml();
  console.log(styles, content)
  const defaultName = '_blank',
      defaultSpecs = ['fullscreen=yes','titlebar=yes', 'scrollbars=yes'],
      defaultReplace = true;
  const url = '';
  const win = window.open(url, defaultName, defaultSpecs, defaultReplace);

  win.document.write(`
    <html>
      <head>
        <title>${document.title}</title>
        ${styles}
      </head>
      <body>
        ${content}
      </body>
    </html>
  `);

  setTimeout(() => {win.document.close();
    win.focus();
    win.print();
    win.close();
    cb();}, 1000);

  return true;
}

VueHtmlToPaper.prototype = {
  calenderFlag: false,
  getStyle: function () {
    var str = "",
      styles = document.querySelectorAll('style, link');
    if (document.getElementById('calender-task-flag')) {this.calenderFlag = true;}
    if (this.calenderFlag) {
      const c_height = window.innerHeight;
      const c_width = window.innerWidth;
      const boxes = document.getElementsByClassName('resize-box');
      for (let i = 0; i < boxes.length; i++) {const box = boxes[i];
        const width = this.getAttr(box, 'width');
        const height = this.getAttr(box, 'height');
        const top = this.getAttr(box, 'top');
        const left = this.getAttr(box, 'left');
        //const widthScale = 1;
        const widthScale = 0.8;

        //parent.style.width = this.pxStrToNumber(width) / widthScale / c_width * 100 + '%';
        box.style.width = this.pxStrToNumber(width) / widthScale / c_width * 100 + '%';
        //parent.style.height = this.pxStrToNumber(height) / c_height * 100 + '%';
        box.style.height = this.pxStrToNumber(height) / c_height * 100 + 'vh';

        box.style.left = this.pxStrToNumber(left) / widthScale / c_width * 100+ '%';
        box.style.top = this.pxStrToNumber(top) / c_height * 100 + 'vh';
        box.style.border = '0.5px solid #909399';
        box.style.color = 'white';
        this.prefixStyle('transform');
        document.getElementById('printMe');
        //container.style[transform] = 'scale(0.5) translate(-50%, -50%)';
        // container.style.height = '100vh';
        // container.style.overflow = 'hidden';
      }
    }
    for (var i = 0; i < styles.length; i++) {str += styles[i].outerHTML;
    }

    str += "<style>" + '.no-print' + "{display:none;}</style>";
    str += "<style> body {margin: 0; padding: 0} </style>";

    return str;
  },
  getHtml: function () {var inputs = document.querySelectorAll('input');
    var textareas = document.querySelectorAll('textarea');
    var selects = document.querySelectorAll('select');

    for (var k = 0; k < inputs.length; k++) {if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {if (inputs[k].checked == true) {inputs[k].setAttribute('checked', "checked")
        } else {inputs[k].removeAttribute('checked')
        }
      } else if (inputs[k].type == "text") {inputs[k].setAttribute('value', inputs[k].value)
      } else {inputs[k].setAttribute('value', inputs[k].value)
      }
    }

    for (var k2 = 0; k2 < textareas.length; k2++) {if (textareas[k2].type == 'textarea') {textareas[k2].innerHTML = textareas[k2].value
      }
    }

    for (var k3 = 0; k3 < selects.length; k3++) {if (selects[k3].type == 'select-one') {var child = selects[k3].children;
        for (var i in child) {if (child[i].tagName == 'OPTION') {if (child[i].selected == true) {child[i].setAttribute('selected', "selected")
            } else {child[i].removeAttribute('selected')
            }
          }
        }
      }
    }
    return this.dom.outerHTML;
  },
  isDOM: (typeof HTMLElement === 'object') ?
    function (obj) {return obj instanceof HTMLElement;} :
    function (obj) {return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';},
  getAttr: function(element, attr) {return window.getComputedStyle ? window.getComputedStyle(element,)[attr] : element.currentStyle[attr];
  },
  pxStrToNumber: function(pxStr) {return Number(pxStr.slice(0, pxStr.length - 2));
  },

  vender: (function() {let elementStyle = document.createElement('div').style;
    let transformNames = {
      webkit: 'webkitTransform',
      Moz: 'MozTransform',
      O: 'OTransform',
      ms: 'msTransform',
      standard: 'transform',
    };

    for(let key of Object.keys(transformNames)) {if (elementStyle[transformNames[key]] !== undefined) return key;
    }

    return false;
  })(),
  prefixStyle: function(style) {if (this.vender === false) return false;
    if (this.vender === 'standard') return style;
    return this.vender + style.charAt(0).toUpperCase() + style.substr(1);
  }
}


export default VueHtmlToPaper;

最初

此插件尽管是在 Vue 中开发,然而是纯 js 实现,实用于任何前端环境,心愿给大家提供一个思路。

正文完
 0