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

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


[toc]

前言

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

生成海報過程

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
        // 這個函數(shù)就是實現(xiàn)裁剪,參看下圖與 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 可能保存不了, 網(wǎng)上說的原因圖片過大童本, 可以降低 html2canvas 的選項 scale 來減少真屯。

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

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

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

html2canvas 報錯 'maximum call stack size exceeded'

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市母谎,隨后出現(xiàn)的幾起案子瘦黑,更是在濱河造成了極大的恐慌,老刑警劉巖奇唤,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幸斥,死亡現(xiàn)場離奇詭異,居然都是意外死亡咬扇,警方通過查閱死者的電腦和手機甲葬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懈贺,“玉大人经窖,你說我怎么就攤上這事∷蟛樱” “怎么了画侣?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長堡妒。 經(jīng)常有香客問我配乱,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任搬泥,我火速辦了婚禮桑寨,結果婚禮上,老公的妹妹穿的比我還像新娘忿檩。我一直安慰自己西疤,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布休溶。 她就那樣靜靜地躺著代赁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪兽掰。 梳的紋絲不亂的頭發(fā)上芭碍,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音孽尽,去河邊找鬼窖壕。 笑死,一個胖子當著我的面吹牛杉女,可吹牛的內容都是我干的瞻讽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼熏挎,長吁一口氣:“原來是場噩夢啊……” “哼速勇!你這毒婦竟也來了?” 一聲冷哼從身側響起坎拐,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤烦磁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后哼勇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體都伪,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年积担,在試婚紗的時候發(fā)現(xiàn)自己被綠了陨晶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡帝璧,死狀恐怖先誉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情聋溜,我是刑警寧澤谆膳,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站撮躁,受9級特大地震影響漱病,放射性物質發(fā)生泄漏买雾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一杨帽、第九天 我趴在偏房一處隱蔽的房頂上張望漓穿。 院中可真熱鬧,春花似錦注盈、人聲如沸晃危。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽僚饭。三九已至,卻和暖如春胧砰,著一層夾襖步出監(jiān)牢的瞬間鳍鸵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工尉间, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偿乖,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓哲嘲,卻偏偏與公主長得像贪薪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子眠副,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345