文档章节

xmlplus 组件设计系列之八 - 分隔框(DividedBox)

qudou
 qudou
发布于 2017/04/30 10:22
字数 1863
阅读 17
收藏 0
点赞 0
评论 0

分隔框

分隔框(DividedBox)是一种布局类组件,可以分为两种,其中一种叫水平分隔框(HDividedBox),另一种叫垂直分隔框(VDividedBox)。水平分隔框会将其子级分为两列,而垂直分隔框则会将其子级分为两行。列与列之间以及行与行之间一般都会有一条可以拖动的用以改变子级组件大小的分隔条。下面仅以垂直分隔框为例来介绍此类组件是如何设计以及实现的。

<img src="http://xmlplus.cn/img/dividedbox.png" class="img-responsive"/>

成品组件用例

按照以往的设计经验,我们可以先写出想像中的成品组件用例,这将有助于我们后续的进一步的设计与实现。垂直分隔框既然是布局类的组件,那么它也一定是一个容器,该容器包含了上述我们提到的三种子级组件。为了使用方便,我们不应该把分隔框也写进去,分隔框应该由组件内部实现的。经过分析,我们得到下面的一个应用示例:

// 08-01
Index: {
    css: "#index { width: 640px; height: 480px; box-sizing: border-box; border: 1px solid blue; }\
          #top, #bottom { width: 100%; height: 100%; background: #AAA; }",
    xml: "<VDividedBox id='index'>\
             <div id='top'/>\
             <div id='bottom'/>\
          </VDividedBox>"
}

该示例由一垂直分隔框组件包裹着两个 div 元素。这里分别设置两个 div 元素的宽高为父级的 100%,同时设置它们的背景色为灰色,这只是为了方便测试。另外,我们还需要考虑一个子框的初始比例分配问题。我们可以设置默认比例为 50:50,比例最好可以在组件实例化时静态指定,同时提供比例设置的动态接口。于是我们就有了下面的改进用例。

// 08-01
Index: {
    css: "#index { width: 640px; height: 480px; box-sizing: border-box; border: 1px solid blue; }\
          #top, #bottom { width: 100%; height: 100%; background: #AAA; }",
    xml: "<VDividedBox id='index' percent='30'>\
             <div id='top'/>\
             <div id='bottom'/>\
          </VDividedBox>",
    fun: function (sys, items, opts) {
        sys.top.on("click", e => sys.index.percent = 50);
    }
}

这个用例在垂直分隔框初始化时设置子框的初始比例分配为 30:70,当用户点击第一子框时,比例分配重新恢复为 50:50。不过要注意,这些比例分配指的是对排除分隔条所占用空间后剩余空间的比例分配。

设计与实现

现在让我们把注意力转移到组件的内部。我们先大致地确定组件基本的组成。直观地看,垂直分隔框显示包含三个组件部分:上子框部分、分隔条以及下子框部分。于是我们暂时可以得到下面的视图项部分:

// 08-01
<div id='hbox'>
    <div id='top'/>
    <div id='handle'/>
    <div id='bottom'/>
</div>`

下一步,确保垂直分隔框组件实例的子级部分被正确地映射到上子框 top 以及下子框 bottom。方法是先让所有的子级元素对象全部被添加到上子框 top 中,然后在函数项中将下子级元素添加到下子框 bottom 中。

// 08-01
VDividedBox: {
    xml: `<div id='hbox'>
            <div id='top'/>
            <div id='handle'/>
            <div id='bottom'/>
          </div>`,
    map: {appendTo: "top" },
    fun: function (sys, items, opts) {
        sys.bottom.elem().appendChild(this.last().elem());
    }
}

现在让我们来考虑下视图项的样式,对于顶层 div 元素,我们设置其定位方式为相对定位。对于子级的三个元素则设置为绝对定位。另外,把分隔条高度设置为 5px

// 08-01
VDividedBox: {
    css: `#vbox { 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: `<div id='vbox'>
            <div id='top'/>
            <div id='handle'/>
            <div id='bottom'/>
          </div>`,
    map: { appendTo: "top" },
    fun: function (sys, items, opts) {
        sys.bottom.elem().appendChild(this.last().elem());
    }
}

最后让我们看看如何响应分隔条的拖动事件,从而更改子框的分配比例。我们需要定义一个改变子框比例的函数,同时侦听分隔条的拖拽事件。下面是我们的一个实现。

// 08-01
VDividedBox: {
    // 视图项同上
    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 实施侦听。

进一步改进

上述组件在大部分情况下运作良好,但当我将 CodeMirror 组件整合进去时,出了点小问题。让我们现在做个小测试,写一个包含两个 CodeMirror 组件作为子级的垂直分隔框的应用实例。拖动分隔条,看会出现什么结果。

// 08-02
Index: {
    css: "#index { width: 640px; height: 480px; box-sizing: border-box; border: 1px solid #AAA; }",
    xml: "<VDividedBox id='index'>\
             <Editor id='top'/>\
             <Editor id='bottom'/>\
          </VDividedBox>"
},
Editor: {
    css: `.CodeMirror { height:100%; height: 100%; font-size: 14px; }
          .CodeMirror-gutters { border-right: 1px solid %border-color; background: linear-gradient...}
          #editor { position: relative; width: 100%; height: 100%; box-sizing: border-box; border: 1px solid #AAA; }`,
    map: { nofragment: true },
    opt: { lineNumbers: true, indentUnit: 4, mode: "text/html" }, 
    xml: "<div id='editor'/>",
    fun: function (sys, items, opts) {
        return CodeMirror(sys.editor.elem(), opts);
    }
}

如果你运行此示例,会发现分隔条失灵了,拖动分隔条子框比例不再出现变化。问题出在 CodeMirror 组件对象对拖拽事件进行了劫持,导致我们我组件内部收不到响应的事件。我们需要做些补丁才行,下面是改进后的组件:

// 08-03
VDividedBox: {
    css: `#vbox { 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: "<div id='vbox'>\
            <div id='top'/>\
            <div id='handle' draggable='true'/>\
            <div id='bottom'/>\
            <div id='mask'/>\
          </div>",
    map: { format: {"int": "percent"}, appendTo: "top" }, 
    fun: function (sys, items, opts) {
        var percent = 50;
        sys.handle.on("dragstart", function (e) {
            sys.mask.show();
            sys.vbox.on("dragover", dragover);
        });
        sys.vbox.on("dragend", function (e) {
            sys.mask.hide();
            e.stopPropagation();
            sys.vbox.off("dragover", dragover);
        });
        function dragover(e) {
            e.preventDefault();
            setPercent((e.pageY - sys.vbox.offset().top) / sys.vbox.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});
    }
}

为了解决问题,我们在组件中引用了额外的 div 元素对象 mask,此元素默认是不显示的。当拖动开始时,它才显示并覆盖住子框以及分隔条,而拖动一结束,它又隐藏掉。这样就避免了 CodeMirror 组件对象对拖拽事件的劫持。

结合水平分隔框使用

我们有了上述垂直分隔框的设计经验,搞个水平分隔框也就不是什么难事了,这里就不列出来了。这里主要是给出一个综合使用水平分隔框和垂直分隔框的示例。当然,在设计之初,我们并没有想到要这么使用。

// 08-04
Index: {
    css: `#index { width: 640px; height: 480px; box-sizing: border-box; border: 1px solid blue; }
          #left0, #right0, #left1, #right1 { width: 100%; height: 100%; background: #AAA; }`,
    xml: `<HDividedBox id='index'>
              <VDividedBox percent='30'>
                  <div id='left0'/><div id='right0'/>
              </VDividedBox>
              <VDividedBox percent='30'>
                  <div id='left1'/><div id='right1'/>
              </VDividedBox>
          </HDividedBox>`
}

© 著作权归作者所有

共有 人打赏支持
qudou

qudou

粉丝 5
博文 11
码字总数 14933
作品 2
福州
程序员
全栈 JavaScript 框架 xmlplus 1.5.9 发布

该版本主要对全局函数 clearLibrary 作了简化,另外对文档的一些文字错误进行了修正,同时保持了 gitHub 与 npm 版本之间的同步。 xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发...

qudou ⋅ 2017/04/30 ⋅ 16

全栈 JavaScript 框架--xmlplus

xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发前后端项目。 基于组件设计 在 xmlplus 中,组件是基本的构造块。评价组件设计好坏的一个重要标准是封装度。基于 xmlplus 设计的组...

qudou ⋅ 2017/04/21 ⋅ 1

xmlplus v1.5.8 正式发布 - 全栈 JavaScript 框架

全栈 JavaScript 框架 xmlplus v1.5.8 正式发布 xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发前后端项目。 基于组件设计 在 xmlplus 中,组件是基本的构造块。评价组件设计好坏...

qudou ⋅ 2017/04/27 ⋅ 2

JavaScript 框架 xmlplus 1.5.12 发布

JavaScript 框架 xmlplus 1.5.12 发布了。xmlplus 是一个设计非常独特 JavaScript 框架,用于快速开发前后端项目。 这个版本主要添加了一个全局接口 create。该函数是一个轻量的用于创建组件...

qudou ⋅ 2017/05/24 ⋅ 0

JavaScript 框架 xmlplus 1.5.14 发布

JavaScript 框架 xmlplus 1.5.14 发布了。更新如下: 将原先对于 xmldom 模块的依赖改为 exmldom。exmldom 模块是对 xmldom 模块的扩展,添加了事件支持。也就是说,你可以像前端那样在 dom ...

qudou ⋅ 2017/06/10 ⋅ 0

基于 angularjs 的 UI 组件--WebUI4Angular

WebUI4Angular 是基于 angularjs 实现的一套 UI 组件,组件需要使用 angularjs1.3 以上版本。参照了 UI Bootstrap 等组件的写法,主要对指令进行了自己的封装,我们希望通过 angular 的指令编...

wi4angular ⋅ 2015/06/17 ⋅ 1

结构型模式之八:MVC模式

Struts2实现了模型-视图、控制器设计模式,下面的类图演示了Struts2怎么实现了MVC组件。Action - modelResult - viewFilterDispatcher - controller 每个模块扮演的角色 Controler的作用是映...

刀狂剑痴 ⋅ 2015/08/27 ⋅ 0

Muse-UI 3.0.0-beta.1:所有组件重构,部分组件合并

Muse-UI 3.0.0 首个测试版发布: 所有组件重构,减小了整体包的体积 组件中增加了 和 属性,用于改变组件显示; 基于 vuejs 2.5 的 API,支持标签原生属性、事件(无需使用 监听, 另外部分属性...

王练 ⋅ 05/30 ⋅ 0

Fixwin电子窗体——介绍

概述 窗体是承载系统业务功能的基本单位,例如:XXX基本信息管理,XXX查询,XXX单据,XXX分析,XXX报表… 一个业务模块可以有多个窗体构成,例如:XXX信息管理【窗体1】,点击“添加”按钮弹...

彭博 ⋅ 2012/03/09 ⋅ 0

Silverlight/WPF 系列汇总

Silverlight 解谜游戏系列 -- Silverlight 3 · Silverlight 解谜游戏 之一 新建项目 · Silverlight 解谜游戏 之二 创建题板 · Silverlight 解谜游戏 之三 消除名单 · Silverlight 解谜游...

junwong ⋅ 2012/03/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Centos7重置Mysql 8.0.1 root 密码

问题产生背景: 安装完 最新版的 mysql8.0.1后忘记了密码,向重置root密码;找了网上好多资料都不尽相同,根据自己的问题总结如下: 第一步:修改配置文件免密码登录mysql vim /etc/my.cnf 1...

豆花饭烧土豆 ⋅ 今天 ⋅ 0

熊掌号收录比例对于网站原创数据排名的影响[图]

从去年下半年开始,我在写博客了,因为我觉得业余写写博客也还是很不错的,但是从2017年下半年开始,百度已经推出了原创保护功能和熊掌号平台,为此,我也提交了不少以前的老数据,而这些历史...

原创小博客 ⋅ 今天 ⋅ 0

LVM讲解、磁盘故障小案例

LVM LVM就是动态卷管理,可以将多个硬盘和硬盘分区做成一个逻辑卷,并把这个逻辑卷作为一个整体来统一管理,动态对分区进行扩缩空间大小,安全快捷方便管理。 1.新建分区,更改类型为8e 即L...

蛋黄Yolks ⋅ 今天 ⋅ 0

Hadoop Yarn调度器的选择和使用

一、引言 Yarn在Hadoop的生态系统中担任了资源管理和任务调度的角色。在讨论其构造器之前先简单了解一下Yarn的架构。 上图是Yarn的基本架构,其中ResourceManager是整个架构的核心组件,它负...

p柯西 ⋅ 今天 ⋅ 0

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 今天 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 2

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部