WaterFall Layout

== 简介 ==
最近有很多网站, 在设计上采用了多栏布局, 类似于 Pinterest (这貌似是最早使用这种布局的网站了), Mark之, 蘑菇街, 哇哦 等等. 这种类似的布局, 很像是一夜之间出现国内大大小小的网站上, 倒是很流行哈~

这种布局更适合于小而重复的数据块排列, 每个数据块没有侧重, 而且大多情况下, 这种布局下, 随着滚动条向下滚动, 不断加载数据块至当前最后, 鉴于此, 他又有另外一个名字 -- 瀑布流式布局.

== 几种实现方式 ==

随着越来越多设计师爱用这种布局了, 我们作为前端, 也尽可能满足视觉/交互设计师的需求. 整理了下这种布局的几种实现方式, 有三种:

1) 传统多列浮动, 即 蘑菇街和哇哦 采用的方式, 如下图所示:

传统多列浮动

- 各列固定宽度, 并且左浮动;
- 一列中的数据块为一组, 列中的每个数据块依次排列即可;
- 跟多数据加载时, 需要分别插入到不同的列上;
- 线上例子;
优点:
- 布局简单, 应该说没啥特别的难点;
- 不用明确知道数据块高度, 当数据块中有图片时, 就不需要指定;
缺点:
- 列数固定, 扩展不易, 当浏览器窗口大小变化时, 只能固定的x列, 如果要添加一列, 很难调整出来数据块;
- 滚动加载更多数据时, 还要指定插入到第几列中, 还是不方便;

2) CSS3 定义, W3C 中有讲述关于多列布局的文档, 排列出来的样子:

CSS3 定义

- 由 chrome/ff 浏览器直接渲染出来, 可以指定容器的列个数, 列间距, 列中间边框, 列宽度来实现;
    #container {
-webkit-column-count: 5;
/*-webkit-column-gap: 10px;
-webkit-column-rule: 5px solid #333;
-webkit-column-width: 210px;*/

-moz-column-count: 5;
/*-moz-column-gap: 20px;
-moz-column-rule: 5px solid #333;
-moz-column-width: 210px;*/

column-count: 5;
/*column-gap: 10px;
column-rule: 5px solid #333;
column-width: 210px;*/
}


- column-count 为列数; column-gap 为每列间隔距离; column-rule 为间隔边线大小; column-width 为每列宽度; 当只设置 column-width 时, 浏览器窗口小于一列宽度时, 列中内容自动隐藏了; 当只设置 column-count 时, 平均计算每列宽度, 列内内容超出则隐藏; 都设了 column-count 和column-width, 浏览器会根据 count 计算宽度和 width 比较, 取大的那个值作为每列宽度, 然后当窗口缩小时, width 的值为每列最小宽度. 这边其实很简单, 简易自己尝试下, 详细可参考 https://developer.mozilla.org/en/CSS3_Columns 中的说明;
- 线上列子;
优点:
- 直接 CSS 定义, 最方便了;
- 扩展方便, 直接往容器里添加内容即可;
缺点:
- 只有高级浏览器中才能使用;
- 还有一个缺点, 他的数据块排列是从上到下排列到一定高度后, 再把剩余元素依次添加到下一列, 这个本质上就不一样了;
- 鉴于这两个主要缺点, 注定了该方法只能局限于高端浏览器, 而且, 更适合于文字多栏排列;

3) 绝对定位, 即 Pinterest , Mark之, KISSY 采用的方式:

绝对定位

- 可谓是最优的一种方案, 方便添加数据内容, 窗口变化, 列数/数据块都会自动调整;
- 线上列子;
缺点:
- 需要实现知道数据块高度, 如果其中包含图片, 需要知道图片高度;
- JS 动态计算数据块位置, 当窗口缩放频繁, 可能会狂耗性能;


== KISSY.Waterfall 实现思路 ==
KISSY 的 Waterfall 组件主要包含两个部分, 一个是对现有数据块进行排列计算各自所在的位置; 二是下拉滚动时, 触发加载数据操作, 并把数据添加到目标容器中;

1) 数据块排列, 算法步骤简述下:
- 初始化时, 对容器中已有数据块元素进行第一次的计算, 需要用户给定: a, 容器元素 -- 以此获取容器总宽度; b, 列宽度; c, 最小列数; 最终列数取的是容器宽度/列宽度和最小列数的最大值, 这样保证了, 当窗口很小时, 仍然出现最小列数的数据;
- 获得列数后, 需要保存每个列的当前高度, 这样在添加每个数据块时, 才知道起始高度是多少;
- 依次取容器中的所有数据块, 先寻找当前高度最小的某列, 之后根据列序号, 确定数据块的left, top值, left 为所在列的序号乘以列宽, top 为所在列的当前高度, 最后更新所在列的当前高度加上这个数据块元素的高度, 至此, 插入一个元素结束;
- 当所有元素插入完毕后, 调整容器的高度为各列最大的高度值, 结束依次调整;
- 性能效率上的注意点: a, 如果当前正在调整中, 又触发了 resize 事件, 需要将上次调整暂停后执行这次调整(见 timedChunk 函数); b, resize 触发会很频繁, 可以将回调函数缓存一段时候后执行, 即当这段时间内多次触发了resize事件, 但回调函数只会执行一次(见 S.buffer 函数)
- 感兴趣的可以参见源码;

2) 异步加载数据, 前面讲的是如何对容器中已有元素进行排列, 但很多情况下, 还需要不断加载新数据块, 为此专门设计了一个独立的模块 KISSY.Waterfall.Loader, 其实这个功能就更简单了, 仅包含两个步骤:
- 绑定滚动事件, 并确定预加载线高度值, 即滚动到哪个高度后, 需要去加载数据, 其实这个就是列的最小高度值, 这样当前滚动值和最小高度值比较一下即可判断出来, 是否要触发加载数据;
- 加载数据, 为了不对数据源做太多限制, 完全由使用者自己决定数据源从哪边获取和其格式, 这样更好的方便用户使用. 为此, 该组件只提供一个load(success, end) 接口, 怎样load 由用户自己去定义, 而其中的 success/end, 分别给出如何添加新数据(suceess 即同 addItems)/如何停止加载的接口. 这样真是太方便了~~
- 感兴趣的可以参见源码;

== KISSY.Waterfall 示例和文档 ==
看到这边, 是不是很想试用一下~~, 嗯嗯, 这里给出一些相关学习资料和示例, 以供参考:

- Waterfall API 文档, 相关构造器, 配置项, 方法都在这里;
- 示例, 包含静态和动态两种;

欢迎试用和提出意见~~

Tags:    

G10入手

上周入手个 HTC Desire HD(G10, A9191), 纯粹白痴+新手, 从网上找了些资料, 补充下这方面知识;

== 名词解释 ==
名字: HTC Desire HD, G10, A9191 , 这几个名字貌似都是指同一款, 不知为何同一款要取3个名字? google了下, 原来分别是 官方名称, 民间叫法, 行货型号.

港版/欧版/亚太版/欧版: 其实区别不大, 只有默认选择的语言不同, 比如亚太版的, 默认是中简, 欧版的就是英文, 但也可以刷成中简的,, 欧版的相对便宜些, 但貌似淘宝上很难买到, 问了好多家店, 都没欧版的货.


刷机: 其实就是重新安装系统
破解root: 是指获取root 最高权限, 以后你自己安装什么都可以了 , 没限制
MIUI: 我的这款, 刷成MIUI系统 (http://www.miui.com 是个基于Android2.3原生系统深度开发, 针对中国用户使用习惯,原创特色的全套UI体系), 但因为电池原因, 刷之后充电总是充不上电, 只能通过USB充上, 据卖家说是因为MIUI的问题, 但我搜了下论坛帖子, 怀疑很可能是电池不是原电, 这等拿回来再查~

OTA: http://zhidao.baidu.com/question/389270, 一项基于短消息机制,通过手机终端或服务器(网上)方式实现SIM卡内业务菜单的动态下载、删除与更新,使用户获取个性化信息服务的数据增值业务(简称OTA业务)OTA(Over-the-Air Technology)空中下载技术.
是通过移动通信(GSM或CDMA)的空中接口对SIM卡数据及应用进行远程管理的技术。空中接口可以采用WAP、GPRS、CDMA1X及短消息技术。OTA技术的应用,使得移动通信不仅可以提供语音和数据服务,而且还能提供新业务下载。这样,应用及内容服务商可以不受平台的局限,不断开发出更具个性化的贴近用户需求的服务,如信息点播、互动娱乐、位置服务以及银行交易等。通过OTA空中下载技术,手机用户只要进行简单操作,就可以按照个人喜好把网络所提供的各种业务菜单利用OTA机制下载到手机中,并且还可以根据自己的意愿定制具体业务。


HD2 和 Desire HD: 两款不同的机型~ 他们最大的区别就是前者 OS 为 Windows mobile 6.5, 后者是 Android OS v2.2, 原先以为是同一款, 看名字挺像... 各参数见 http://detail.zol.com.cn/pk/248972_203330.shtml

PS: DHD, 很耗电, 差不多隔天就要充一次电~


接下来打算:
0) 附上一张照片;
1) 先熟悉下整个操作, 什么安装/卸载等等 ;
2) 翻墙;
3) 待熟悉了后再学刷机: 完整刷机教程 http://www.miui.com/a-57.html



Tags:  

ImageZoom重构记

年后回到公司, 花了些时间重构原来写的imagezoom, 主要是重构成基于uibase的组件模块化开发.

首先, 如果不熟悉 uibase的话, 可以先看承玉写的:
* 基于mixin的组件设计: http://yiminghe.javaeye.com/blog/808763 及里面的PPT ,
* selectbox的例子 http://yiminghe.javaeye.com/blog/897229,

PS: 这两篇文章及里面的ppt, 值得多看几遍, 不然肯定会看不懂~~ 偶就看文章/PPT, 对着代码, 不下看了三四遍后, 终于被我折腾出新的imagezoom. 而且还发现旧版的一个比较严重的bug.


分离
==================
将原来放在一起的逻辑分离出来:
- imagezoom/base.js, 处理初始化逻辑, 及小图, 放大镜图标的DOM构建, 绑定鼠标移入事件 等 ,
- imagezoom/zoomer.js, 放大显示逻辑, 放大层的DOM构建, 绑定鼠标移动事件, 显示对应的图像,

两者独立开来, 功能比较明确;


结构分离
==================
整个结构如下图所示:

mixin <br /><br />组件化

imagezoom主组件扩展于UIBase, 具有基本的box, position, align, mask 功能, 再加上扩展组件zoomer, 就可以写出新的 imagezoom, 而这里的扩展组件 zoomer 也方便其他组件使用.


这样组织的好处是:
- 利于已有组件, 快速搭建你所需要的新组件;
- 设置变量和UI展现分离, 数据通过setter/getter 统一设置, 而数据更改带来的UI变化, 通过_uiSetXX方式自动被调用, 而完成对应的UI变化. 这样分离了便于思维逻辑上的分离;
- 开发代码量减少, 因为组件可以重用, 我们自己只要写上对应的逻辑即可. 不过总的代码量没变多少, 重构后, 原先 imagezoom-pkg-min.js 7k , 重构后imagezoom-pkg-min.js, 6k 再加uibase-pkg-min.js 12k 共18k, 从这点上看, 貌似代码还可以优化.. 咔咔~

重构过程中, 还发现旧版本中的一个bug, 情况简化可以描述成, 当mouseenter到小图后, 绑定mousemove到body, 然后 mouseleave小图后, 删除body的mousemove事件, 大致想一下, 这逻辑没问题, 但是问题在小图上覆盖了一层镜片, 当显示大图时, 鼠标正在镜片元素上, 导致立即触发小图的mouseleave事件, 这样就会立刻hide()了, 但就是因为旧版本上, 在 mouseleave小图时, 没有正确删除body的mousemove事件, 即 原本是
    Event.on(self.image, 'mouseenter', function() {
//..
Event.on(body, 'mousemove', self._onMouseMove, self);
//..
});
Event.on(self.image, 'mouseleave', function() {
//..
Event.remove(body, 'mousemove', self._onMouseMove);
//..
});


而这里事件删除时, 后面的参数应该和on时完全一致, 如
    Event.on(self.image, 'mouseenter', function() {
//..
Event.on(body, 'mousemove', self._onMouseMove, self);
//..
});
Event.on(self.image, 'mouseleave', function() {
//..
Event.remove(body, 'mousemove', self._onMouseMove, self);
//..
});


这样才对, 不然就body上一直存在mouseleave, 而且每次mouseenter后, 再一次绑定body的mousemove事件, 虽然视觉差异不大, 测试也注意到这个问题, 唉... 说到这里, 好惭愧啊...



嗯嗯, 下次一定得记住了!

Tags: