阅读更多

2顶
1踩

Web前端
有限状态机(FSM)(维基百科)是设计和实现事件驱动程序内复杂行为组织原则的有力工具。

早在2007年,IBM的工程师就提出在JavaScript中使用有限状态机来实现组件的方法,原文地址如下:《JavaScript 中的有限状态机》

现在结合KISSY等现代JS库和框架提供的强大的自定义事件的功能,我们可以利用有限状态机设计出代码层次清晰,结构优雅的前端交互组件。

今天,我们会通过设计并实现一个下拉选择(模拟select)组件来一步步说明如何利用FSM和KISSY来设计和实现一个有复杂行为的交互组件。

我们的工作会分成三个步骤来进行:

  • 第一步:设计组件状态,用户行为和组件行为
  • 第二步:通过代码来描述设计出来的内容
  • 第三步:实现一个有限状态机让组件工作起来
第一步:设计阶段

首先,我们需要确定组件的状态和状态间的转换关系。通过对组件可能会发生的行为进行研究,我们为组件设计了以下三个状态:

1.  收起状态(fold)


组件的初始状态,用户可能会进行以下操作:

  • 展开下拉框(unfoldmenu)转移到展开状态(unfold)
2.  展开状态(unfold)


用户展开下拉框的状态,用户可能会进行以下操作:

  • 收起下拉框(foldmenu)转移到收起状态(fold)
  • 鼠标经过选项(overitem)转移到高亮状态(highlight)
3.  高亮状态(highlight)


鼠标经过选项时,高亮经过的选项,用户可能会进行以下操作:

  • 收起下拉框(foldmenu)转移到收起状态(fold)
  • 点击选项(clickitem)转移到收起状态(fold)
  • 鼠标经过选项(overitem)转移到高亮状态(highlight)
以上就是这个小组件可能会有的三种状态,用一个状态转换图来表示如下:



  • 在状态描述中包含了触发状态发生转移的动作(事件)
  • 可以很明显的看出这些事件并不是浏览器中原生的事件。
  • 这里,我们使用自定义事件来描述用户的行为,这样我们可以使得用户行为和组件行为的逻辑完全分离,代码将会更容易理解和维护。

定义用户行为:

在这个组件里,我们有以下四种用户行为:

  • 展开下拉框(unfoldmenu):鼠标点击橙色区域时触发
  • 收起下拉框(foldmenu):鼠标离开组件区域达到2秒,点击橙色区域,点击组件外部区域
  • 点击选项(clickitem):点击下拉框中的某个选项
  • 鼠标经过选项(overitem):鼠标经过下拉框中的某个选项

定义组件行为:

在状态转移的过程中,组件本身会有很多动作,如显示下拉框等,我们接下来在上面的状态图中加入转移过程中组件的动作。


  • fold():收起下拉框
  • unfold():展开下拉框
  • highlightItem():高亮某个选项
  • selectItem():选中某个选项,并把值填充到橘黄色区域
第二步:实现阶段(基于KISSY实现)

全局变量:S=KISSY, D=S.DOM, E=S.Event

1.  描述状态

跟设计过程一样,我们需要用一个结构来描述状态的转移以及转移过程中的动作。我们在这里使用对象来描述:

"fold":{
    unfoldmenu:function(event){
        _this.unfold();
        return "unfold";
    }
}


如上面这段代码就描述了在fold状态下,可以触发unfoldmenu这个用户行为来转移到unfold状态,我们通过函数返回值的形式来通知FSM下一步的状态。这样,我们就可以通过这种形式描述所有的状态,结构如下:

states:{
    //收起(初始状态)
    "fold":{
        unfoldmenu:function(event){
            _this.unfold();
            return "unfold";
        }
    },
    //展开状态
    "unfold":{
        foldmenu:function(event){
            _this.fold();
            return "fold";
        },
        overitem:function(event){
            _this.highlightItem(event.currentItem);
            return "highlight";
        }
    },
    //高亮状态
    "highlight":{
        foldmenu:function(event){
            _this.fold();
            return "fold";
        },
        //选中条目
        clickitem:function(event){
            _this.selectItem(event.currentItem);
            return "fold";
        },
        overitem:function(event){
            _this.highlightItem(event.currentItem);
            return "highlight";
        }
    }
}


在定义好状态后,我们还需要设定一个初始状态:

initState:"fold"


2.  描述用户行为

我们使用一个方法来描述用户行为,即驱动FSM发生状态转移的事件:

"foldmenu":function(fn){
    var timeout;
    E.on(_this.container,"mouseleave",function(e){
        if(timeout)clearTimeout(timeout);
        timeout = setTimeout(function(){
            fn();
        },1000);
    });
    E.on([_this.container,_this.slideBox],"mouseenter",
    function(e){
        if(timeout)clearTimeout(timeout);
    });
    E.on("body","click",function(e){
        var target = e.target;
        if(!D.get(target,_this.container)){
            if(timeout)clearTimeout(timeout);
            fn();
        }
    });
}


如上面这个代码就定义了foldmenu这个用户行为,同时,FSM会自动将它定义为一个自定义事件,我们通过传入的回调函数fn来通知FSM触发这个事件的时机。

通过上边的例子可以看出,我们可以将一个很复杂的动作定义为一个用户行为,也可以将几个不同的动作定义为一个用户行为,将用户行为和组件的动作彻底分开。

与状态相同,我们也将所有的用户行为放在一个对象中。

events:{
    "unfoldmenu":function(fn){
    },
    "foldmenu":function(fn){
    },
    "overitem":function(fn){
    },
    "clickitem":function(fn){
    }
}

3.  描述组件行为

由于组件行为一般都包含对组件本身的一些直接操作,可以作为API开放给用户使用,因此我们把描述组件行为的方法放在组件的prototype上,这部分代码如下:

S.augment(SlideMenu,S.EventTarget,{

    setText:function(){
        var _this = this,
        select = _this.select;
        D.html(select,_this.text);
    },

    unfold:function(){
        var _this = this,
        slideBox = _this.slideBox;
        if(!_this.isFold)return;
        _this.isFold = false;
        D.show(slideBox);
    },

    fold:function(){
        var _this = this,
        options = _this.options,
        slideBox = _this.slideBox;
        if(_this.isFold)return;
        D.removeClass(options,"hover");
        _this.isFold = true;
        D.hide(slideBox);
    },

    highlightItem:function(curItem){
        var _this = this,
        options = _this.options;
        D.removeClass(options,"hover");
        D.addClass(curItem,"hover");
    },

    selectItem:function(curItem){
        var _this = this,
        value = D.attr(curItem,"data-value"),
        text = D.attr(curItem,"data-text");
        _this.value = value;
        _this.text = text;
        _this.setText()
        _this.fold();
        _this.fire("select",{
            value:value,
            text:text
        });
    }
});


第三步:实现有限状态机(基于KISSY实现)

前面我们定义了组件的状态,用户行为,以及组件本身的动作,接下来我们来实现一个有限状态机(FSM),让整个组件工作起来。

通过上面实现的代码,我们可以看出FSM的输入有以下三个:

  • 初始状态
  • 状态描述对象
  • 用户行为描述对象

代码结构如下:

initState:"fold",
states:{
    //收起(初始状态)
    "fold":{
    },
    //展开状态
    "unfold":{
    },
    //高亮状态
    "highlight":{
    }
},

events:{
    "unfoldmenu":function(fn){
    },
    "foldmenu":function(fn){
    },
    "overitem":function(fn){
    },
    "clickitem":function(fn){
    }
}


FSM需要2个功能:

  • 将用户行为与自定义事件相关联(defineEvents)
  • 在用户行为发生时(即触发自定义事件时),根据状态描述对象来转移状态(handleEvents)

代码如下:

functionFSM(config){
    this.config = config;
    this.currentState = this.config.initState;
    this.nextState = null;
    this.states = this.config.states;
    this.events = this.config.events;
    this.defineEvents();
}

var proto = {
    //事件驱动状态转换(表现层)
    handleEvents:function(event){
        if(!this.currentState)return;

	var actionTransitionFunction =
	this.states[this.currentState][event.type];

        if(!actionTransitionFunction)return;
        

	var nextState = actionTransitionFunction
	.call(this,event);

        this.currentState = nextState;
    },

    //定义事件 (行为层)
    defineEvents:function(){
        var _this = this,
        events = this.events;
        for(k in events){
            (function(k){
                var fn = events[k];
                fn.call(_this,function(event){
                    _this.fire(k,event);
                });
                _this.on(k,_this.handleEvents);
            })(k)
        }
    }
}
S.augment(FSM, S.EventTarget, proto);


然后,只需要实例化一个FSM即可

new FSM({
     initState:"fold",
     states:{...},
     events:{...}
});


总结

使用FSM模式设计和实现交互组件,可以获得以下特性:

  • 交互逻辑清晰
  • 用户行为和组件行为完全分离,代码具有良好的分层结构
  • 对设计具有良好的纠错特性,当设计上对状态和状态的转移有遗漏时,在实现阶段很容易流程出现走不通的情况,可以促进交互设计对细节的补充。
源码:https://github.com/yhanwen/fsm
  • 大小: 1.2 KB
  • 大小: 3.7 KB
  • 大小: 5 KB
  • 大小: 33.8 KB
  • 大小: 40.5 KB
来自: TaobaoUED
2
1
评论 共 1 条 请登录后发表评论
1 楼 fyland 2012-10-12 10:30
1、KISSY的颗粒度有点细,造成很多一些不必要的连接请求;

2、KISSY的UI是哪个程序员美工做出来的?

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 基于有限状态机与交互组件设计与实现

    NULL 博文链接:https://andrewstz.iteye.com/blog/2037291

  • 基于SSM的校园二手交易平台的设计与实现

    基于SSM的校园二手交易平台 完整的设计报告在后面 提供2种方案: 1 Spring Boot项目 喜欢就点一下star哟,谢谢亲的支持 https://github.com/wsk1103/Used-Trading-Platform2 下载地址: ...

  • 软件工程与计算-11-人机交互设计

    软件工程与计算-11-人机交互设计

  • 人机交互-10-交互设计模型

    人机交互-10-交互设计模型

  • 基于android音乐播放器的设计与实现

    我觉得应该把我们学过的专业知识总结出来,应用在项目中,我的android音乐播放器就是java和android这一块最好的总结,它使用eclipse adt编写java和android代码及其布局文件实现界面与数据的交互,通过观察者设计模式...

  • 面向组件,状态机,消息驱动,三合一的编程模型

    在实践中,发现面向组件,状态机,消息驱动。如果整合起来的模型,能够更为自然和简单的进行抽象。当然这些都是以面向对象为基础,更进一步的抽象扩展。本文,先会分别介绍一下,面向组件,状态机,消息驱动的各自...

  • 《人机交互技术》 第六章 人机交互界面的表示模型与实现

    第六章 人机交互界面表示模型与实现 1.表示模型(第二版课本P99)  使用人机交互界面的表示模型和形式化的设计语言来分析和表达用户界面的功能以及用户和系统之间的交互情况,并且界面表示模型能方便映射到实际的...

  • 用户界面设计有效的人机交互策略_学习笔记

    目录说明第一章 交互系统的可用性ISO9241: 可用性目标:有效性、效率、满意度一种可用性度量(只...**Contextual theories**微观人机交互理论 Micro-HCI Theories宏观人机交互理论 Macro-HCI Theories第二部分:设计过

  • 人机交互-9-人机交互基础知识

    人机交互-9-人机交互基础知识

  • 基于Web的仓库管理系统的设计与实现

    摘 要 仓库物品的管理是与我们的日常生活息息相关的一个重大问题。随着我国经济飞速的发展,改革开放的不断深入,企业要想在...在此篇文章中严格按照软件工程思想,设计并实现了一个仓库信息管理系统。介绍了系统从...

  • 【毕业论文】| 基于Unity3D引擎的冒险游戏的设计与实现

    我将展示是如何从选题、可行性分析、设计和实现等方面去书写的这篇论文。在这篇毕业论文中,介绍我所采用的游戏设计原则、技术和工具,并详细描述了游戏的各种模块的实现方式。通过这篇论文,我希望能够为步入大四的...

  • 基于java的健康管理平台设计与实现(含源文件)

    面向特定群体的健康管理平台设计与实现 摘要 随着网络技术的不断发展,网站的开发与运用变得更加广泛。这次采用java语言SSH框架(Spring,Struts,Hibernate)设计并实现了面向特定群体的健康管理平台。该网站主要有...

  • 人机交互界面设计

    人机交互技术(Human-Computer Interaction Techniques)是指通过计算机输入、输出设备,以有效的方式实现人与计算机对话的技术。 2.简单介绍人机交互技术的研究内容。 (1)人机交互界面的表示模型和设计方法 ...

  • 基于微信小程序图书馆座位预约系统设计与实现

    在此基础上,设计了基于微信小程序的图书馆座位管理系统,通过该系统来实现图书馆座位预约,使得该馆能更加高效地管理。 该系统是以需求分析为出发点,根据用户需求,按功能模块进行编写程序,在开发过程中努力去...

  • 基于Vue的在线购物系统的设计与实现(论文+源码)_kaic

    (三)研究内容 本文介绍了在线购物系统的设计与开发过程,系统前端采用Vuejs框架,服务器使用Nodejs平台,文章内容主要有系统可靠性、易用性以及安全性的分析,五个功能模块的设计计、数据库设计以及实现应用的类...

  • 人机交互重点知识点

    人机交互是关于设计、评价和实现供人们使用的交互式计算机系统,且围绕这些方面的主要现象进行研究的科学。 1.2人机交互的研究内容 (1)人机交互界面表示模型与设计方法 (2)可用性分析与评估 (3)多通道交互技术...

  • 基于JSP的学术交流论坛系统的设计与实现

    参考文献 # 基于JSP的学术交流论坛系统的设计与实现 摘要 学术交流论坛系统是按照MVC模式进行设计实现的,采用Tomcat作为后台服务器,以MySQL数据库作为数据服务器,使用SERVLET进行逻辑控制,表现层用JSP页面显示,...

Global site tag (gtag.js) - Google Analytics