者:西嵐
轉發鏈接:https://juejin.im/post/5dadd0236fb9a04de04d968e
.你是如何理解HTML語義的?
答:使用合適的標簽標示內容。優點在于標簽語義化有利于搜索引擎建立索引進行抓 取,有助于構建良好的HTML結構,便于團隊開發和維護。
2.meta viewport 是做什么用的,怎么寫?
答:meta表示不能被HTML的其它元素(link,script,base, style, title)之一表示的任何元素信息。viewpoint讓web開發者控制視口的尺寸及比例,移動設備的viewpoint指設備屏幕上用來展示網頁的那一塊區域,也就是瀏覽器上用來展示網頁的那部分,可能比瀏覽器的可視區大,也可能比瀏覽器可視區域小,一般情況,比瀏覽器可視區域大。屬性包括width、height、initial-scale、maximum-scale、minimum-scale,使用方式是
<meta name="viewpoint" content="width=device-width, initial-scale=1, maximum-scale=1">
3.canvas 元素是干什么的?
答: canvas是用來繪制圖形的HTML元素。
4.html5新特性?如何處理HTML5新標簽的瀏覽器兼容問題?如何區分 HTML 和 HTML5?
答:html5新特性:
解決兼容性的方法:
<!--[if lt IE 9]> <script> src="http://html5shim.googlecode.com/svn/trunk/html5.js"</script> <![endif]-->
HTML和HTML5
html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml">
html5:
<!doctype html>
<div id="header"></div>
html5: 具有結構語義
<header></header>
5.Doctype作用?標準模式與兼容模式各有什么區別?
答: Doctype是document type(文檔類型),告訴瀏覽器解析器采用哪種規范(html、xhtml)來解析頁面,Doctype不存在或格式錯誤的情況下,采用兼容模式。
標準模式(嚴格模式)展示的支持最新標準的網頁。兼容模式(松散模式或怪異模式)展示的是兼顧傳統瀏覽器的網頁,向后兼容老式瀏覽器。
具體區別:
6.用戶訪問頁面到最終渲染的整個過程?
用戶輸入url,瀏覽器向服務器發送請求,獲取html,然后進入HTML渲染機制。首先,根據HTML生成DOM樹;其次,根據css和js重排頁面 https://segmentfault.com/a/1190000009317496
7.你對頁面進行性能優化的思路和思想是什么?
答: 減少http請求; 減少DOM操作,避免不必要的重繪和重排;壓縮文件體積;采用CDN;
者開源了一個Web思維導圖,在做導出為圖片的功能時走了挺多彎路,所以通過本文來記錄一下。
思維導圖的節點和連線都是通過 svg渲染的,作為一個純 js 庫,我們不考慮通過后端來實現,所以只能思考如何通過純前端的方式來實現將svg或html轉換為圖片。
我們都知道 img 標簽可以顯示 svg,然后 canvas 又可以渲染 img,那么是不是只要將svg渲染到img標簽里,再通過canvas導出為圖片就可以呢,答案是肯定的。
const svgToPng=async (svgStr)=> {
// 轉換成blob數據
let blob=new Blob([svgStr], {
type: 'image/svg+xml'
})
// 轉換成data:url數據
let svgUrl=await blobToUrl(blob)
// 繪制到canvas上
let imgData=await drawToCanvas(svgUrl)
// 下載
downloadFile(imgData, '圖片.png')
}
svgStr是要導出的svg字符串,比如:
然后通過Blob構造函數創建一個類型為image/svg+xml的blob數據,接下來將blob數據轉換成data:URL:
const blobToUrl=(blob)=> {
return new Promise((resolve, reject)=> {
let reader=new FileReader()
reader.onload=evt=> {
resolve(evt.target.result)
}
reader.onerror=err=> {
reject(err)
}
reader.readAsDataURL(blob)
})
}
其實就是base64格式的字符串。
接下來就可以通過img來加載,并渲染到canvas里進行導出:
const drawToCanvas=(svgUrl)=> {
return new Promise((resolve, reject)=> {
const img=new Image()
// 跨域圖片需要添加這個屬性,否則畫布被污染了無法導出圖片
img.setAttribute('crossOrigin', 'anonymous')
img.onload=async ()=> {
try {
let canvas=document.createElement('canvas')
canvas.width=img.width
canvas.height=img.height
let ctx=canvas.getContext('2d')
ctx.drawImage(img, 0, 0, img.width, img.height)
resolve(canvas.toDataURL())
} catch (error) {
reject(error)
}
}
img.onerror=e=> {
reject(e)
}
img.src=svgUrl
})
}
canvas.toDataURL()方法返回的也是一個base64格式的data:URL字符串:
最后就可以通過a標簽來下載:
const downloadFile=(file, fileName)=> {
let a=document.createElement('a')
a.href=file
a.download=fileName
a.click()
}
實現很簡單,效果也不錯,不過這樣就沒問題了嗎,接下來我們插入兩張圖片試試。
第一張圖片是使用base64的data:URL方式插入的,第二張圖片是使用普通url插入的:
導出結果如下:
可以看到,第一張圖片沒有問題,第二張圖片裂開了,可能你覺得同源策略的問題,但實際上換成同源的圖片,同樣也是裂開的,解決方法很簡單,遍歷svg節點樹,將圖片都轉換成data:URL的形式即可:
// 操作svg使用了@svgdotjs/svg.js庫
const transfromImg=(svgNode)=> {
let imageList=svgNode.find('image')
let task=imageList.map(async item=> {
// 獲取圖片url
let imgUlr=item.attr('href') || item.attr('xlink:href')
// 已經是data:URL形式不用轉換
if (/^data:/.test(imgUlr)) {
return
}
// 轉換并替換圖片url
let imgData=await drawToCanvas(imgUlr)
item.attr('href', imgData)
})
await Promise.all(task)
return svgNode.svg()// 返回svg html字符串
}
這里使用了前面的drawToCanvas方法來將圖片轉換成data:URL,這樣導出就正常了:
到這里,將純 svg 轉換為圖片就基本沒啥問題了。
svg提供了一個foreignObject標簽,可以插入html節點,實際上,筆者就是使用它來實現節點的富文本編輯效果的:
接下來使用前面的方式來導出,結果如下:
明明顯示沒有問題,導出時foreignObject內容卻發生了偏移,這是為啥呢,其實是因為默認樣式的問題,頁面全局清除了margin和padding,以及將box-sizing設置成了border-box:
那么當svg存在于文檔樹中時是沒有問題的,但是導出時使用的是svg字符串,是脫離于文檔的,所以沒有這個樣式覆蓋,那么顯示自然會出現問題,知道了原因,解決方法有兩種,一是遍歷所有嵌入的html節點,手動添加內聯樣式,注意一定要給所有的html節點都添加,只給svg、foreignObject或最外層的html節點添加都是不行的;第二種是直接在foreignObject標簽里添加一個style標簽,通過style標簽來加上樣式,并且只要給其中一個foreignObject標簽添加就可以了,兩種方式看你喜歡哪種,筆者使用的是第二種:
const transformForeignObject=(svgNode)=> {
let foreignObjectList=svgNode.find('foreignObject')
if (foreignObjectList.length > 0) {
foreignObjectList[0].add(SVG(`<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>`))
}
return svgNode.svg()
}
導出結果如下:
可以看到,一切正常。
關于兼容性的問題,筆者測試了最新的chrome、firefox、opera、safari、360急速瀏覽器,運行都是正常的。
前面介紹的是筆者目前采用的方案,看著實現其實非常簡單,但是過程漫長且坎坷,接下來,開始我的表演。
對于svg的操作筆者使用的是svg.js庫,創建富文本節點的核心代碼大致如下:
import { SVG, ForeignObject } from '@svgdotjs/svg.js'
let html=`<div>節點文本</div>`
let foreignObject=new ForeignObject()
foreignObject.add(SVG(html))
g.add(foreignObject)
SVG方法是用來將一段html字符串轉換為dom節點的。
在chrome瀏覽器和opera瀏覽器上渲染非常正常,但是在firefox瀏覽器上foreignObject標簽的內容完全渲染不出來:
檢查元素也看不出有任何問題,并且神奇的是只要在控制臺元素里編輯一下嵌入的html內容,它就可以顯示了,百度搜索了一圈,也沒找到解決方法,然后因為firefox瀏覽器占有率并不高,于是這個問題就擱淺了。
chrome瀏覽器雖然渲染是正常的:
但是使用前面的方式導出時foreignObject標簽內容卻是跟在firefox瀏覽器里顯示一樣是空的:
firefox能忍這個不能忍,于是嘗試使用一些將html轉換為圖片的庫。
使用html2canvas:
import html2canvas from 'html2canvas'
const useHtml2canvas=async (svgNode)=> {
let el=document.createElement('div')
el.style.position='absolute'
el.style.left='-9999999px'
el.appendChild(svgNode)
document.body.appendChild(el)// html2canvas轉換需要被轉換的節點在文檔中
let canvas=await html2canvas(el, {
backgroundColor: null
})
mdocument.body.removeChild(el)
return canvas.toDataURL()
}
html2canvas可以成功導出,但是存在一個問題,就是foreignObject標簽里的文本樣式會丟失:
這應該是html2canvas的一個bug,不過看它這issues數量和提交記錄:
指望html2canvas改是不現實的,于是又嘗試使用dom-to-image:
import domtoimage from 'dom-to-image'
const dataUrl=domtoimage.toPng(el)
發現dom-to-image更不行,導出完全是空白的:
并且它上一次更新時間已經是五六年前,所以沒辦法,只能回頭使用html2canvas。
后來有人建議使用dom-to-image-more,粗略看了一下,它是在dom-to-image庫的基礎上修改的,嘗試了一下,發現確實可以,于是就改為使用這個庫,然后又有人反饋在一些瀏覽器上導出節點內容是空的,包括firefox、360,甚至chrome之前的版本都不行,筆者只能感嘆,太難了,然后又有人建議使用上一個大版本,可以解決在firefox上的導出問題,但是筆者試了一下,在其他一些瀏覽器上依舊存在問題,于是又在考慮要不要換回html2canvas,雖然它存在一定問題,但至少不是完全空的。
用的人多了,這個問題又有人提了出來,于是筆者又嘗試看看能不能解決,之前一直認為是firefox瀏覽器的問題,畢竟在chrome和opera上都是正常的,這一次就想會不會是svgjs庫的問題,于是就去搜它的issue,沒想到,還真的搜出來了issue,大意就是因為通過SVG方法轉換的dom節點是在svg的命名空間下,也就是使用document.createElementNS方法創建的,導致部分瀏覽器渲染不出來,歸根結底,這還是不同瀏覽器對于規范的不同實現導致的:
你說chrome很強吧,確實,但是無形中它阻止了問題的暴露。
知道了原因,那么修改也很簡單了,只要將SVG方法第二個參數設為true即可,或者自己來創建節點也可以:
foreignObject.add(document.createElemnt('div'))
果然,在firefox瀏覽器上正常渲染了。
解決了在firefox瀏覽器上foreignObject標簽為空的問題后,自然會懷疑之前使用img結合canvas導出圖片時foreignObject標簽為空會不會也是因為這個問題,同時了解了一下dom-to-image庫的實現原理,發現它也是通過將dom節點添加到svg的foreignObject標簽中實現將html轉換成圖片的,那么就很搞笑了,我本身要轉換的內容就是一個嵌入了foreignObject標簽的svg,使用dom-to-image轉換,它會再次把傳給它的svg添加到一個foreignObject標簽中,這不是套娃嗎,既然dom-to-image-more能通過foreignObject標簽成功導出,那么不用它必然也可以,到這里基本確信之前不行就是因為命名空間的問題。
果然,在去掉了dom-to-image-more庫后,重新使用之前的方式成功導出了,并且在firefox、chrome、opera、360等瀏覽器中都不存在問題,兼容性反而比dom-to-image-more庫好。
雖然筆者的實現很簡單,但是dom-to-image-more這個庫實際上有一千多行代碼,那么它到底多做了些什么呢,點個關注,我們下一篇文章再見。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。