html2canvas適配總結(jié)

緣起

近期在使用 html2canvas 插件生成圖片時账磺,發(fā)現(xiàn)對于 svg 元素支持不是很好临梗。

而且查閱網(wǎng)友的解決方案時宇挫, 發(fā)現(xiàn)是一份原文和N份copy捧书, 總體來說解決方案有以下幾種:

0吹泡、使用 canvg 插件轉(zhuǎn)換 svg 元素

//以下是對svg的處理
var nodesToRecover = [];
var nodesToRemove = [];
var svgElem = $("#divReport").find('svg');//divReport為需要截取成圖片的dom的id
svgElem.each(function (index, node) {
    var parentNode = node.parentNode;
    var svg = node.outerHTML.trim();
    var canvas = document.createElement('canvas');
    canvg(canvas, svg); 
    if (node.style.position) {
        canvas.style.position += node.style.position;
        canvas.style.left += node.style.left;
        canvas.style.top += node.style.top;
    }
    nodesToRecover.push({
        parent: parentNode,
        child: node
    });
    parentNode.removeChild(node);

    nodesToRemove.push({
        parent: parentNode,
        child: canvas
    });

    parentNode.appendChild(canvas);
});

這里有 nodesToRecovernodesToRemove 兩個變量,猜測應(yīng)該是方便回滾用经瓷, 但是并沒有回滾的相關(guān)代碼爆哑。

1、把 svg 元素轉(zhuǎn)換為圖片舆吮,然后再轉(zhuǎn)換成 canvas元素

//允許跨域獲取揭朝,否則百度地圖不能生成圖片
const opts = {
    useCORS: true,
    ignoreElements: el => {
        const tagName = el.tagName.toLowerCase();
        const list = ['head', 'body', 'style', 'title', 'meta']
        if(list.includes(tagName)) return false;
        // id="extra" 下所有節(jié)點(diǎn)忽略
        if(el.id === 'extra') return true;
        return false;
    },
    // TODO:: SVG to canvas
    onclone(cloneDom) {
        const svgElems = $(cloneDom).find('svg');
        svgElems.each(function (index, node) {
            let parentNode = node.parentNode;
            const svg_string = (node.outerHTML || xmlserializer.serializeToString(node)).trim()
            const img = new Image();
            img.src = 'data:image/svg+xml;charset=utf-8,' + svg_string;
            img.crossOrigin = 'anonymous';
            img.onload = function(){
                const width = parseFloat($(node).css('width'));
                const height = parseFloat($(node).css('height'));
                const canvas = document.createElement('canvas');
                canvas.width = width;
                canvas.height = height;
                const ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0, width, height);
                parentNode.appendChild(canvas).
                parentNode.removeChild(node);
            }
        });
    }
}

我發(fā)現(xiàn)百度地圖上的 svg 元素是有絕對定位和偏移的, 他們的方案不能解決偏移的問題色冀。

性空

后來測試方案0并不成功潭袱。 測試方案一,是因?yàn)闆]有追加圖片到document 里面锋恬,導(dǎo)致沒有觸發(fā)onload方法失敗的敌卓, 這里進(jìn)行如下改進(jìn):

  • 0、把當(dāng)前頁面的svg元素轉(zhuǎn)換為 canvas 元素:
// 把svg轉(zhuǎn)換為canvas
async convertSvg2Canvas() {
    const svgElms = document.getElementsByTagName('svg');
    // 回調(diào)
    const callbacks = [];
    for(let svg of svgElms) {
        const parentElement = svg.parentElement;
        const img = new Image();
        img.src = `data:image/svg+xml,${encodeURIComponent((new XMLSerializer()).serializeToString(svg))}`;
        img.crossOrigin = 'anonymous';
        img.onload = async () => {
            const width = parseFloat(svg.style.width);
            const height = parseFloat(svg.style.height);
            const canvas = document.createElement('canvas');
            canvas.width = width;
            canvas.height = height;
            const ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, width, height);
            parentElement.append(canvas);
            svg.remove();
            img.remove();
        };
        parentElement.append(img);
        callbacks.push(img.onload);
    }
    //await this.axios.all(callbacks);
    await Promise.all(callbacks);
},
  • 1伶氢、等待svg轉(zhuǎn)換完成之后趟径,再使用html2canvas 截圖:
async getPreviewImg() {
    //顯示加載圖標(biāo)
    this.$store.dispatch('showDataloader');
    await Promise.all([this.loadScript('/static/js/html2canvas.min.js'), this.convertSvg2Canvas()]);
    //允許跨域獲取,否則百度地圖不能生成圖片
    const opts = {
        useCORS: true
    }
    const canvasObj = await html2canvas(document.getElementById('poster_context'), opts);
    var context = canvasObj.getContext('2d');
    //防止圖片模糊的設(shè)置
    context.mozImageSmoothingEnabled = false;
    context.webkitImageSmoothingEnabled = false;
    context.msImageSmoothingEnabled = false;
    context.imageSmoothingEnabled = false;
    // png 格式圖片
    let imgType = "image/png";
    this.canvasSrc = canvasObj.toDataURL(imgType);
    this.previewShowFlag = true;
    //隱藏加載圖標(biāo)
    this.$store.dispatch('hideDataloader');
},
…… 

說明: 截屏的時候癣防,必須等待 svg 元素全部轉(zhuǎn)換成為 canvas 元素才可以蜗巧,否則是截取不成功的

loadScript 方法用來異步加載js, 本人是全局mixin的蕾盯, 下附 loadScript 方法:

// 動態(tài)加載 js 及 回調(diào)
async loadScript(src, callback = null) {
    await new Promise(resolve => {
        // 如果已經(jīng)加載了本js幕屹,直接調(diào)用回調(diào)
        if (this.checkScriptLoaded(src)) {
            return resolve(callback);
        }
        let scriptNode = document.createElement("script");
        scriptNode.setAttribute("type", "text/javascript");
        scriptNode.setAttribute("src", src);
        document.body.appendChild(scriptNode);
        if (scriptNode.readyState) { //IE 判斷
            scriptNode.onreadystatechange = () => {
                if (scriptNode.readyState == "complete" || scriptNode.readyState == 'loaded') {
                    return resolve(callback);
                }
            }
        } else {
            scriptNode.onload = () => resolve(callback);
        }
    })
},
// 檢測是否加載了 js 文件
checkScriptLoaded(src) {
    const scriptObjs = Array.from(document.getElementsByTagName('script'));
    return scriptObjs.find(ele => ele.src.includes(src));
},

2021-11-19 更新:

H5適配ios系統(tǒng)多行文本時截圖錯行的問題

近期使用html2canvas插件截圖時,發(fā)現(xiàn) iphone手機(jī)上,當(dāng)文本超過一行時望拖,截圖后文本排版錯亂了(第二行會縮進(jìn)一個字渺尘,而且右邊把空白都占滿了)

查看CanvasRenderer渲染文本的時候,發(fā)現(xiàn)有個 letter-spacing 屬性:

CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing) {
            var _this = this;
            if (letterSpacing === 0) {
                this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
            }
            else {
                var letters = toCodePoints(text.text).map(function (i) { return fromCodePoint(i); });
                letters.reduce(function (left, letter) {
                    _this.ctx.fillText(letter, left, text.bounds.top + text.bounds.height);
                    return left + _this.ctx.measureText(letter).width;
                }, text.bounds.left);
            }
        };

如上说敏,如果沒有設(shè)置 letter-spacing的樣式鸥跟,則會使用 canvas 的 fillText方法把文本渲染到畫布上。(但是fillText 對換行文字排版等支持不夠友好)盔沫, 設(shè)置了 letter-spacing屬性医咨,會使用measureText 方法把文字渲染到畫布,雖然不會溢出box架诞,但是第二行還是會有縮進(jìn)的問題……

解決方案就是: 盡量避免文本超過兩行拟淮,當(dāng)文本超過兩行的時候,每行頭尾用 行內(nèi)元素 包一下谴忧, 這樣就會當(dāng)做 html元素去渲染到畫布很泊,極限的可以把每個字符都用行內(nèi)元素包一下……

(to be continued …… )


他山之石

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市沾谓,隨后出現(xiàn)的幾起案子撑蚌,更是在濱河造成了極大的恐慌,老刑警劉巖搏屑,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異粉楚,居然都是意外死亡辣恋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門模软,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伟骨,“玉大人,你說我怎么就攤上這事燃异⌒粒” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵回俐,是天一觀的道長逛腿。 經(jīng)常有香客問我,道長仅颇,這世上最難降的妖魔是什么单默? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮忘瓦,結(jié)果婚禮上搁廓,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好境蜕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布蝙场。 她就那樣靜靜地躺著,像睡著了一般粱年。 火紅的嫁衣襯著肌膚如雪售滤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天逼泣,我揣著相機(jī)與錄音趴泌,去河邊找鬼。 笑死拉庶,一個胖子當(dāng)著我的面吹牛嗜憔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氏仗,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼吉捶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了皆尔?” 一聲冷哼從身側(cè)響起呐舔,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎慷蠕,沒想到半個月后珊拼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡流炕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年澎现,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片每辟。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡剑辫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渠欺,到底是詐尸還是另有隱情妹蔽,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布挠将,位于F島的核電站胳岂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏舔稀。R本人自食惡果不足惜旦万,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望镶蹋。 院中可真熱鬧成艘,春花似錦赏半、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秋冰,卻和暖如春仲义,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剑勾。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工埃撵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人虽另。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓暂刘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捂刺。 傳聞我的和親對象是個殘疾皇子谣拣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353