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