簡單的網頁截圖功能 - DOM to Image
Tags: javascript, webdev, domtoimage, capture
Published: 2023-02-11 04:35:16

## 本篇要解決的問題

去年九月時，August 有寫了一篇〈[簡單的網頁截圖功能 – html2canvas、Canvas2image](https://www.letswrite.tw/web-to-image/)〉，當時寫完後，以為這就是前端在網頁截圖這塊的終極套件（咦？）。結果最近工作上真的要使用這套截圖、下載的功能時，才發現真實的情形下，因為網頁會比 Demo 中的複雜，所以存下來的圖，圖片裡的網頁有些部份是跑版的！

傻眼，還得另外調一版 CSS 給截圖狀態下的網頁，多耗了一點時間。

最近又有另一個案子要使用截圖並下載的功能，發現一樣會有跑版的情形，August 眉頭一皺，覺得案情並不單純，所以就問了 ChatGPT 看有沒有替代的工具，對話如下圖：

![ChatGPT 提供網頁截圖的套件](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7hols67fmvh9czy8ut7e.png)

立刻試了一下裡面提到的 dom-to-image，發現不會跑版耶，而且下載下來的圖片，檔案大小少很多。

但中間過程中還是有踩到坑，就決定為這個套件再寫一篇截圖的功能，讓大家要使用時可以拿來當速成避坑指南。

本篇最後有一個彩蛋，是關於最近被生成式 AI 給影響的部份，可以看到最後喔。

本篇最後完成的 Demo：

<https://letswritetw.github.io/letswrite-dom-to-image/>

---

## 安裝 DOM to Image

DOM to Image：[https://github.com/tsayen/dom-to-image](https://github.com/tsayen/dom-to-image)

安裝方式就跟一般要使用 JavaScript 的套件一樣，可以直接 CDN 引用或是用 npm package。

**CDN**

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"></script>
```

**npm package**

先安裝 package：

```shell
$ npm install dom-to-image
或
$ yarn add dom-to-image
```

JS 中 import：

```javascript
import domtoimage from 'dom-to-image';
```

---

## 使用 Dom to Image

DOM to Image 可以指定存成 JPEG、PNG、SVG。

SVG 因為 August 沒實際上用到，這邊就不會使用。

以下示範存成 PNG 的圖檔。

```js
// 抓取指定的 div，產生 base64 格式圖片
// 之所以重複二次，下一段的注意事項會解釋
const el = document.getElementById('xxx');
const config = {
    quality: 1,
    style: {
        "filter": "grayscale(100%)"
    }
};
    
const dataUriTemp = await domtoimage.toPng(el, captureConfig).then(dataUrl => dataUrl);
const dataUri = await domtoimage.toPng(el, captureConfig).then(dataUrl => dataUrl);

// 下載圖片
const link = document.createElement('a');
const filename = 'Demo.png';
link.download = filename;
link.href = dataUri;
link.click();
```

如果想存成 JPEG，就是把 `toPng` 改成 `toJpeg`，然後檔案的部份副檔名也要記得改成 `*.jpeg`。

程式碼中的 `config` 是可傳入的參數，這邊示範的是改 `quality`、`style`。

因為 style 寫了 `filter: grayscale(100%)`，所以下載下來的圖片都會變成灰階。

如果是活動性質的頁面，截圖前加上 `style` 會是很有趣的作法。

可用的參數如下：

- filter：可以濾掉指定的 tag。官方文件是用在濾掉 SVG 的 tag。
- bgcolor：指定背景色。
- width、height：指定寬、高。
- style：可以加上額外的 style。
- quality：圖片的品質，數值是 0 - 1，1 為最高，預設是 1。
- cacheBust：要不要清除緩存，true 是要、false 是否，預設是 false。
- imagePlaceholder：如果抓不到 div 的圖片，要給什麼墊檔圖，看文件是要填 Base64。

---

## 注意事項

以下是 August 踩了一上午的坑後，終於發現在手機操作時有 2 個要注意的地方，不注意的話會讓 Dom-to-Image 產生的圖檔，div 中的圖片變一片空白：

- 圖片有 `position: relative`。
- 執行 `domtoimage.toJpeg(targetId, [config])` 時，第一次圖片的地方會是空白，但第二次以後就會正常，所以……雖然很蠢，但為了維持手機世界的和平，請執行二次，這就是為什麼範例程式碼會先寫一次 `dataUriTemp`，接著又寫了一次 `dataUri`。

另外，還有一種情形會讓下載的圖片不完整，就是抓取的 `div` 本身有寫 `max-width: xxx; margin: 0 auto;`，寫了 `max-width`，會讓 DOM to Image 在抓圖時寫上限定的寬，但因為抓圖不會跟著置中，就變成抓下來的位置會從最左邊開始，造成抓下來的圖右邊會被裁切掉。

---

## 本篇 Demo 及原始碼

本篇的程式碼有放上 GitHub 上，也用 GitHub Pages 產生了 Demo，請自行取用，但希望在取用前能分享本篇，或在 GitHub 上點個星星，你的一個小小動作對本站都是大大的鼓勵。

原始碼：<https://github.com/letswritetw/letswrite-dom-to-image>

Demo：<https://letswritetw.github.io/letswrite-dom-to-image/>

---

## Notion AI 生成的文章

其實在開始要寫這篇時，August 就被生成式 AI 驚呆了。

August 前幾天才通過 Notion 加上 AI 的功能申請，所以當今天打開 Notion，才打完本篇的標題後，就跳出一個「**Ask AI to write**」的選項，好奇了按了一下……AI 就把本篇幾百字關於 DOM to Image 的介紹及應用文章寫完了，雖然給的是英文。

好啊，都給 AI 寫就好啊~~~

其實在開始用了 ChatGPT 後，就在想是不是還要繼續堅持寫學習的筆記文？

因為未來大家會愈來愈來常先問 AI，問不到才 Google。

那，即便堅持寫，也是寫給 AI 拿去學習用的吧？來網站的人，真實的人類會愈來愈少。

這樣，還有經營的必要嗎？現在除了「之後面試時可以當作品集」的理由，想不到其它繼續寫下去的理由了。

Anyway，以下是 Notion AI 所編寫的文章，有經過 August 修改，把文字從英文轉成中文，並調整了詞句。

大家可以對比一下上面幾段出自 August 的真人文字，跟下面幾段 AI 所生成的文字，未來 August 撰文，為了省時間跟心力，會陸續使用 AI 提供的文字了。

---

DOM to Image 是一個非常有用的工具，用於建立網頁的螢幕截圖。它允許用戶通過點擊一個按鈕快速輕鬆地抓取網頁的快照然後保存，可以共享結果給朋友或同事。

本篇筆記文將提供一個使用 DOM to Image 建立一個簡單的網頁截圖功能的簡單教學。

第一步是在 HTML 頁面中引用 DOM to Image 的 JS。這可以在 HTML 引用 CDN 來快速輕鬆完成：

```html
<script src="https://unpkg.com/dom-to-image@2.6.0/dist/dom-to-image.min.js"></script>
```

引用了 CDN 後，還需要建立一個函數，這個函數可以讓我們抓取螢幕截圖。

此函數接受 HTML 元素作為參數，並返回一個解析為圖像的 Promise。

以下是使用 `domtoimage.toPng()` 的方法完成的：

```javascript
function captureScreenshot(element) {
  return new Promise((resolve, reject) => {
    domtoimage.toPng(element)
      .then((dataUrl) => {
        resolve(dataUrl);
      })
      .catch((error) => {
        reject(error);
      });
  });
}

```

建立函數後，執行函數便可從 HTML 中輕鬆獲取螢幕截圖：

```javascript
captureScreenshot(document.getElementById('element-id'));
```

這是這樣！只需幾行代碼，您現在就可以使用強大的 DOM to Image 建立一個簡單的網頁截圖功能。

當我們需要快速抓取網頁的螢幕截圖時，此明件可以節省時間和麻煩，對於任何使用網頁的人來說，它都是一個不可多得的工具。