Html2canvas——圖片空白的幾種排查解決方案

喜葵發表於2024-03-20
問題:用html2canvas生成畫布圖片,再轉成pdf。生成圖片時內容結構裡的圖片顯示空白。
解決: 首先伺服器設定圖片允許跨域,如阿里雲騰訊雲配置跨域規則。其次圖片設定crossOrigin=“anonymous”,並且拿到圖片地址加隨機引數如 src +‘?v=’ + Math.random()防止使用快取,再者html2canvas設定屬性useCORS, allowTaint為true。

  下列文章來源該篇

一、工作原理

html2canvas庫的工作原理並不是真正的“截圖”,而是讀取網頁上的目標DOM節點的資訊來繪製canvas,所以它並不支援所有的css屬性

二、在 img標籤中載入外部圖片

前提是外部圖片允許跨域,需要伺服器設定

以nginx為例,response-header內要存在Access-Control-Allow-Orgin:xxxx(可以是*,安全性要求比較高的可以根據主域名自定義),如果該資源所在的web-server是不支援跨域的,儲存圖片是不會成功的。

img標籤設定跨域

當html2canvas中生成的截圖中包含外部圖片,那麼外部圖片在引入的時候就需要設定允許跨域。

另外,透過 ‘img’ 載入的圖片,瀏覽器預設情況下會將其快取起來,在canvas中用到圖片時,就會直接使用快取圖片,不會再重新發請求。又因為瀏覽器本身不會有跨域問題,所以如果‘img’沒有設定 crossorigin 屬性,那麼就會出現跨域問題,圖片不能再次被複用到 canvas 上去的。

1. 'img’標籤設定crossOrigin屬性
<img crossOrigin="anonymous" src={imgSrc} alt="img" />

這是最簡單的方法,讓圖片可以複用到canvas上。

2. chrome設定
<img src={imgSrc} alt="img" />

瀏覽器設定 disable cache,這種方法需要設定瀏覽器屬性,不推薦。

3. 使用JS載入圖片
const [imgBase, setImgBase] = useState("");
<img src={imgBase} alt="img" />
  const downloadIamge = imgsrc => {
    //下載圖片地址和圖片名
    let image = new Image();
    // 解決跨域 Canvas 汙染問題
    image.setAttribute("crossOrigin", "anonymous");
    image.onload = function() {
      let canvas = document.createElement("canvas");
      canvas.width = image.width;
      canvas.height = image.height;
      let context = canvas.getContext("2d");
      context.drawImage(image, 0, 0, image.width, image.height);
      let url = canvas.toDataURL("image/png"); //得到圖片的base64編碼資料
      setImgBase(url);
    };
    image.src = imgsrc;
  };

這種方法能避免使用圖片快取,在開發過程中,使用js方式請求圖片資源,最好每一次都加個隨機數,以保證源都是最新的,不走快取。

三、在 html2canvas 中使用
 const createImg = async id => {
     const dom = document.getElementById(id);
     const config = {
          useCORS: true,
          width: dom.offsetWidth,
          height: dom.offsetHeight,
          scrollX: 0,
          scrollY: 0,
          x: 0,
          y: 0,
        };
const data = await html2canvas(dom, config).then(canvas =&gt; {
  canvas.id = "mycanvas";

  document.body.appendChild(canvas);
  const mycanvas = document.getElementById("mycanvas");
  mycanvas.style.display = "none";
  mycanvas.style.position = "fixed";
  mycanvas.style.top = "0px";
  mycanvas.style.left = "0px";
  mycanvas.style.zIndex = "9999";

  let imgData = mycanvas.toDataURL("png");
  imgData = imgData.replace(fixType("png"), "image/octet-stream");
  const file = dataURLtoFile(imgData, "poster.png");

  return file;
});

return data;
};

四、html2canvas設定

使用外部圖片就會面臨跨域的問題,html2canvas也需要設定屬性。

1. 允許畫布圖片跨域
confit = { useCORS: true }

2. 允許外部圖片汙染畫布
confit = { allowTaint: true } 

使用上面兩種方法就可以讓外部圖片顯示在畫布上,雖然方法2可以讓外部圖片顯示在畫布上,但是卻不能呼叫 toBlob(), toDataURL() 或 getImageData() 方法,呼叫它們會丟擲安全錯誤。

五、可能出現的問題
1. html2canvas生成的圖片無法顯示

可能是使用快取圖片,有以下解決方法

  • 在 Chrome 的偵錯程式中,在 network 皮膚中,勾選 disable cache 選項,再重新載入圖片
  • 圖片的訪問地址加個隨機數
  • 將圖片url轉成base64
2. html2canvas生成的圖片顯示空白

如果是出現了空白,那有可能是轉成圖片的部分出現捲軸且是在彈窗中展示,需要設定相關滾動高度。
或者有可能是dom不對,請保證dom是要截圖那個元素的父元素

 const dom = document.getElementById(id);
 const config = {
    useCORS: true,
    windowHeight:modal.scrollHeight + 24 + 100,//獲取y方向頁面包含捲軸的高度,24和100為padding,margin
    width: dom.offsetWidth + 48,//48為padding值
    height: dom.clientHeight + 400,//可見高度+padding+margin
    y: window.pageYOffset + 100,//捲軸高度修復
    scrollX: 17
 };

3. ‘img’ 加上crossOrigin="anonymous"之後圖片無法顯示

這個有可能是瀏覽器快取導致了,可以試試重新載入資源,也可以在資源的請求路徑後加上時間戳重新載入。

六、參考
  1. 中文文件
  2. canvas.toDataURL()報錯的解決方案全都在這了
  3. 一個關於image訪問圖片跨域的問題
  4. canvas.toDataURL()報錯的解決方案全都在這了
  5. html2canvas生成圖片
  6. html2canvas截圖空白問題
  7. html2canvas生成pdf頁面空白
  8. 基於html2canvas實現網頁儲存為圖片及圖片清晰度最佳化

相關文章