基于Web的svg编辑器(2)——层次结构设计(DOM结构)
svg 編輯器系列(2)其實(shí)在之前已經(jīng)寫過了,但寫得不好,所以這次重寫一下,順便也把示例代碼重寫了。
本文主要講解一款 svg 編輯器的DOM結(jié)構(gòu),該如何分層以及這樣分層的原因。DOM 結(jié)構(gòu)主要參考了一款名為 svgedit的開源 svg 編輯器。
代碼實(shí)現(xiàn)
代碼用到了 svgjs 庫。不過應(yīng)該還是挺好懂的。
演示鏈接:f-star.github.io/web-editor-…
源碼地址:github.com/F-star/web-…
網(wǎng)頁截圖:
總覽
DOM 結(jié)構(gòu)如下:
svg 編輯器 ├── workarea (視口層)└── svgcanvas (掛載層)└── svgRoot (最上層的svg)├── canvasBg (svg背景層)├── svgcontent (繪制層)| ├── layout1 (圖層一)| ├── layout2 (圖層二)| └── ...├── guideLine (輔助線層) 復(fù)制代碼層
視口層
div#workarea
顧名思義,該層為編輯器的可視范圍。當(dāng) svgRoot 的高或?qū)挻笥谠搶拥膶捀邥r,會顯示滾動條。
掛載層
div#svgcanvas
掛載層負(fù)責(zé) svg 的掛載。它會保持寬高和 svgRoot 相同。你可能會奇怪這層到底有什么用,直接掛載到適口層不也行嗎?沒錯,直接掛載在視口層也是可行的,但軟件設(shè)計(jì)需要考慮到一些可能會發(fā)生的情況。這里我們加上了這層,是考慮到后期我們可能會需要在 svgRoot 的同一層上添加一些 div 元素(畢竟svg中是無法添加 div 元素的),比如說添加一些備忘錄。
svgRoot
svg#svgRoot
該元素為 根部svg 元素,它并不是真正繪制 svg 的載體,真正繪制 svg 圖形的地方是它的內(nèi)嵌 svg 元素,即繪制層。我們追加這層根部 svg 的目的,是為了使超出內(nèi)嵌 svg 范圍外的矢量圖形,仍然能顯示出來(svgRoot 比)。如圖所示:
這里給個圖片
svg背景層
svg#canvasBg
它負(fù)責(zé)顯示出畫布的位置,一般設(shè)置為白色。
繪制層
svg#svgcontent
矢量圖形真正繪制的地方。導(dǎo)出的 svg 文件內(nèi)容即是這個元素下的所有內(nèi)容。注意要設(shè)置 overflow="visible"
繪制層的圖層
g#layout
類似 PhotoShop,我們引入了“圖層”的概念。圖層就像疊好的一張張半透明(軟件中其實(shí)是全透明)的白紙,你可以選擇任意一張進(jìn)行繪畫。另外上層的紙的不透明的部分,會擋住它的下層的相同區(qū)域的顯示。
輔助線層
作為一款編輯器,我們需要一些輔助線,提供一些選中效果以及一些交互。比如點(diǎn)擊一個圖形,就顯示一個包圍著它的矩形,我稱之為“選中框”,此外這個矩形上有一些控制點(diǎn),對它們進(jìn)行拖拽等操作,可以對當(dāng)前這個矩形進(jìn)行縮放或者旋轉(zhuǎn)。為了實(shí)現(xiàn)這些功能,輔助線是 svg 編輯器十分重要的部分。
但是為什么我們把輔助線層放到 根部svg 下,而不是直接放到繪制層呢?原因是在實(shí)現(xiàn)畫布“縮放”功能的時候,畫布的縮放其實(shí)是通過設(shè)置 svg#svgcontent 的 viewBox 屬性來實(shí)現(xiàn)的(后面的svg編輯器系列會詳細(xì)講解),這種縮放會導(dǎo)致元素的 stroke-width(線寬)也會跟著變大變小。這樣的話,用戶體驗(yàn)就很不好了。
放大還好,選中框的線條雖然很大,但被選中的那個元素也好大。當(dāng)如果是縮小的話,并縮小到很小的時候,就會有一個問題,那就是選中框也變細(xì)變小了,這樣選中框的一些變形控制點(diǎn)也不好點(diǎn)中了。
輔助線層下的元素一般來說,在編輯器初始化的時候就應(yīng)該進(jìn)行創(chuàng)建,并將其設(shè)置 style="display: none;"。比如說選中框,選中一個元素時,輔助線層下相關(guān)的元素一些屬性會被修改(如元素的位置),然后顯示出來;取消選中時,則將相關(guān)元素全部設(shè)置為不可見。我不建議直接刪除或添加這些元素,因?yàn)檫@會有性能損失。
初始化
const svgRoot = SVG('svgcanvas').id('svgroot'); // svg root const canvasBg = svgRoot.nested().id('canvasBg'); // svg 內(nèi)容的底色,寬高需和 svg content 同步 const svgContent = svgRoot.nested().id('svgcontent'); // svg 的真正內(nèi)容位置。 const draw = svgContent.group(); // svgContent 下創(chuàng)建一個 group,作為第一個“圖層”(類似ps的圖層概念)const guideLine = svgRoot.group().id('guideLine'); // 放置輔助線的父容器 const selectedBox = guideLine.group().id('selectedBox'); // 選中框相關(guān)輔助線 const selectedBoxOutline = selectedBox.polygon() // 選中框-矩形輪廓.fill('none').stroke({width: 1, color: '#4d84ff'}).hide();// 選中框的 6個 縮放控制點(diǎn) const scaleGrips = (() => { ... }) 復(fù)制代碼SVG('svgcanvas') 的意思是在 id 為 svgcanvas 的元素下創(chuàng)建一個根部 svg。
// 參數(shù)配置 const config = {bgcolor: '#fff',contentW: 517,contentH: 384, }// 初始化 svgContent.size(config.contentW, config.contentH).move(config.contentW, config.contentH); // 設(shè)置寬高和左上角坐標(biāo) canvasBg.size(config.contentW, config.contentH).move(config.contentW, config.contentH); svgRoot.size(config.contentW * 3, config.contentH * 3); workarea.scroll( (svgRoot.width() - workarea.w())/2, (svgRoot.height() - workarea.h())/2 ); // 滾動條拖到中間 canvasBg.rect('100%', '100%').fill(config.bgcolor); // canvasBg 添加 白色 rect,實(shí)現(xiàn)達(dá)到填充背景色效果 復(fù)制代碼svgcontent(繪制層)的寬為 w,高為 h。則有:
- svgroot 的寬為 w * 3, 高為 h * 3;
 - canvasbg 的寬為 w,高為 h;
 - workarea 的 scrollLeft 為 (w * 3 - workarea.width) / 2,(w * 3 - workarea.height) / 2。(使畫布位于視口層的正中心)
 
最后我們用代碼繪制一個 path,并調(diào)用寫好的 showSelectedBox() 方法,顯示它的選中框。
結(jié)尾
這篇文章主要講述了 svg 編輯器的層次結(jié)構(gòu)(DOM結(jié)構(gòu))設(shè)計(jì)和這樣設(shè)計(jì)的原因,并簡單講述了 svg 編輯器初始化時需要做的事情。
下一篇系列文章的內(nèi)容應(yīng)該是講解如何進(jìn)行工具(如選擇工具切換為鋼筆工具)的激活和切換,以及如何配合事件響應(yīng)函數(shù)實(shí)現(xiàn)其中一個簡單的工具功能。
總結(jié)
以上是生活随笔為你收集整理的基于Web的svg编辑器(2)——层次结构设计(DOM结构)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: html svg 在线编辑器,用于矢量图
 - 下一篇: Scala 下载安装配置