这一章将设计一个选项卡组件,选项卡组件在手持设备上用的比较多,下面是一个示意图:

选项卡组成

在具体实现之前,想像一下目标组件是如何使用的,对于设计会有莫大的帮助。通过观察,可以将选项卡组件分为容器部分和子项部分,正如下面的 XML 结构所展示的。

  1. <!-- 05-01 -->
  2. <Tabbar id="tabbar">
  3. <TabItem id="home" label="首页"/>
  4. <TabItem id="setting" label="设置"/>
  5. <TabItem id="logs" label="日志"/>
  6. <TabItem id="about" label="关于"/>
  7. </Tabbar>

现在我们把目光切换到选项卡组件的子项部分,来看看子项部分是如何分解的。通过示意图,你可以发现子项部分可以分解为子项容器以及包含一个图标和一个文本的子级部分。

  1. <!-- 05-01 -->
  2. <a id="tabitem">
  3. <Icon id="icon"/>
  4. <span id="label">首页</span>
  5. </a>

所以,现在我们的目标已经很明确了,主要设计三个组件:图标组件 Icon、选项卡组件的子项 TabItem 以及选项卡组件的容器 Tabbar。

结构图

由于该组件比较简单,所以可以将三种子组件放置在同一层级。但请注意,我们还有四个图标组件,可以创建一个子级用于容纳它们。下面给出我们的组件结构图:

  1. Tabbar/
  2. ├── Tabbar
  3. ├── TabItem
  4. └── Icon/
  5. ├── About
  6. ├── Home
  7. ├── Logs
  8. └── Setting

图标的实现

我们从最简单的开始,先看四个图标组件,图标组件主要通过封装 SVG 文本来实现,由于图标文本较长,所以这里仅截取每个图标文本的一段。

  1. // 05-01
  2. About: {
  3. xml: `<svg width="48" height="48" viewBox="0 0 1024 1024">
  4. <path d="M507.577907 23.272727C240.142852..."/>
  5. </svg>`
  6. },
  7. Home: {
  8. xml: `<svg width="48" height="48" viewBox="0 0 1024 1024">
  9. <path d="M949.082218 519.343245 508.704442..."/>
  10. </svg>`
  11. },
  12. Logs: {
  13. xml: `<svg width="48" height="48" viewBox="0 0 1024 1024">
  14. <path d="M576 125.344l32 0 0 64-32 0 0-64Z..."/>
  15. </svg>`
  16. },
  17. Setting: {
  18. xml: `<svg width="48" height="48" viewBox="0 0 1024 1024">
  19. <path d="M512 336.664c-96.68 0-175.336 78...."/>
  20. </svg>`
  21. }

请注意,这些图标位于虚拟目录 /icon 之下,也就是你要像下面这样导入:

  1. // 05-01
  2. xmlplus("ui", function (xp, $_, t) {
  3. $_().imports({Tabbar: {... }, TabItem: {...}});
  4. $_("icon").imports({--这里包含了四个图标组件--});
  5. });

下面来实现图标组件 Icon,这里的图标组件与上面是不同的,它会根据输入的图标类型实例化不同的图标。这样设计可以复用部分相同的代码,避免冗余。

  1. // 05-01
  2. Icon: {
  3. css: "#icon { width: 1.5em; height: 1.5em; display: inline-block; }",
  4. opt: { icon: "about" },
  5. xml: `<span id="icon"/>`,
  6. fun: function (sys, items, opts) {
  7. sys.icon.replace("icon/" + opts.icon).addClass("#icon");
  8. }
  9. }

该组件的函数项根据输入的图标类型创建图标组件并替换已有的 span 元素对象。注意,替换完后需要重新添加样式。

子项的实现

按从内到外的原则,接下来实现选项卡组件的子项 TabItem。对于此组件,需要在组件的映射项中做一次异名的属性映射,把 id 属性值映射给内部的图标组件的 icon 属性。

  1. // 05-01
  2. TabItem: {
  3. css: `a#tabitem { display: table-cell; overflow: hidden; width: 1%; height: 50px; text-align: center; ... }
  4. #label { display: block; font-size: .75em; overflow: hidden; text-overflow: ellipsis; -webkit-user-select: none; }
  5. a#primary { color: #337ab7; fill: currentColor; }`,
  6. map: {"attrs": { icon: "id->icon" } },
  7. xml: `<a id="tabitem">
  8. <Icon id="icon"/>
  9. <span id="label">首页</span>
  10. </a>`,
  11. fun: function (sys, items, opts) {
  12. sys.label.text(opts.label);
  13. function select(bool) {
  14. sys.tabitem[bool ? 'addClass' : 'removeClass']("#primary");
  15. }
  16. return Object.defineProperty({}, "selected", { set: select});
  17. }
  18. }

此组件提供了用于选项切换时选中与非选中状态之间切换的接口。以供选项卡容器使用。

选项卡的实现

最后来看下选项卡组件 Tabbar 的实现。该组件侦听了用户触击选项卡时的事件,在侦听器里主要做两件事:一是维持选项卡状态的切换;另一是派发一选项卡切换时的状态改变事件。

  1. // 05-01
  2. Tabbar: {
  3. css: `#tabbar { display: table; width: 100%; height: 50px; padding: 0; table-layout: fixed; -webkit-touch-callout: none; }
  4. #tabbar { z-index: 10; background-color: #f7f7f7; backface-visibility: hidden; }`,
  5. xml: `<nav id="tabbar"/>`,
  6. fun: function (sys, items, opts) {
  7. var sel = this.first();
  8. this.on("touchend", "./*[@id]", function (e) {
  9. sel.value().selected = false;
  10. (sel = this).value().selected = true;
  11. this.trigger("switch", this.toString());
  12. });
  13. if (sel) sel.value().selected = true;
  14. }
  15. }

至此,一个选项卡组件算是完成了,下面来看下具体的一个测试示例。注意,最好在 chrome 浏览器的移动模式下做测试,这样 touchend 事件才会生效。

  1. // 05-01
  2. Index: {
  3. xml: `<Tabbar id="index">
  4. <TabItem id="home" label="首页"/>
  5. <TabItem id="setting" label="设置"/>
  6. <TabItem id="logs" label="日志"/>
  7. <TabItem id="about" label="关于"/>
  8. </Tabbar>`,
  9. fun: function (sys, items, opts) {
  10. this.on("switch", (e, target) => console.log(target));
  11. }
  12. }

在组件 Index 中,你可以侦听来自选项卡的切换事件来做相应的操作。比如结合后续我们介绍的视图栈组件做页面之间的切换操作。

xmlplus 组件设计系列之五 - 选项卡的更多相关文章

  1. xmlplus 组件设计系列之零 - xmlplus 简介

    xmlplus 是什么 xmlplus 是博主写的一个 JavaScript 框架,用于快速开发前后端项目. xmlplus 基于组件设计,组件是基本的构造块.评价组件设计好坏的一个重要标准是封装度. ...

  2. xmlplus 组件设计系列之三 - 文本框

    文本框是页面中最常用的输入组件,它的默认使用方式如下: <input type='text'/> 当然,这里的 `type='text' 可以略去不写.大部分情况下,使用默认的文本框作为输 ...

  3. xmlplus 组件设计系列之二 - 按钮

    除了图标以外,按钮也许是最简单的组件了,现在来看看如何定义按钮组件. 使用原生按钮组件 在 xmlplus 中,HTML 元素也以组件的方式存在.所以,你可以直接通过使用 button 标签或者 in ...

  4. xmlplus 组件设计系列之一 - 图标

    网页上使用的图标分可为三种:文件图标.字体图标和 SVG 图标.对于文件图标,下面仅以 PNG 格式来说明. PNG 图标 对于 PNG 图标的引用,有两种方式.一种是直接由 HTML 元素 img ...

  5. xmlplus 组件设计系列之六 - 下拉刷新

    "下拉刷新"由著名设计师 Loren Brichter 设计,并应用于 Twitter 第三方应用 Tweetie 中.2010年4月,Twitter 收购 Tweetie 开发商 ...

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

    分隔框(DividedBox)是一种布局类组件,可以分为两类,其中一类叫水平分隔框(HDividedBox),另一类叫垂直分隔框(VDividedBox).水平分隔框会将其子级分为两列,而垂直分隔框则 ...

  7. xmlplus 组件设计系列之九 - 树(Tree)

    树形组件是一种具有层级结构的组件,广泛应用于各种场景.本章会实现一个简单的树形组件,尽管功能有限,但你可以通过扩展它来实现自己所需要的树形组件. 数据源 树形组件的数据源可以是 JSON 格式的数据对 ...

  8. xmlplus 组件设计系列之十 - 网格(DataGrid)

    这一章我们要实现是一个网格组件,该组件除了最基本的数据展示功能外,还提供排序以及数据过滤功能. 数据源 为了测试我们即将编写好网格组件,我们采用如下格式的数据源.此数据源包含两部分的内容,分别是表头数 ...

  9. xmlplus 组件设计系列之四 - 列表

    列表组件是极其常用的一类组件,是许多视图组件系统的必须包含的.列表可以做的很简单,只显示简洁的内容.列表也可以做的很复杂,用于展示非常丰富的内容. 组成元素 列表离不开列表项以及包含列表项的容器.下面 ...

随机推荐

  1. Eclipse添加tomcat出现 The Apache Tomcat installation at this directory is version 8.5.6. A Tomcat 8.0 installation is expected.

    打开tomcat安装目录:apache-tomcat-8.5.6\lib 找到catalina.jar 用解压缩工具打开 org/apache/catalina/util/ServerInfo.pro ...

  2. install g++ on windows

    install c++/g++ on windows     install c++/g++ on windows link 原文 1. 算是提示吧: Pick the drive and a fol ...

  3. python 的正则表达式 贪婪模式与懒惰模式

    正则表达式中用于表示匹配数量的元字符如下: ? 重复0次或1次,等同于{0,1} * 重复0次或更多次,等同于{0,} + 重复1次或更多次,等同于{1,} {n,} 重复n次及以上 上面的表示匹配次 ...

  4. ORM映射设计思想

    using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; usin ...

  5. 因为本地没有配置 localhost 导致的 eclipse 的奇葩问题

    因为电脑没有配置 127.0.0.1 localhost,已经碰到两次奇葩问题了. 问题一: 我的博文http://www.cnblogs.com/sonofelice/p/5143746.html中 ...

  6. Chapter 4. Working with Key/Value Pairs

    Chapter4 working with key/value pairs key/values pairs键值对是Spark中非常常见的一种数据类型(type),RDD有时经常操作键值对数据类型.第 ...

  7. iOS用户行为追踪——无侵入埋点

    本文章系作者原创文章,如需转载学习,请注明该文章的原始出处和网址链接.  在阅读的过程中,如若对该文章有不懂或值得优化的建议,欢迎大家加QQ:690091622 进行技术交流和探讨. 前言:  前几日 ...

  8. linux下大于2T的硬盘格式化方法

    我们先在超级用户模式下用fdisk -l命令查看挂载的硬盘设备,假设设备号为/dev/sdb,接下来我们使用parted命令来进行GPT分区:1. yum install parted -y# par ...

  9. javascript 类型的判断

    在平常写js代码,类型判断必不可少,那么我们常见有哪几种?看到了标题,先不看你会想到那些方法 ,常用呢些呢?那么今天我自己总结一些判断类型的判断,如有错,万望告知! 1:typeof 常用这种方法不错 ...

  10. 腾讯云数据库团队:PostgreSQL TOAST技术理解

    作者介绍:胡彬 腾讯云高级工程师 TOAST是"The Oversized-Attribute Storage Technique"的缩写,主要用于存储一个大字段的值.要理解TOA ...