簡單的網頁截圖功能 – html2canvas、Canvas2image

簡單的網頁截圖功能 - html2canvas、Canvas2image
簡單的網頁截圖功能 - html2canvas、Canvas2image

本篇要解決的問題

幾年前公司有一個案子,是要讓使用者可以做一個簡單的卡片,然後把這卡片存成圖檔分享給朋友,那時就有寫過一次把指定的 <div> 存成圖檔的 JS,結果,案子太久遠了,竟然沒把當時寫存圖檔的 function 給存下來 XD,直到前陣子前同事在問才想起這件事。

把網頁的區塊存成圖檔,總覺得以後有機會再用到,就先整理這篇筆記文,以後真的要用的時候就可以直接 複製貼上 複習一下怎麼寫。

要把頁面存成圖檔有二個步驟,各自用了一個套件。

  1. 把指定的 HTML 區塊轉成 Canvas:html2canvas
  2. 把 Canvas 轉存成圖檔:Canvas2image

本篇有做一個 Demo 出來,在繼續往下閱讀前大家可以先玩玩看:

https://letswritetw.github.io/letswrite-web-to-image/

另外,August 也有寫了另一篇是使用 DOM to Image 這套來進行截圖的筆記文,文章連結如下:


將指定區塊轉成 Canvas

html2canvas 這個套件很方便,基本上官方文件上就明明白白的寫了使用方法:

const el = document.querySelector("#something");
html2canvas(el, [option])
  .then(canvas => { document.body.appendChild(canvas) });

option 是選填,可寫可不寫,項目請參考官方文件的 Configuration,如果去爬一些教學文,會看見最常寫到的就是 useCORS: true

useCORS: true 是在我們要抓圖的 div 裡如果存在其它網域的圖片,就必須告訴 html2canvas 說我們有跨網域的圖片。

啊不過,經 August 人體實驗證明,當有圖片是跨網域的來源,很常會抓圖失敗,失敗機率是 3 張失敗 1 ~ 2 張,如果真要用這套把 div 轉成 Canvas,div 裡的圖要用同網域的會比較保險。

在本篇的範例裡,除了 useCORS,也有用到 backgroundColor,直接把空白的地方填入網頁裡的背景色,看上去才會無違和。

如果要好玩一點,也可以背後再接一個 Color Thief 抓出圖檔的主要色系當背景色,就不用寫死背景色的值。


把 Canvas 轉存成圖檔

Canvas2image 這套很妙,一開始 August 在寫 Demo 時,JS 是引用 CDN 上的絕對路徑,然後一直無法自訂圖檔的檔名,可是看官方的原始碼裡又確實有參數是讓人放檔名的。

如此如此,這般這般,就決定直接從官方 GitHub 上下載 JS 檔來用,然後果不其然就直接成功設定圖檔檔名勒。

只是官方給的 JS 要用 import 才能使用,如果對 import 還不熟的朋友,可以考慮下載 August 前陣子寫的 開發初始檔,就能直接編譯 ES6 的 JS 檔。

Canvas2image 的使用方式很簡單:

Canvas2Image.saveAsImage(canvasObj, width, height, type, filename)
Canvas2Image.saveAsPNG(canvasObj, width, height, filename)
Canvas2Image.saveAsJPEG(canvasObj, width, height, filename)
Canvas2Image.saveAsGIF(canvasObj, width, height, filename)
Canvas2Image.saveAsBMP(canvasObj, width, height, filename)

官方的說明文件沒有寫上 filename 這個參數,應該是漏了寫,因為 August 去看了 JS 原始碼是有這個參數的。

canvasObj 就直接寫我們從 html2canvas 取得的 canvas 就行。

widthheight 這二個的值 canvas 本身就會有,如果沒有其它需求可以直接寫 canvas.widthcanvas.height

第一個的 saveAsImage 需要多帶 type 參數,寫上要什麼檔案類型就行,比方 pngjpeg。不過建議就直接用後面四個 function 吧,不讓使用者選直接存成我們希望使用者擁有的類型省事多了。

總合上面二個步驟,程式碼簡單整理如下。

// 將指定區塊轉成 Canvas
function htmlToCanvas(id) {
  const el = document.getElementById(id);
  html2canvas(el).then(canvas => {
    canvasToImage(canvas);
  });
}

// 把 Canvas 轉存成圖檔
function canvasToImage(canvas) {
  const filename = 'xxxxx';
  Canvas2Image.saveAsPNG(
    canvas,
    canvas.width, canvas.height,
    filename
  );
}

截多個 div 存成圖

假設今天我們要截的圖是頁面上幾個 div 併在一起的,方法很簡單,在本篇的 Demo 裡就有寫出來,那個「截圖 Section 1 + 2」的按鈕就是在示範這件事。

Demo 中有二個 sectoin,分別是 <section id="section1"><section id="section2">

我們的目標是一個按鈕,存出來的圖片包含這二個 section。

首先,建立第三個 section,隨意取叫 <section id="section3">,然後用 JS 把 1、2 二個 section 的 HTML 塞進去。

塞進 3 後,就可以用 html2canvas 來指定 3 轉成 Canvas。

這樣會遇到一個小狀況,就是 html2canvas 要的是真的要存在頁面上的區塊才行,而當我們把 1、2 的內容都塞到 3 後,3 就會有內容,就會被使用者看到。

把 3 寫上 display: none 是不行的,因為在網頁上就不會有寬高,會被認定是圖片不存在,就無法透過 html2canvas 轉成 Canvas。

山不轉路轉,藏起來不行,那我們就把它移到畫面之外,讓使用者在螢幕上看不到就行。

August 在 Demo 裡的作法如下:

html, body {
  width: 100;
  overflow-x: hidden;
}
#section3 {
  position: absolute;
  left: -100%;
}

這樣,就可以順利截圖,而使用者是看不到那個被我們偷偷塞了內容的 section3 的。


範例及原始碼

原始碼存在 GitHub 上,也用了 GitHub Pages 生成 Demo 頁。

要取用前麻煩分享本篇,或是 GitHub 上點個星星,你的小小動作對本站都是大大的鼓勵。

原始碼:https://github.com/letswritetw/letswrite-web-to-image

Demo:https://letswritetw.github.io/letswrite-web-to-image/

Summary
簡單的網頁截圖功能 - html2canvas、Canvas2image
Article Name
簡單的網頁截圖功能 - html2canvas、Canvas2image
Description
這篇文章教你如何使用 html2canvas 和 Canvas2image 來將網頁的特定區塊轉換成圖像文件。文章內容包括將指定的 HTML 區塊轉換成 Canvas,將 Canvas 轉存成圖像文件,以及如何截取多個 div 並將它們存儲為圖像。文章提供了範例和原始碼,以及一個實用的 Demo 供讀者參考和使用。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg
訂閱
通知
guest

3 Comments
最舊
最新
Inline Feedbacks
看所有留言
student
student
4 月 之前

最後一種做法很妙!感謝分享,目前(2023/12/12)引用 CDN 是可以自訂檔案名稱

student
student
3 月 之前

實作此截圖功能時,發現若是圖片跨域會無法正確轉存圖檔