xmlplus 是一個JavaScript框架,用於快速開發前後端專案。這篇文章主要介紹了xmlplus佈局類別元件之分隔框,具有一定的參考價值,有興趣的小夥伴們可以參考一下
分隔框(pidedBox)是一種佈局類組件,可分為兩大類,其中一類叫水平分隔框(HpidedBox),另一類叫垂直分隔框(VpidedBox)。水平分隔框會將其子級分為兩列,而垂直分隔框則會將其子級分為兩行。列與列之間以及行與行之間一般都會有一條可以拖曳的用以改變子級組件大小的分隔條。以下僅以垂直分隔框為例來介紹此類元件是如何設計以及實現的。
成品元件案例
按照過去的設計經驗,我們可以先寫出想像中的成品元件用例,這將有助於我們後續的進一步的設計與實現。垂直分隔框既然是佈局類別的元件,那麼它也一定是容器,該容器包含了上述我們提到的三種子級元件。為了使用方便,我們不應該把分隔框也寫進去,分隔框應該由元件內部實現的。經過分析,我們得到下面的一個應用範例:
Example1: { css: "#example p { width: 80%; height: 80%; background: #AAA; }", xml: `<VpidedBox id="example"> <p id='top'/> <p id='bottom'/> </VpidedBox>` }
這個範例由一個垂直分隔框元件包裹著兩個 p 元素。這裡分別設定兩個 p 元素的寬高為父級的 80%,同時設定它們的背景色為灰色,這只是為了方便測試。另外,我們還需要考慮一個子框的初始比例分配問題。我們可以設定預設比例為 50:50,比例最好可以在元件實例化時靜態指定,同時提供比例設定的動態介面。於是我們就有了下面的改進用例。
Example2: { css: "#example p { width: 80%; height: 80%; background: #AAA; }", xml: `<VpidedBox id="example" percent='30'> <p id='top'/> <p id='bottom'/> </VpidedBox>`, fun: function (sys, items, opts) { sys.top.on("click", e => sys.example.percent = 50); } }
這個用例在垂直分隔框初始化時設定子框的初始比例分配為 30:70,當使用者點擊第一子框時,比例分配重新恢復為 50:50。不過要注意,這些比例分配指的是排除分隔條所佔用空間後剩餘空間的比例分配。
設計與實作
現在讓我們把注意力轉移到元件的內部。我們先大致確定組件基本的組成。直觀地看,垂直分隔框顯示包含三個組件部分:上子框部分、分隔條以及下子框部分。於是我們暫時可以得到下面的視圖項目部分:
VpidedBox: { xml: `<p id='hbox'> <p id='top'/> <p id='handle'/> <p id='bottom'/> </p>` }
下一步,確保垂直分隔框元件實例的子級部分被正確地對應到上子框 top 以及下子框 bottom。方法是先讓所有的子級元素物件全部被加入到上子框 top 中,然後在函數項中將下子級元素加入到下子框 bottom 中。
VpidedBox: { xml: `<p id='hbox'> <p id='top'/> <p id='handle'/> <p id='bottom'/> </p>`, map: {appendTo: "top" }, fun: function (sys, items, opts) { sys.bottom.elem().appendChild(this.last().elem()); } }
現在讓我們來考慮下視圖項目的樣式,對於頂層 p 元素,我們設定其定位方式為相對定位。對於子級的三個元素則設定為絕對定位。另外,把分隔條高度設定為 5px。
VpidedBox: { css: `#hbox { position:relative; width:100%; height:100%; box-sizing: border-box; } #top { top: 0; height: 30%; } #bottom { bottom: 0; height: calc(70% - 5px); } #top,#bottom { left: 0; right: 0; position: absolute; } #handle { height: 5px; width: 100%; position:absolute; left:0; top: 30%; z-index:11; cursor:row-resize; }`, xml: `<p id='hbox'> <p id='top'/> <p id='handle'/> <p id='bottom'/> </p>`, map: {appendTo: "top" }, fun: function (sys, items, opts) { sys.bottom.elem().appendChild(this.last().elem()); } }
最後讓我們看看如何回應分隔條的拖曳事件,從而更改子框的分配比例。我們需要定義一個改變子框比例的函數,同時偵聽分隔條的拖曳事件。下面是我們的一個實作。
VpidedBox: { // 视图项同上 map: { format: {"int": "percent"}, appendTo: "top" }, fun: function (sys, items, opts) { var percent = 50; sys.handle.on("dragstart", function (e) { sys.hbox.on("dragover", dragover); }); sys.hbox.on("dragend", function (e) { e.stopPropagation(); sys.hbox.off("dragover", dragover); }); function dragover(e) { e.preventDefault(); setPercent((e.pageY - sys.hbox.offset().top) / sys.hbox.height() * 100); } function setPercent(value) { sys.handle.css("top", value + "%"); sys.top.css("height", value + "%"); sys.bottom.css("height", "calc(" + (100 - value) + "% - 5px)"); } setPercent(opts.percent || percent); sys.bottom.elem().appendChild(this.last().elem()); return Object.defineProperty({}, "percent", {get: () => {return percent}, set: setPercent}); } }
上述程式碼的映射項目中有一項關於 percent 格式的設置,該設置確保了 percent 為整數數。另外函數項中對子框的比例設定用到了 css3 的 calc 計算函數,改函數在瀏覽器窗體改變大小時仍能運作。如果你希望能相容於更多的瀏覽器,你需要做更多的工作。另外注意,為了讓元件有好的效能表現,只有當使用者開始拖曳時,才對事件 dragover 實作偵聽。
進一步改進
讓我們現在做個小測試,寫一個包含兩個文字域作為子層級的垂直分隔框的應用實例。拖曳分隔條,看看會出現什麼結果。
Example3: { css: `#example textarea { width: 80%; height: 80%; }`, xml: `<VpidedBox id="example"> <textarea id='top'/> <textarea id='bottom'/> </VpidedBox>` }
在這個範例中,有時候分隔條會失靈,子框比例就不再隨著分隔條位置而改變。問題出在文字域對拖曳事件進行了劫持,導致我們我組件內部收不到回應的事件。我們需要做些補丁才行。
VpidedBox: { css: "#hbox { position:relative; width:100%; height:100%; box-sizing: border-box; }\ #top { top: 0; height: 30%; } #bottom { bottom: 0; height: calc(70% - 5px); }\ #top,#bottom { left: 0; right: 0; position: absolute; }\ #handle { height: 5px; width: 100%; position:absolute; left:0; top: 30%; z-index:11; cursor:row-resize; }\ #mask { width: 100%; height: 100%; position: absolute; display: none; z-index: 10; }", xml: "<p id='hbox'>\ <p id='top'/>\ <p id='handle' draggable='true'/>\ <p id='bottom'/>\ <p id='mask'/>\ </p>", map: { format: {"int": "percent"}, appendTo: "top" }, fun: function (sys, items, opts) { var percent = 50; sys.handle.on("dragstart", function (e) { sys.mask.show(); sys.hbox.on("dragover", dragover); }); sys.hbox.on("dragend", function (e) { sys.mask.hide(); e.stopPropagation(); sys.hbox.off("dragover", dragover); }); function dragover(e) { e.preventDefault(); setPercent((e.pageY - sys.hbox.offset().top) / sys.hbox.height() * 100); } function setPercent(value) { sys.handle.css("top", value + "%"); sys.top.css("height", value + "%"); sys.bottom.css("height", "calc(" + (100 - value) + "% - 5px)"); } setPercent(opts.percent || percent); sys.bottom.elem().appendChild(this.last().elem()); return Object.defineProperty({}, "percent", {get: () => {return percent}, set: setPercent}); } }
為了解決問題,我們在元件中引用了額外的 p 元素物件 mask,此元素預設是不顯示的。當拖曳開始時,它才會覆蓋住子框以及分隔條,而拖曳一結束,它又隱藏掉。這樣就避免了文字域對拖曳事件的劫持。
結合水平分隔框使用
我們有了上述垂直分隔框的設計經驗,搞個水平分隔框也就不是什麼難事了,這裡就不列出來了。這裡主要是給出一個綜合使用水平分隔框和垂直分隔框的範例。當然的設計之初,我們並沒有想到要這麼使用。
Example4: { css: `#example p { width: 100%; height: 100%; }`, xml: `<HpidedBox id='example'> <VpidedBox percent='30'> <p/><p/> </VpidedBox> <VpidedBox percent='30'> <p/><p/> </VpidedBox> </HpidedBox>` }
這個範例主要用來展示當分隔框巢狀使用時的表現。這個範例包含一個水平分隔框,該水平分隔框又包含兩個垂直分隔框,這種佈局在不少編輯器中是很常見的,我們這裡已經簡單高效地把它實現了。
本系列文章是基於 xmlplus 框架。如果你對 xmlplus 沒有太多了解,可以訪問 www.xmlplus.cn。這裡有詳盡的入門文件可供參考。
【相關推薦】
1. 免費js線上影片教學
3. php.cn獨孤九賤(3)-JavaScript影片教學
以上是JavaScript框架(xmlplus)元件的介紹(八)分隔框(DividedBox)的詳細內容。更多資訊請關注PHP中文網其他相關文章!