这个模块应该就是该书全篇的唯一一个模块吧,后面几乎所有的篇章都围绕这个模块去实现的,不过就通过这一个模块的实现和上线,也能体现单页应用开发到发布上线的整个过程,毕竟后面的数据,通信,发布什么的都是通用的东西,应用其他部分完全可以参照这个‘聊天模块’去实现。跟着作者的思路走一遍,也能熟知个大概。
先看下成型图吧,会不会有种很老旧XP时代的感觉,囧!!
chat
内容该聊天模块整体分以下几块:其实都是跟着模版去实现的,差在具体实现上而已
涉及行为处理:
1. 点击标题栏,显示或隐藏窗口;
2. hash 值的变化触发聊天窗口的状态变化;
重点在 通过 hash 值(锚)的变化来管理聊天窗口显示和隐藏,前面在shell里面实现过,这里主要拎出来作为一个单独模块来讲
配置三大块:
之前chat
模块是直接在shell
模块中实现的,为了实现模块化,减少依赖,从而把该模块分离出来单独创建模块文件:spa.chat.js
和相应的样式和辅助文件等
configMap
这里包含了chat
模块的基本配置,和所用到的模块变量,来看下里面具体都有些啥
var configMap = {
// 模块的文档结构部分,通过动态加载到其父容器中
main_html: ‘‘
+ ‘<div class="spa-chat">‘
+ ‘<div class="spa-chat-head">‘
+ ‘<div class="spa-chat-head-toggle">+</div>‘
+ ‘<div class="spa-chat-head-title">Chat</div>‘
+ ‘</div>‘
+ ‘<div class="spa-chat-closer">x</div>‘
+ ‘<div class="spa-chat-sizer">‘
+ ‘<div class="spa-chat-msgs"></div>‘
+ ‘<div class="spa-chat-box">‘
+ ‘<input type="text" />‘
+ ‘<div>send</div>‘
+ ‘</div>‘
+ ‘</div>‘
+ ‘</div>‘,
// 这个映射表,对应配置里面的变量,从对象名便可知,是用来
// 保存配置是否可设置状态,true:表示该变量值可以改变,反之不能修改
settable_map: {
slider_open_time : true,
slider_close_time : true,
slider_opened_em : true,
slider_closed_em : true,
slider_opened_title : true,
slider_closed_title : true,
chat_model : true,
people_model : true,
set_chat_anchor : true
},
// 窗口关闭和打开的动画时间,使用 $.animate 时使用的时间
slider_open_time : 250,
slider_close_time : 250,
slider_opened_em : 18,
slider_closed_em : 2,
// 两个最小阀值用来防止页面被缩放的时候,聊天窗口同比例缩放导致变的太小而做出的限制
// 这里单位是:em
slider_opened_min_em : 10,
window_height_min_em : 20,
// 窗口打开和隐藏状态时,鼠标放在上面时显示的提示内容,通过元素的‘title‘属性实现
slider_opened_title : ‘Click to close‘,
slider_closed_title : ‘Click to open‘,
// 两个数据模型,该书采用了数据库插件:taffyDb 来管理和模拟数据
chat_model : null,
people_model : null,
// 此方法是聊天窗口显示和隐藏的关键
// 核心模块shell 正是通过这个接口来管理聊天模块触发锚的变化从而
// 让聊天模块具备前进后台历史等浏览器功能
// 该方法指向shell模块中的 setChatAnchor 函数,
// setChatAnchor 在 shell 中定义,与 ‘uriAnchor‘ 插件发生联系
// 然后通过配置模块:configModule, 将该函数与`chat`模块发生联系,从而
// 使 chat 改变 -> setChatAnchor -> shell 锚管理
set_chat_anchor : null
},
通过 set_chat_anchor
改变聊天窗口状态变化路径图
通过图中就能明显的看出,chat 是如何通过 shell 来实现状态更新,以及实现自身的前进后退历史等功能
stateMap
相对于基本配置容器里面的配置,在运行过程中基本都不会有变化的值来说,stateMap
里面就是一些运行过程中,会因为用户行为而发生变化的一些状态值了
突然发现人在不同的时间和不同的认知段会对同一个事物的理解和解释大有不同(废话么,谁不知道啊!!)
var stateMap = {
// 模块容器,不多说
$container: null,
// 位置值,表示模块窗口因用户行为而发生变化的状态值
// 主要有:‘closed‘,‘opened‘,‘hidden‘
position_type : ‘closed‘,
// 单位转换, px/em,实际运行过程中会涉及到元素的位置设置,通过转换
// 得到实际像素值,去设置
px_per_em : 0,
// 下面三个就是针对三个不通状态时,窗口的位置了
slider_hidden_px : 0,
slider_closed_px : 0,
slider_opened_px : 0
}
针对 stateMap
里面的属性,有几个针对性的函数
第一个就是 setPxSizes
,由于页面都是通过 em 来设置大小的,因此在获取实际像素值的时候需要做个响应的转换
/*
这里面干了以下几件事:
1. 获取 em -> px 的单位值,然后计算出当前文档窗口的总高度值:window_height_em
2. 根据文档窗口决定模块窗口打开时的高度:opened_height_em
3. 更新 stateMap 里面的窗口相关的状态值
4. 最后确定聊天窗口内容体的具体高度
*/
setPxSizes = function () {
var
px_per_em, opened_height_em, window_height_em;
px_per_em = getEmSize( jqueryMap.$slider.get(0) );
// 计算窗口高度:em
window_height_em = Math.floor(
( $(window).height() / px_per_em ) + 0.5
);
// 根据范围值来决定窗口打开的高度
opened_height_em
= window_height_em > configMap.window_height_min_em
? configMap.slider_opened_em
: configMap.slider_opened_min_em;
stateMap.px_per_em = px_per_em;
stateMap.slider_closed_px = configMap.slider_closed_em * px_per_em;
stateMap.slider_opened_px = opened_height_em * px_per_em;
jqueryMap.$sizer.css({
height: ( opened_height_em - 2 ) * px_per_em
});
};
// 这个函数,需要在初始化模块initModule中将模块加载到容器中之后,
// 就需要执行,因为其中涉及的一些状态值需要在后续运行操作过程中就要用到,
// 比如:px_per_em, slider_closed_px,slider_opened_px 等等
第二个函数就是:setSliderPosition
,这个是真正修改聊天窗口显示隐藏状态的函数,里面通过$.animate
动画实现,并且是提供给 shell
模块,在 hash 值发生变化时触发了hashchange
事件调用事件处理函数:onHashChange
,里面根据具体的状态值去调用的,因此上面的状态走向图还不是很准确,
/*
这里面的实现,主要依赖与:position_type,根据用户行为,去改变这个值,然后最终通过 hash 值的变化,传到这里,去改变窗口显示或隐藏
里面涉及到的参数:
height_px:动画的目标属性,窗口高度
animate_time: 动画时间
slider_opened_title:以及窗口改变后显示的 title 属性值
最后通过动画结束后的回调:callback
*/
setSliderPosition = function ( position_type, callback ) {
var
height_px, animate_time, slider_title, toggle_text,
setAttr;
if ( stateMap.position_type === position_type ) {
return true;
}
setAttr = function ( height, an_time, title, text ) {
height_px = height;
animate_time = an_time;
slider_title = title;
toggle_text = text;
};
switch ( position_type ) {
case ‘opened‘:
setAttr(
stateMap.slider_opened_px,
configMap.slider_open_time,
configMap.slider_opened_title,
‘=‘
);
break;
case ‘hidden‘:
setAttr( 0, configMap.slider_open_time, ‘‘, ‘+‘ );
break;
case ‘closed‘:
setAttr(
stateMap.slider_closed_px,
configMap.slider_close_time,
configMap.slider_closed_title,
‘+‘
);
break;
default:
return false;
break;
}
// 重置位置类型
stateMap.position_type = ‘‘;
jqueryMap.$slider.animate(
{ height: height_px },
animate_time,
function () {
jqueryMap.$toggle.prop( ‘title‘, slider_title );
jqueryMap.$toggle.text( toggle_text );
stateMap.position_type = position_type;
callback && callback( jqueryMap.$slider );
}
);
return true;
};
上面的图修改之后添加 setSliderPosition
就比较完美了
最后为了应对窗口的 resize
事件,窗口需要做出相应的变化:handleResize
// spa.chat.js
handleResize = function () {
if ( !jqueryMap.$slider ) { return false; }
setPxSizes();
if ( stateMap.position_type === ‘opened‘ ) {
jqueryMap.$slider.css({ height: stateMap.slider_opened_px });
}
return true;
};
// spa.shell.js 中应对 window 的 resize 事件
onResize = function ( event ) {
if ( stateMap.resize_idto ) { return true; }
spa.chat.handleResize();
stateMap.resize_idto = setTimeout(function () {
stateMap.resize_idto = undefined;
}, configMap.resize_interval);
return true;
};
值得一提的是应对 window
的 resize
事件方案采取的是针对每个模块实现自己的应对方法,从而使模块能自己来决定如何应对窗口的缩放应对方式
jqueryMap
这里面的东西就不多记录了,都是些死的东西,缓存模块内元素的对象,方便jQuery
使用,同样需要在该模块被追加到模块容器shell
中之后才调用
/*
这里就不能直接用 $.html 了,而是使用 $.append 追加到模块容器 `shell` 模块当中
*/
setJqueryMap = function () {
var
$append_target = stateMap.$append_target,
$slider = $append_target.find( ‘.spa-chat‘ );
jqueryMap = {
$slider : $slider,
$head : $slider.find( ‘.spa-chat-head‘ ),
$toggle : $slider.find( ‘.spa-chat-head-toggle‘ ),
$title : $slider.find( ‘.spa-chat-head-title‘ ),
$sizer : $slider.find( ‘.spa-chat-sizer‘ ),
$msgs : $slider.find( ‘.spa-chat-msgs‘ ),
$box : $slider.find( ‘.spa-chat-box‘ ),
$input : $slider.find( ‘.spa-chat-box input[type=text]‘ ),
};
};
这篇主要介绍了该书对 chat
模块的处理,用这个示例来说明子模块如何和模块管理器模块:shell相互作用,如何组装子模块,如何通过锚变化来管理子模块的状态变化,以及如何应对 window
的 resize
事件的思想。另外还包含了如何移除 chat
模块,其中涉及的 removeSlider
处理等。
这里面需要注意的地方也没几处,主要感觉还是要弄清楚,模块的状态值和状态的变化如何在子模块和 shell
模块之间是如何传递以及起作用的,上面两张过程图更直观点。
到此,模块的页面和组装基本完成了,下一篇将会介绍该书是如何实现数据模型,如何让数据模型发生作用(数据管理插件:taffyDb)。
完结,待续……!
原文:http://blog.csdn.net/gccll/article/details/55212702