聊聊UI组件设计-Modal

在人机交互中,有一个概念叫做Modality,中文叫模态。模态,顾名思义,就是模拟。计算机可以模拟人通过各种通道接收的信息,比如视觉、听觉、触觉等等通道。视觉就通过显示器输出,听觉通过音响、触觉通过振动。同理,人也可以模拟计算机接收到的电信号,人可以通过键盘、触摸板等待设备来模拟0/1信号。 模态可以是但通道的,也可以是多通道的(比如玩游戏时有声音、视觉、和振动反馈)。今天我们要将的计算机软件中的Modal组件,就是计算机向人建立的单通道信息交互方式。 Modal在交互设计中的作用 具体到产品的交互设计中(交互设计和人机交互不是一回事,准确的说两者有交叉),Modal这个组件是很常用的,那么Modal在交互上的意义是什么呢? Modal的弹出,其实就是计算机和人之间建立了一个信息传递的通道,这个通道是独占的,在关闭这个通道之前,不能进行其他的交互。计算机程序建立这个通道,为的是传递一些信息。但传递信息有很多的方式,为什么要使用独占通道的Modal来传达呢? 因为Modal传递的信息,是为了让用户提供关键信息,这个信息的回复(Modal的输入)可以是是一个true or false的选择,也可以是较为复杂的数据结构。Modal是一个浮层,所以用户在Modal弹出时,不能再点击应用的其他部分。用户必须要做出决定,是输入信息,或者取消(关闭Modal)。因此,Modal会中断用户当前的工作流。 实现:Modal组件的特点 全局一般只能显示一个Modal,因为Modal显示时会需要用户做出决定后再消失(点击取消也是一种决定,即为对操作的否定)。 Modal一般提供确定和取消两个按钮。 Modal中标题和底部按钮直接的内容,一般是可以自由组合的。一种特殊情况是Modal中组合了input,那这种Modal类型被称为prompt。 Modal如果涉及异步的操作,则需要有一个confirm loading状态。这个状态下用户不能再次点击confirm。 这里推荐一篇很好的总结文章覆盖层设计(上)-对话框&浮层来自网易UEDC。系统的总结了交互设计中浮层相关的设计。 不同UI库的Modal设计 下面讲讲正题,Modal作为一个Web前端组件的设计方式。 根据Modal在前端代码中的调用方式,可以分为声明式和命令式两种。Modal的声明式使用是指在前端模板中声明Modal。命令式则是在前端代码中调用一个函数,来显式的调用Modal。 声明式 Ant Design中的Modal是典型的声明式组件。Modal被声明在模板中,在父组件初始化之时便存在了。 render() { return ( <div> <Modal title="Basic Modal" visible={this.state.visible} onOk={this.handleOk} onCancel={this.handleCancel} > <p>Some contents...</p> <p>Some contents...</p> <p>Some contents...</p> </Modal> </div> ); } Modal出现时只是visible这个flag被设置为true。而不是在那个时候初始化Modal组件。要在Modal中组合内嵌内容,只要在模板中的Modal标签中组合内容即可。在用户点击Modal的取消时,只要将visible设为false。所以这里不涉及Modal的销毁问题。Modal的回收是和父组件一起的。 命令式 Ant Design中的Modal组件也提供了几个静态的方法,用于在组件中手动初始化一个Modal,并且提供了destroy方法来手动销毁。 const modal = Modal.success({ title: 'This is a notification message', content: 'This modal will be destroyed after 1 second', }); Element UI中的Modal组件的API则是标准的命令式。 this.$prompt('请输入邮箱', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', inputPattern: /[\w!...

August 14, 2017

一个组件的诞生(下)

上一篇中我们实现了一个通用的组件模型。但这个模型在前端这种用户交互密集的应用场景下,会遇到更多的挑战。 数据驱动 你接到了这样一个需求,要在banner上加几个指示的圆点(indicator),当前页面是第几个页面,第几个圆点就会高亮。你想着这个需求很简单啊,刷刷刷就写好了。 HTML <div class="banner"> <li> <img src="{{ url1 }}"/> <img src="{{ url2 }}"/> </li> <div class="button-left"></div> <div class="button-right"></div> <div class="indicator"> <li class="item"></li> <li class="item"></li> <li class="item"></li> </div> </div> JS import * as _ from "utility" var banner = new Banner({ el: document.querySelector("template"), data: { index:0, url1: "/somewhere/1.jpg", url2: "/somewhere/2.jpg" }, events: { ".left-button.click": this.onSwitch.bind(this, 1), ".right-button.click": this.onSwitch.bind(this, -1) }, methods: { onSwitch: function(event, direction) { // 切换banner逻辑 data.index += direction var indicators = document....

September 30, 2016

一个组件的诞生(上)

你是一个新手前端工程师,今天你接到了一个需求,写一个banner。banner嘛,大家都知道,就是一个用来展示图片的东西,不同的图片可以滚动切换。 你心想着这很简单啊,于是刷刷刷写了一个。 JS var banner = { init: function() { this.addEvent(); }, addEvent: function() { var buttonLeft = document.querySelector(".button-left"); var buttonRight = document.querySelector(".button-right"); buttonLeft.addEventListener("click", function() { this.switchClassName(1) }) buttonRight.addEventListener("click", function() { this.switchClassName(-1) }) }, switchClassName: function(direction) { // 切换Class类名 } } banner.init(); HTML <div class="banner"> <li> <img src="1.jpg"></img> </li> <li> <img src="2.jpg"></img> </li> <li> <img src="3.jpg"></img> </li> <div class="button-left"></div> <div class="button-right"></div> </div> 正当你觉得自己可以刷知乎的时候,主管把你叫到一边,告诉你: 如果你这个页面上需要有两个banner,怎么办呢?复制粘贴吗? 如果在需要在某个时间点从页面上移除banner,怎么办呢?手动remove节点吗,销毁对象吗? 如果banner中图片的URL是从服务端API请求到的动态的数据,难道要用一个个用修改图片的src的方法来显示这些数据吗? 听到这里,你的内心是崩溃的。 面向对象大法 这时,你想起了大学时学过的面向对象。你把你的组件改成了这样: JS...

September 29, 2016