整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          前端頁面如何原樣快速導出PDF?附示例

          何保持頁面樣式基本不變的前提下將HTML頁面導出為PDF,下面提供一些示例代碼,純屬個人原創,如對你有幫助請記得加關注、加收藏、點贊、轉發、分享~謝謝~~

          • 基本思路:保持頁面樣式基本不變,使用 `html2canvas` 將頁面轉換為圖片,然后再通過 `jspdf` 將圖片分頁導出為PDF文件(中間會遇到圖片或文字等內容在分頁處被切割開的問題,如何解決了?詳見末尾干貨)
          • 上基礎代碼:下面為項目中實際代碼截取
          <div>
              <!-- 要打印的內容區 -->
              <div ref="contentRef">
                  <div class="print-item print-out-flow">這是脫離文檔流的內容區域</div>
                  <div class="print-item">這是一行內容,也是最小葉子元素內容</div>
              </div>
              <!-- 打印內容容器 -->
              <div ref="printContainerRef" class="print-container"></div>
          </div>
          /**
            * 1.使用一個隱藏div裝載有滾動條的div.innerHTML
            * 2.隱藏div使用position: absolute, z-index: -999, left: -9999px, width: 900px 控制讓用戶無感知
            * 3.根據需要覆寫隱藏div內html樣式(例如textarea多行顯示有問題, 可以新增一個隱藏的div
            *    包裹textarea的綁定值, 然后在打印樣式中覆寫樣式, 隱藏textarea并顯示對應div)
            */
          handleExport() {
             // 下面是VUE組件內獲取DOM元素代碼,將內容放置到打印區(定義的隱藏DIV)中
              const contentRef = this.$refs.contentRef as HTMLElement;
              const printContainerRef = this.$refs.printContainerRef as HTMLElement;
              // 打印區的需額外處理絕對定位值, 調整使得第一個元素的.top值為0, 以便于頁面計算
              printContainerRef.innerHTML = contentRef.innerHTML;	
              
              // 所有葉子div元素加上 print-item 樣式名, 脫離文檔流的額外添加 print-out-flow
              handlePrintItem(printContainerRef);  // 解決多頁內容可能被切割問題
              
              html2canvas(printContainerRef, {allowTaint: false, useCORS: true}).then((canvas: any) => {
                const contentHeight = canvas.height;
                const contentWidth = canvas.width;
                // pdf每頁顯示的內容高度
                const pageHeight = contentWidth / 595.28 * 841.89;
                // 未生成pdf的頁面高度
                let offsetHeight = contentHeight;
                // 頁面偏移值
                let position = 0;
                // a4紙的尺寸[595.28, 841.89], canvas圖片按a4紙大小縮放后的寬高
                const imgWidth = 595.28;
                const imgHeight = 595.28 / contentWidth * contentHeight;
          
                const dataURL = canvas.toDataURL('image/jpeg', 1.0);
                const doc = new jsPDF('p', 'pt', 'a4');
          
                if (offsetHeight < pageHeight) {
                  doc.addImage(dataURL, 'JPEG', 0, 0, imgWidth, imgHeight);
                } else {
                  while (offsetHeight > 0) {
                    doc.addImage(dataURL, 'JPEG', 0, position, imgWidth, imgHeight);
                    offsetHeight -= pageHeight;
                    position -= 841.89;
          
                    if (offsetHeight > 0) {
                      doc.addPage();
                    }
                  }
                }
          
                doc.save(this.generateReportFileName());
                printContainerRef.innerHTML = '';
              });
          }

          上干貨代碼:上面分頁導出PDF可能網上能看到類型代碼,但絕對找不到下面的代碼,純手搓解決分頁元素被切開問題(思路:獲取自身定位,如自己剛好在被分頁處,則加上一定的margin-top值將內容向下移)

          /** 
           * 處理打印元素項, 修復分頁后被切割的元素
           * @param printContainerRef 打印內容div容器
           * @param itemClassName 打印最小元素標識類名
           * @param outFlowClassName 脫離文檔流的元素標識類名
           */
          export function handlePrintItem(
            printContainerRef: HTMLElement,
            itemClassName: string = 'print-item',
            outFlowClassName: string = 'print-out-flow'
          ): void {
            const rootClientRect = printContainerRef.getBoundingClientRect();
            // 初始化頁面相關數據
            const totalHeight = rootClientRect.height;  // 內容總高度
            const a4PageHeight = (printContainerRef.clientWidth / 595.28) * 841.89; // a4紙高度
            let pageNum = Math.ceil(totalHeight / a4PageHeight);  // 總頁數
            let addPageHeight = 0;  // 修正被分割元素而增加的頁面高度總和
            let currentPage = 1;  // 當前正在處理切割的頁面
            const splitItemObj: { [key: number]: HTMLElement[] } = {};  // 內容中各頁被切割元素存儲對象
          
            const printItemNodes: NodeListOf<HTMLElement> = printContainerRef.querySelectorAll(`.${itemClassName}`);
            for (let item of printItemNodes) {
              // 如果當前頁已經是最后一頁, 則中斷判斷
              if (currentPage >= pageNum) {
                break;
              }
          
              // 獲取元素絕對定位數據
              const clientRect = item.getBoundingClientRect();
              let top = clientRect.top;
              const selfHeight = clientRect.height;
              // 如果當前元素距離頂部高度大于當前頁面頁腳高度, 則開始判斷下一頁頁腳被切割元素
              if (top > currentPage * a4PageHeight) {
                // 換頁前修正上一頁被切割元素
                addPageHeight += fixSplitItems(currentPage, a4PageHeight, splitItemObj[currentPage], outFlowClassName);
                pageNum = Math.ceil((totalHeight + addPageHeight) / a4PageHeight);
                top = item.getBoundingClientRect().top;
                currentPage++;
              }
              // 如果元素剛好處于兩頁之間, 則記錄該元素
              if (top > (currentPage - 1) * a4PageHeight && top < currentPage * a4PageHeight && top + selfHeight > currentPage * a4PageHeight) {
                if (!splitItemObj[currentPage]) {
                  splitItemObj[currentPage] = [];
                }
                splitItemObj[currentPage].unshift(item);
                // 如果當前元素是最后一個元素, 則直接處理切割元素, 否則交由處理下一頁元素時再處理切割
                if (item === printItemNodes[printItemNodes.length - 1]) {
                  fixSplitItems(currentPage, a4PageHeight, splitItemObj[currentPage], outFlowClassName);
                }
              }
            }
          }
          
          /**
            * 修復當前頁所有被切割元素
            * @param currentPage 當前頁
            * @param pageHeight 每頁高度
            * @param splitElementItems 當前被切割元素數組
            * @param outFlowClassName 脫離文檔流的樣式類名
            */
          function fixSplitItems(
            currentPage: number,
            pageHeight: number,
            splitElementItems: HTMLElement[],
            outFlowClassName: string
          ): number {
            if (!splitElementItems || !splitElementItems.length) {
              return 0;
            }
          
            const yMargin = 5;  // y方向距離頁眉的距離
            const splitItemsMinTop = getSplitItemsMinTop(splitElementItems);
            if (!splitItemsMinTop) {
              return 0;
            }
          
            let fixHeight = currentPage * pageHeight - splitItemsMinTop + yMargin;
            const outFlowElement = splitElementItems.find((item) => item.classList.contains(outFlowClassName));
            if (outFlowElement && outFlowElement.parentElement) {
              const parentPreviousElement = outFlowElement.parentElement.previousElementSibling as HTMLElement;
              fixHeight += getMarinTopNum(parentPreviousElement, outFlowElement.parentElement);
              outFlowElement.parentElement.style.marginTop = `${fixHeight}px`;
              return fixHeight;
            }
          
            splitElementItems.forEach((splitElement) => {
              splitElement.style.marginTop = `${fixHeight}px`;
            });
            return fixHeight;
          }
          
          /**
            * 獲取被切割元素數組中最小高度值(如一行有多個元素被切割,則選出距離頂部最小的高度值)
            * @param splitElementItems 當前被切割元素數組
            */
          function getSplitItemsMinTop(
            splitElementItems: HTMLElement[]
          ): number | undefined {
            // 獲取元素中最小top值作為基準進行修正
            let minTop: number | undefined;
            let minElement: HTMLElement | undefined;
            splitElementItems.forEach((splitElement) => {
              let top = splitElement.getBoundingClientRect().top;
              if (minTop) {
                minTop = top < minTop ? top : minTop;
                minElement = top < minTop ? splitElement : minElement;
              } else {
                minTop = top;
                minElement = splitElement;
              }
            });
          
            // 修正當前節點及其前面同層級節點的margin值
            if (minTop && minElement) {
              const previousElement = splitElementItems[splitElementItems.length - 1].previousElementSibling as HTMLElement;
              minTop -= getMarinTopNum(previousElement, minElement);
            }
            return minTop;
          }
          
          /**
            * 通過前一個兄弟元素和元素自身的位置確認一個距離頂部高度修正值
            * @param previousElement 前一個兄弟元素
            * @param curElement 當前元素
            */
          function getMarinTopNum(previousElement: HTMLElement, curElement: HTMLElement): number {
            let preMarginNum = 0;
            let curMarginNum = 0;
            if (previousElement) {
              // 獲取外聯樣式需要getComputedStyle(), 直接.style時對象的值都為空
              const previousMarginBottom = window.getComputedStyle(previousElement).marginBottom;
              preMarginNum = previousMarginBottom ? Number(previousMarginBottom.replace('px', '')) : 0;
            }
            const marginTop = window.getComputedStyle(curElement).marginTop;
            curMarginNum = marginTop ? Number(marginTop.replace('px', '')) : 0;
            return preMarginNum > curMarginNum ? preMarginNum : curMarginNum;
          }

          以上純原創!歡迎加關注、加收藏、點贊、轉發、分享(代碼閑聊站)~

          么是HTML

          Hyper Text Markup Language, 超文本標記語言

          標記又稱為標簽(Tag), 一般語法:

          <tagName></tagName>

          它可以有屬性(Attribute):

          <tagName attributeName="value">, 如:

          <meta charset="utf-8" />

          標簽也可以不成對地關閉:

          <tagName />

          HTML文檔由瀏覽器解釋并執行。

          HTML文檔基本結構

          <!DOCTYPE html> ----- 告訴瀏覽器用html5的標準來解釋和執行該網頁

          <html>

          <head> ---- 頭部, 可包含meta, title等標簽

          </head>

          <body> ---- 主體, 包含主要內容

          </body>

          </html>

          meta

          <meta charset="utf-8" /> 用于告訴瀏覽器用什么樣的字符編碼來解釋網頁中的文本.

          常見編碼:

          iso-8859-1: 純英文編碼

          gbk, gb2312: 簡體中文編碼

          big5: 大五碼,繁體中文編碼,主要應用于臺灣地區

          utf-8: 國際首選編碼,它兼容所有的字符

          除此之外, meta還可以通過keywords, description屬性對頁面關鍵詞及描述信息進行設置, 以提高搜索引擎的命中.

          title

          網頁標題, 顯示在瀏覽器選項卡的標題欄上!

          文本排版標簽

          h1-h6: 內容標題標簽

          p: 段落

          br: 換行

          hr: 水平線

          strong: 粗體文本

          em: 斜體文本

          span: 無任何特殊樣式的文本

          pre: 預格式標簽,其中的內容在頁面上帶格式渲染

          small: 比當前字體小的文本

          html特殊字符/轉義字符

          空格

          < 小于

          > 大于

          ? 版權符

          " 雙引號

          html注釋

          <!-- 注釋內容 -->

          圖像標簽

          <img

          src="圖像地址"

          title="鼠標懸停提示"

          alt="圖像加載錯誤時的替代文本"

          width="寬度"

          height="高度"

          />

          圖像地址分為2種:

          1. 相對地址, 如: img/cc.jpg

          2. 絕對地址, 如: http://img.bcd.com/2017/1644232421.jpg

          超鏈接


          <a href="鏈接地址" target="目標窗口">文本|圖片</a>

          目標窗口:

          _self: 目標頁面在當前窗口打開

          _blank: 目標頁面在新窗口中打開

          如果是在頁面具有frameset/frame/iframe的場景下:

          _top: 在頂級窗口中打開

          _parent: 在父級窗口中打開

          _自定義名稱: 在指定的特定窗口中打開

          三種用法:

          1. 頁面間鏈接

          <a href="page/login.html"></a>

          2. 錨鏈接

          <a href="#help"></a>

          help是本頁面中一處id為help的標簽, 如: <p id="help">

          或者:

          help是通過a標簽命名的錨記, 如: <a name="help"></a>

          3. 功能性鏈接

          喚醒本地安裝的外部程序如 outlook/foxmail/qq/msn/aliwangwang...

          <a href="mailto:abcdef@qq.com"></a>

          div標簽

          div是一個容器, 常用于頁面的布局

          標簽的分類:

          1. 塊級標簽/塊級元素

          如: div, h1-h6, p, hr

          特征: 獨占容器中的一行, 其寬度是容器的100%

          2. 行級標簽/行級元素

          如: span, img, strong, em, a

          特征1: 多個行級元素可以同處一行, 其寬度由內容來撐開(auto)

          特征2: 大部分行級元素設置其width/height無效

          HBuilder常用快捷鍵

          ctrl + D : 刪除當前行

          ctrl + PgUp : 當前行上移

          ctrl + PgDown : 當前行下移

          ctrl + / : 注釋 | 取消注釋

          ctrl + shift + F : 整理代碼格式

          ctrl + C : 復制當前行

          ctrl + X : 剪切當前行

          ctrl + V : 粘貼

          ctrl + Z : 撤消上一步操作

          ctrl + S : 保存當前文件

          ctrl + shift + S : 保存項目中全部文件

          ctrl + Enter : 在當前行的下方插入新行

          ctrl + shift + Enter : 在當前行的上方插入新行


          以上知識能做的效果圖

          部分效果

          文本標記語言(Hypertext Markup Language)是一種用來制作網頁的標記語言,不需要編譯,可以直接由瀏覽器解釋執行,更形象地說,HTML是瀏覽器的“母語”。在網頁設計中HTML主要負責信息展現,能獨立于各種操作系統平臺,并且可以通知瀏覽器顯示內容。自20世紀90年代以來,HTML就一直被用作Internet的信息展示語言,用于描述網頁的格式設計和與Internet上其他網頁的鏈接信息。


          關于超文本標記語言,有以下含義:

          • 超(Hyper):是相對于線性(linear)來說的。以前的計算機程序基本上是線性運行,即當計算機程序執行完一條命令后,轉向下一行,該行結束后,繼續下移,依此類推。但HTML則不同,它可以在任何時候跳轉到任何位置。例如,程序可以從第1行跳轉到第8行,然后跳轉到第5行,甚至可以跳轉到另外一個程序中去執行。

          • 文本(Text):即文字本身,意味著它是自解釋的(self-explanatory)。

          • 標記(Markup):指的是如何處理文本。對文本作標記的方式,與在文本編輯程序中將文本加粗,或者將一行文字設為標題或列表項目類似。

          • 語言(Language):HTML就是一種語言,它使用了許多英文單詞。

          HTML文件也可以說是一個文本文件,它包含了一些HTML元素、標簽等。HTML文件必須使用.html或.htm為文件名后綴。


          按字母排序



          按功能排序


          HTML主要應用于:

          • 控制頁面和內容的外觀;

          • 發布和檢索聯機文檔;

          • 創建聯機表單;

          • 插入諸如音頻剪輯和視頻剪輯等對象。


          主站蜘蛛池模板: 亚洲一本一道一区二区三区| 国产一区二区三区不卡观| 精品一区二区三区免费毛片爱| 中日韩一区二区三区| 国产成人无码AV一区二区| 亚洲日本精品一区二区 | 国产怡春院无码一区二区| 国产人妖视频一区在线观看| 成人在线视频一区| 国产亚洲一区二区三区在线观看| 亚洲丰满熟女一区二区v| 日本精品视频一区二区| 精品亚洲综合在线第一区| 亚洲乱色熟女一区二区三区蜜臀| 日本一区二区三区在线网| 久久久久久免费一区二区三区| 亚洲国产成人久久一区二区三区| 变态调教一区二区三区| 亚洲一区欧洲一区| 国产一区二区高清在线播放| 久久综合亚洲色一区二区三区 | 国产一区二区三区亚洲综合| 中文无码精品一区二区三区| 中文字幕在线一区二区在线| 精品视频一区二区观看| 亚洲国产精品自在线一区二区| 国产aⅴ精品一区二区三区久久 | 在线精品亚洲一区二区| 亚洲一区二区精品视频| 日本伊人精品一区二区三区| 日本一区二区三区在线视频| 一本大道在线无码一区| 韩国福利一区二区三区高清视频 | 国产成人精品无码一区二区| 狠狠综合久久av一区二区| 亚洲国产福利精品一区二区| 制服中文字幕一区二区| 动漫精品一区二区三区3d| 毛片一区二区三区无码| 中文字幕日韩一区| 消息称老熟妇乱视频一区二区|