svg 項(xiàng)目實(shí)踐——添加可編輯的 svg 圖片

轉(zhuǎn)載自:LeapFE
https://segmentfault.com/a/1190000039754124
最近的項(xiàng)目中遇到一個(gè)需求,在頁面上添加一個(gè)可以自由拖拽、縮放、編輯顏色的 svg 圖片,項(xiàng)目中引用了兩個(gè)現(xiàn)有的插件,對(duì)插件進(jìn)行解讀之后略有心得,與大家分享一下。
自由拖拽縮放的節(jié)點(diǎn) —— react-rnd
說到拖拽,我們的第一反應(yīng)當(dāng)然是監(jiān)聽鼠標(biāo)事件來修改 dom 元素的位置,而縮放的話,則是在對(duì)元素邊界進(jìn)行操作時(shí)重新修正元素的 position 和 width、height 屬性。這部分就不予過多的贅述,有興趣的朋友可以參考下 react-rnd 這個(gè)插件,它引用了 react-draggable 和 react-resizeable 兩個(gè)插件來實(shí)現(xiàn)元素的拖拽和縮放。
此時(shí)我們理論上已經(jīng)實(shí)現(xiàn)了 dom 元素的拖拽和縮放,但是在添加了 svg 圖片之后我們很快發(fā)現(xiàn),由于外層的拖拽是由 document.addEventListener 來實(shí)現(xiàn)的,但是我們?cè)?mouseDown 的時(shí)候,鼠標(biāo)點(diǎn)擊的位置實(shí)際上是 svg 內(nèi)的 document。

那么這個(gè)時(shí)候應(yīng)該怎么處理呢?最簡(jiǎn)單的思路:把 svg 的 document 取出來,放到父級(jí)的 document 里面。
自由的 svg —— react-svg
有了把 svg 的 document 取出來的思路,我們很容易能找到一個(gè)插件:react-svg,它的實(shí)現(xiàn)思路與我們上面提到的完全一致,此處貼上它的核心代碼供各位查看:

此時(shí)我們發(fā)現(xiàn),我們添加的 svg 圖片已經(jīng)可以自由的拖拽和縮放了。另外由于 react-svg 還開放了一個(gè)屬性 beforeInjection,我們可以通過這個(gè)屬性來在 svg 掛載之前修改它的屬性,如 stroke、stroke-width、fill 等,因此我們可以更加靈活得處理我們的 svg 圖片。
svg 的縮放 —— preserveAspectRatio、vector-effect
我們很快又發(fā)現(xiàn)一個(gè)問題,svg 的縮放默認(rèn)是等比的,也就是說當(dāng)我們橫向拉長(zhǎng)圖片的時(shí)候,它并不會(huì)變大,只會(huì)橫向偏移居中。

這個(gè)時(shí)候我們就要用到 svg 自帶的一個(gè)屬性:preserveAspectRatio,用來表示是否強(qiáng)制進(jìn)行統(tǒng)一縮放,當(dāng)設(shè)置為 none 的時(shí)候,svg 圖片不會(huì)進(jìn)行強(qiáng)制統(tǒng)一縮放,如果需要,會(huì)縮放指定元素的圖形內(nèi)容,使元素的邊界完全匹配視圖矩形。如下圖。其他類型強(qiáng)制縮放可參考 MDN。

現(xiàn)在我們的 svg 終于可以自由縮放了,但是很快我們又發(fā)現(xiàn),把一個(gè) svg 放大之后,它的線條寬度也跟著變寬了,那么有沒有什么屬性來保持線條寬度不變呢?答案是有的,vector-effect,但是這個(gè)屬性只對(duì)部分 svg 元素( <circle>, <ellipse>, <foreignObject>, <image>, <line>, <path>, <polygon>, <polyline>, <rect>, <text>, <textPath> <tspan>, <use>)生效,而它的屬性有以下幾個(gè):
none 該值指定不應(yīng)用矢量效果,即,使用默認(rèn)的渲染行為,即首先用指定的繪畫填充形狀的幾何形狀,然后使用指定的繪畫描邊輪廓。
non-scaling-stroke 該值修改了筆觸的方式。通常,筆觸涉及在當(dāng)前用戶坐標(biāo)系中計(jì)算形狀路徑的筆觸輪廓,并用筆觸顏料(顏色或漸變)填充輪廓。該值的最終視覺效果是筆觸寬度不依賴于元素的變換(包括非均勻縮放和剪切變換)和縮放級(jí)別。
non-scaling-size 該值指定元素及其后代使用的特殊用戶坐標(biāo)系。盡管從宿主坐標(biāo)空間進(jìn)行任何轉(zhuǎn)換更改,該用戶坐標(biāo)系的比例也不會(huì)更改。但是,它沒有指定抑制旋轉(zhuǎn)和偏斜。同樣,它也不指定用戶坐標(biāo)系的原點(diǎn)。由于此值抑制了用戶坐標(biāo)系的縮放,因此它還具有 non-scaling-stroke 的特性。
non-rotation 該值指定元素及其后代使用的特殊用戶坐標(biāo)系。盡管從宿主坐標(biāo)空間發(fā)生任何變換更改,該用戶坐標(biāo)系的旋轉(zhuǎn)和傾斜仍被抑制。但是,它沒有指定抑制縮放。同樣,它也沒有指定用戶坐標(biāo)系的原點(diǎn)。
fixed-position 該值指定元素及其后代使用的特殊用戶坐標(biāo)系。盡管從宿主坐標(biāo)空間進(jìn)行任何轉(zhuǎn)換更改,用戶坐標(biāo)系的位置都是固定的。但是,它沒有指定抑制旋轉(zhuǎn),偏斜和縮放。當(dāng)同時(shí)指定了該矢量效果和 transform 屬性, transform 屬性將因該矢量效果而被消耗。
當(dāng)我們?cè)O(shè)置vector-effect="non-scaling-stroke"后,我們的svg終于看起來正常了~

尾聲
以上就是我們?cè)谧鼋o頁面上添加一個(gè)可以自由拖拽、縮放、編輯顏色的 svg 圖片時(shí)總結(jié)的一些東西,希望對(duì)各位有所幫助。另外我們還有一個(gè)沒有解決的問題,如果path的內(nèi)容是通過類似同心圓的方式來繪制圖形的時(shí)候,我們并沒有什么好的方法來保證縮放時(shí)候線條寬度的變化,如果有人有什么好的意見或者方法,可以聯(lián)系我交流指導(dǎo),可以知音樓搜曹文亮或者添加本人微信caowl_1013,謝謝。
