前端生成海報 使用 html2canvas 及其注意事項

標簽(空格分隔): 前端 html2canvas


[toc]

前言

最近用有個需求需要上傳圖片然后生成海報保存,使用了 html2canvas 來實現,這里記錄一下實現過程與遇到的問題

生成海報過程

1.用戶上傳圖片, 拿到 File 對象或者 ObjectURL

2.ios 需要修復上傳圖片的旋轉角度,需要使用配合 exif.js 與 canvas 來解決,可參考以下代碼。 參看 《移動端圖片上傳旋轉、壓縮的解決方案》

// 主過程
img.onload = function() {
    EXIF.getData(this, () => {
        // 獲取當前旋轉角度
        const orientation = EXIF.getTag(this, "Orientation")
        // 修復旋轉
        fixOrientation(this, orientation, (blob) => {
            const img2 = new Image()
            img2.onload = function() {
                // zheng正方形
                clipSquare(this, (blob2) => {
                    const result = URL.createObjectURL(blob2)
                    // ...
                })
            }
            img2.src = URL.createObjectURL(blob)
        })
    })
}
img.src = source
// 修復旋轉
fixOrientation(img, orientation, cb) {
    const canvas = document.createElement("canvas")
    const ctx = canvas.getContext('2d');
    const w = img.width
    const h = img.height
    canvas.width = w;
    canvas.height = h;
    switch (orientation) {
        case 6: // 旋轉90度
            canvas.width = h;
            canvas.height = w;
            ctx.rotate(Math.PI / 2);
            // (0,-imgHeight) 從旋轉原理圖那里獲得的起始點
            ctx.drawImage(img, 0, -h, w, h);
            break;
        case 3: // 旋轉180度
            ctx.rotate(Math.PI);
            ctx.drawImage(img, -w, -h, w, h);
            break;
        case 8: // 旋轉-90度
            canvas.width = h;
            canvas.height = w;
            ctx.rotate(3 * Math.PI / 2);
            ctx.drawImage(img, -w, 0, w, h);
            break;
        default:
            ctx.drawImage(img, 0, 0, w, h);
            break
    }
    canvas.toBlob(cb)
}
// 裁剪正方形,取圖片中間的最大正方形
clipSquare(img, cb) {
    const canvas = document.createElement("canvas")
    const ctx = canvas.getContext('2d');
    const w = img.width
    const h = img.height
    const diff = Math.abs(w-h)
    if (w >= h) {
        canvas.width = h
        canvas.height = h
        // 這個函數就是實現裁剪,參看下圖與 https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage
        ctx.drawImage(img, diff/2, 0, h, h, 0, 0, h, h)
    } else {
        canvas.width = w
        canvas.height = w
        ctx.drawImage(img, 0, diff/2, w, w, 0, 0, w, w)
    }
    canvas.toBlob(cb)
}
image

3.目標元素上布局,然后用 html2canvas 生成

htlm2canvas(targetDom, option)
    .then((canvas) => {
        const context = canvas.getContext('2d')
        canvas.toBlob((blob) => {
            const img = new Image()
            img.src = canvas.toDataURL('image/jpeg', 0.9)
            document.body.appendChild(img)
        }, "image/jpeg", 0.9)
    })

其他注意事項

圖片跨域問題

開啟 html2canvas 的兩個選項

const option = {
    allowTaint: true,
    useCORS: true,
}

生成海報清晰度問題

將目標元素及其子元素的 css 尺寸放大多倍,然后用 transform: scale() 來縮放到需要顯示的位置。當然,也可以不顯示該目標元素,直接顯示生成后的 canvas 或者 img

另外可以給 canvas 關閉抗鋸齒效果,不過感覺沒用。

// 關閉抗鋸齒,感覺沒用
context.mozImageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.msImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;

圖片不能保存

最終海報是由 canvas 通過 toDataURL() 或者 toBlob()
如果圖片是用 toDataURL() 導出,那么 ios 可能保存不了, 網上說的原因圖片過大, 可以降低 html2canvas 的選項 scale 來減少。

如果圖片使用 toBlob(), 然后用 URL.createObjectURL(blob) 來導出,android 可能保存不了,提示 保存到手機相冊失敗,我就是遇到這種情況,那么可以將其導出改為 toDataURL 或者直接轉,可參考這篇文章 《Blob/DataURL/canvas/image的相互轉換》

好像 toDataURL(type) 或者 toBlob(type) type 是 png 的話,android 也不能保存

另外,微信內只能長按保存圖片,不能用 <a></a> 標簽來實現點擊下載

html2canvas 報錯 'maximum call stack size exceeded'

我的情況是,用戶上傳的圖片,經過調轉旋轉、正方形裁剪后,生成了一個 dataURL 賦值到目標 img 元素上用于 html2canvas 生成海報,結果就報這個錯了,我將那個 dataURL 改成 ObjectURL 就沒問題了

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370