js截圖并上傳到后端

業(yè)務(wù)場景是這樣的仿野,前端獲取某個部分截圖窃蹋,調(diào)用后端的圖片上傳服務(wù)卡啰,生成URL。這種業(yè)務(wù)場景大家應(yīng)該都遇到過警没,網(wǎng)上的方案(不準確的)也是一大把匈辱。
由于這個業(yè)務(wù)場景不需要加載外部資源,所以沒準備用htmlToCanvas杀迹。查閱各種blog亡脸,東拼西湊手寫了一個(失敗),后來找到了一個第三方庫dom-to-image浅碾,研究了它的源碼大州,成功寫了一個簡陋版的代碼。

dom轉(zhuǎn)化為圖片的流程

后端接口可以接受入?yún)⑹莃ase64的圖片垂谢。所以厦画,前端目標就是將dom轉(zhuǎn)化為png(base64格式)。根據(jù)網(wǎng)上的方案一般流程是這樣:


image.png

按照這種流程網(wǎng)上給出的代碼如下所示:

//此代碼僅在chrome測試下通過
function html2Svg (domStr) {  
            //創(chuàng)建模版字符串
            var svgXML=
            `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
                <foreignObject width="100%" height="100%">${generateXML(html)}</foreignObject>
             </svg>`
            //利用Blob創(chuàng)建svg
            var svg = new Blob([svgXML], {type: 'image/svg+xml'})
            //利用DOMURL.createObjectURL取出對象
            var url = window.URL.createObjectURL(svg);
            var img = new Image()
            img.src = url
            return img
        }

// 由于`foreignObject`只能引用XML文檔滥朱,
// 所以我們需要對DOM進行格式化
function generateXML (domStr) {  
        var doc = document.implementation.createHTMLDocument('');
            doc.write(html);
            doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
            doc = parseStyle(doc)  //函數(shù)定義在哪里根暑?這個函數(shù)要干什么?焚虱?給代碼都給不全的嗎购裙?
            console.log(doc)
            html = (new XMLSerializer).serializeToString(doc).replace('<!DOCTYPE html>','');
            return html
}

首先懵逼的是代碼中generateXML函數(shù)的parseStyle在哪里?行吧,大佬也沒有寫鹃栽。估計不重要躏率,去掉。然后直接復(fù)制代碼使用民鼓,發(fā)現(xiàn)圖片出不來??薇芝。OK,又查閱了更多blog并沒有什么用丰嘉。所以我找了成熟的第三方庫dom-to-image研究這個庫是怎么實現(xiàn)dom轉(zhuǎn)化為png的夯到。

真正的解決方案

1.dom生成svg沒有啥麻煩的,代碼一般可以這樣寫:



export const asArray = arrayLike => {
  let array = []
  let length = arrayLike.length
  for (let i = 0; i < length; i++) array.push(arrayLike[i])
  return array
}
export const getNodeCSSText = node => {
  let comStyle = window.getComputedStyle(node)

  if (comStyle.cssText) {
    node.style.cssText = comStyle.cssText
  } else {
    asArray(comStyle).forEach(function(name) {
      node.style.setProperty(
        name,
        comStyle.getPropertyValue(name),
        comStyle.getPropertyPriority(name)
      )
    })
  }

  return node
}

export const travelEveryNode = parentNode => {
  let children = asArray(parentNode.childNodes)

  if (children.length === 0) {
    if (parentNode instanceof Element) {
      getNodeCSSText(parentNode)
    }

    return parentNode
  }

  children.forEach(child => {
    travelEveryNode(child)
  })

  if (parentNode instanceof Element) {
    getNodeCSSText(parentNode)
  }

  return parentNode
}

export const toSvg = domId => {
  let dom = document.getElementById(domId)
  dom.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')

  travelEveryNode(dom)
  let wid = width(dom)
  let he = height(dom)

  let xml = new XMLSerializer().serializeToString(dom)

  let svgDom = `<svg xmlns="http://www.w3.org/2000/svg" width='${wid}' height='${he}'>
  <foreignObject x="0" y="0" width="100%" height="100%">
  ${xml}
  </foreignObject>
  </svg>`

  return `data:image/svg+xml;charset=utf-8,${svgDom}`
}

從dom轉(zhuǎn)化為svg的重點是饮亏,將元素的所有樣式都放在style中(不然樣式展示不出來)耍贾。所以,需要循環(huán)遍歷每個子節(jié)點路幸,對每一個子節(jié)點進行同樣的處理荐开。

2.svg轉(zhuǎn)化為image對象
要image對象渲染完成,才能從canvas中獲取dataurl.所以這一步要返回promise對象

export const svgToImage = svgString => {
  return new Promise(function(resolve, reject) {
    let image = new Image()
    image.onload = function() {
      resolve(image)
    }
    image.onerror = reject
    image.src = svgString
  })
}

3.image對象轉(zhuǎn)化為canvas獲取dataurl简肴。這里需要注意的一個細節(jié)點晃听,是canvas的長度和寬度要和被截圖的元素保持一致。

export const width = node => {
  let leftBorder = px(node, 'border-left-width')
  let rightBorder = px(node, 'border-right-width')
  return node.scrollWidth + leftBorder + rightBorder
}

export const height = node => {
  let topBorder = px(node, 'border-top-width')
  let bottomBorder = px(node, 'border-bottom-width')
  return node.scrollHeight + topBorder + bottomBorder
}

export const demo = async domId => {
  let svg = toSvg(domId)

  let dom = document.getElementById(domId)
  let wid = width(dom)
  let he = height(dom)
  svgToImage(svg).then(img => {
    setTimeout(() => {
      getCanvasUrl(img, wid, he)
    }, 1000)
  })

  // return await upload(url, true)
}


export const getCanvasUrl = (img, wid, he) => {
  let canvas = document.createElement('canvas')

  canvas.width = wid
  canvas.height = he
  canvas.getContext('2d').drawImage(img, 0, 0)
  return canvas.toDataURL()
}

getCanvasUrl獲取的是base64編碼的圖片砰识,可以直接傳遞給后端能扒。當(dāng)然dom-to-image的處理比我寫的代碼精細很多很多。我只是讀過它的源碼之后辫狼,摘取的主流程自己寫了一遍初斑。至少,我這個代碼是可用的??予借。
這告訴我一個道理越平,blog(普通質(zhì)量的blog)只能是一個入口频蛔,真正解決方案在源碼灵迫。

參考文檔:
dom-to-image源碼地址: https://github.com/tsayen/dom-to-image
并沒有解決我的問題的blog:http://www.reibang.com/p/17d7e5ddf10a

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秦叛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瀑粥,更是在濱河造成了極大的恐慌挣跋,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狞换,死亡現(xiàn)場離奇詭異避咆,居然都是意外死亡,警方通過查閱死者的電腦和手機修噪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門查库,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人黄琼,你說我怎么就攤上這事樊销。” “怎么了脏款?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵围苫,是天一觀的道長。 經(jīng)常有香客問我撤师,道長剂府,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任剃盾,我火速辦了婚禮腺占,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痒谴。我一直安慰自己衰伯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布闰歪。 她就那樣靜靜地躺著嚎研,像睡著了一般。 火紅的嫁衣襯著肌膚如雪库倘。 梳的紋絲不亂的頭發(fā)上临扮,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音教翩,去河邊找鬼杆勇。 笑死,一個胖子當(dāng)著我的面吹牛饱亿,可吹牛的內(nèi)容都是我干的蚜退。 我是一名探鬼主播闰靴,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钻注!你這毒婦竟也來了蚂且?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤幅恋,失蹤者是張志新(化名)和其女友劉穎杏死,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捆交,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡淑翼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了品追。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玄括。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肉瓦,靈堂內(nèi)的尸體忽然破棺而出遭京,到底是詐尸還是另有隱情,我是刑警寧澤风宁,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布洁墙,位于F島的核電站,受9級特大地震影響戒财,放射性物質(zhì)發(fā)生泄漏热监。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一饮寞、第九天 我趴在偏房一處隱蔽的房頂上張望孝扛。 院中可真熱鬧,春花似錦幽崩、人聲如沸苦始。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陌选。三九已至,卻和暖如春蹄溉,著一層夾襖步出監(jiān)牢的瞬間咨油,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工柒爵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留役电,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓棉胀,卻偏偏與公主長得像法瑟,于是被迫代替她去往敵國和親冀膝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • $HTML霎挟, HTTP窝剖,web綜合問題 1、前端需要注意哪些SEO 2氓扛、 的title和alt有什么區(qū)別 3枯芬、HT...
    Hebborn_hb閱讀 4,596評論 0 20
  • Data Visualization with D3 D3: SVG中的jQurey 1. Add Documen...
    王策北閱讀 760評論 0 2
  • 作者: 阮一峰www.ruanyifeng.com/blog/2018/08/svg.html 一论笔、概述 SVG ...
    grain先森閱讀 2,813評論 0 12
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫采郎、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,102評論 4 62
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,100評論 1 32