svg 編輯器系列(2)其實在以前已經寫過了,但寫得很差,因此此次重寫一下,順便也把示例代碼重寫了。git
本文主要講解一款 svg 編輯器的DOM結構,該如何分層以及這樣分層的緣由。DOM 結構主要參考了一款名爲 svgedit的開源 svg 編輯器。github
代碼用到了 svgjs 庫。不過應該仍是挺好懂的。web
演示連接:f-star.github.io/web-editor-…canvas
源碼地址:github.com/F-star/web-…bash
網頁截圖:編輯器
DOM 結構以下:ide
svg 編輯器
├── workarea (視口層)
└── svgcanvas (掛載層)
└── svgRoot (最上層的svg)
├── canvasBg (svg背景層)
├── svgcontent (繪製層)
| ├── layout1 (圖層一)
| ├── layout2 (圖層二)
| └── ...
├── guideLine (輔助線層)
複製代碼
div#workareasvg
顧名思義,該層爲編輯器的可視範圍。當 svgRoot 的高或寬大於該層的寬高時,會顯示滾動條。函數
div#svgcanvas工具
掛載層負責 svg 的掛載。它會保持寬高和 svgRoot 相同。你可能會奇怪這層到底有什麼用,直接掛載到適口層不也行嗎?沒錯,直接掛載在視口層也是可行的,但軟件設計須要考慮到一些可能會發生的狀況。這裏咱們加上了這層,是考慮到後期咱們可能會須要在 svgRoot 的同一層上添加一些 div 元素(畢竟svg中是沒法添加 div 元素的),好比說添加一些備忘錄。
svg#svgRoot
該元素爲 根部svg 元素,它並非真正繪製 svg 的載體,真正繪製 svg 圖形的地方是它的內嵌 svg 元素,即繪製層。咱們追加這層根部 svg 的目的,是爲了使超出內嵌 svg 範圍外的矢量圖形,仍然能顯示出來(svgRoot 比)。如圖所示:
這裏給個圖片
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。則有:
最後咱們用代碼繪製一個 path,並調用寫好的 showSelectedBox() 方法,顯示它的選中框。
這篇文章主要講述了 svg 編輯器的層次結構(DOM結構)設計和這樣設計的緣由,並簡單講述了 svg 編輯器初始化時須要作的事情。
下一篇系列文章的內容應該是講解如何進行工具(如選擇工具切換爲鋼筆工具)的激活和切換,以及如何配合事件響應函數實現其中一個簡單的工具功能。