vue使用html2canvas踩坑總結

需求場景

運營後臺上傳一張圖,同時頁面生成小程序二維碼,與運營上傳的圖合成一張大圖,用於該頁面在朋友圈的分享傳播。html

實現思路

1. 背景圖上傳

背景圖上傳,調用接口實現文件上傳到oss平臺,生成背景圖連接。vue

2. 生成頁面的小程序二維碼

調用小程序二維碼生成接口,生成該頁面的二維碼。詳情參考小程序官方文檔:developers.weixin.qq.com/miniprogram…。調用時注意參數長度有限制,若是參數過長會致使二維碼生成失敗。咱們採用的作法是跟小程序約定一套url映射規則,經過特定的參數來匹配對應的h5頁面。web

3. 編寫截圖的html代碼

將須要生成截圖的html代碼編寫完整,其中包括背景圖(運營上傳的背景圖),頁面小程序二維碼等其餘元素。canvas

4. 調用html2canvas實現截圖

網上搜索了下,使用html2canvas插件能夠實現截圖功能,代碼以下:小程序

// index.js
import html2canvas from 'html2canvas';

html2canvas(this.$refs.shareImgElem, {
  useCORS: true,
  backgroundColor: null
})
.then(canvas => {
  const dataUrl = canvas.toDataURL('images/jpg');
  // 第一步:將dataUrl轉換成Blob
  const blob = this.base64ToBlob(dataUrl);
  // 第二步:上傳分享圖
  this.uploadShareImg(blob);
})
複製代碼
// index.vue
// 須要截圖的html代碼
<div ref="shareImgElem">....</div>
// 截圖圖片的連接
<img :src="imgUrl" />
複製代碼

此時覺得將dataUrl保存下來,就能夠完美解決這個需求了。然而事實大跌眼鏡,截圖生成的base64位的圖是白屏的。網上也查詢了html2canvas的用法,肯定調用方法沒有寫錯,可是截出來的圖就是空白的。後來查緣由,從最簡單的demo開始寫起,終於發現了白屏的緣由,現總結以下。api

5. 截圖白屏問題總結

5.1 圖片跨域問題

截屏的代碼裏若是包含圖片,圖片須要設置容許跨域訪問,不然js是讀不到圖片信息的。若是圖片是放在cdn上,cdn須要設置cors相關設置,也就是圖片請求的響應頭裏須要設置Access-Control-Allow-Origin: *跨域

咱們公司的圖片是上傳到阿里oss平臺,oss裏bucket設置跨域信息是控制圖片上傳時的域名來源的。而咱們是須要設置圖片讀取時能跨域,圖片是存放在cdn上,因此聯繫運維在cdn配置里加上跨域信息便可。bash

5.2 截圖元素在屏幕可見範圍內

圖片請求的響應頭裏加上跨域信息後,截圖看仍是白屏,接着繼續找緣由,最後發現當截屏元素在首屏可見範圍內就能夠生成正確的截圖。原來是在截圖生成過程當中,若是鼠標在滾動,生成出來的截圖在canvas畫布上會有偏移。解決這個問題有兩個操做:cors

5.2.1 將截圖元素提早,放在頁面頂部,在屏幕範圍以內。

5.2.2 截圖生成過程當中,頁面禁用滾動。代碼以下:

dom.setScrollTop(0);  // 先滾動到最頂部
document.documentElement.style.position = 'fixed';
複製代碼

5.2.3 截圖生成完成後,頁面恢復滾動,代碼以下:

document.documentElement.style.position = '';
複製代碼

這時終於截圖展現出來了。可是此時截圖是base64編碼的,這麼大一串字符,存到後臺不大合適,此時考慮將base64字符轉換成blob二進制數據流,上傳到oss。運維

6. 上傳截圖

6.1 將base64編碼的字符轉換成blob二進制數據對象。

轉換代碼以下:

// base64轉換成blob數據
base64ToBlob(dataUrl, type) {
    var arr = dataUrl.split(',');
    var mime = arr[0].match(/:(.*?);/)[1] || type;
    // 去掉url的頭,並轉化爲byte
    var bytes = window.atob(arr[1]);
    // 處理異常,將ascii碼小於0的轉換爲大於0
    var ab = new ArrayBuffer(bytes.length);
    // 生成視圖(直接針對內存):8位無符號整數,長度1個字節
    var ia = new Uint8Array(ab);
    for (var i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
    }
    return new Blob([ab], {
        type: mime
    });
}
複製代碼

6.2 將blob二進制數據流上傳到oss平臺

代碼以下:

// 上傳Blob二進制數據
uploadBlob(fileName, blob) {
    return new Promise((resolve, reject) => {
        async function putBlob() {
            try {
                let result = await ossClient.put(fileName, blob);
                result.imgUrl = `${CDN_IMAGE_DOMAIN}/${result.name}`;
                resolve(result);
            } catch (e) {
                reject(e);
            }
        }
        putBlob();
    });
}
複製代碼
// 上傳分享大圖
uploadShareImg(blob) {
    const fileName = `web/activityms/share_big_img_${Date.parse(new Date())}.jpg`;
    this
        .uploadBlob(fileName, blob)
        .then(res => {
            this.imgUrl = res.imgUrl;
            this.$message.success('朋友圈分享大圖上傳成功!');
        });
}
複製代碼

到此,截圖生成成功,且成功上傳到oss平臺,並返回圖片路徑。

相關文章
相關標籤/搜索