/ Google Reader

实现类似Google Reader订阅树更新效果

最近因为工作需要,要在页面上呈现一棵树,并且数据会实时更新,更新的节点会高亮并逐渐淡出,就如同Google Reader左边订阅树的效果,如果有新的文章,节点背景变成黄色,然后渐渐透明。

首先,当然是在客户端实现一棵展现静态数据的树,这个我之前已经做过,有现成的了。实现也比较简单,做的是一个jQuery的插件,操作元素为树的容器。数据是服务端处理好的对象形如

[{Text:”aaa”, Icon:”img1.gif”, Children: [{Text:”aaa1”}, {Text:”aaa2”}]}, {Text:”bbb”, Icon:”img2.gif”, Children: [{Text:”bbb1”}, {Text:”bbb2”}]}]

传入参数包括数据,以及要显示为节点文字的属性名,图标路径的属性名,子节点集合属性名等。具体过程就是采用深度优先遍历数据画树,即对每一个节点创建一个Li元素,添加加减图标元素,节点文字元素,递归为其子树创建Ul。

然后,实现动态更新树的内容。逻辑很简单,树的结构不变,只是节点的文字变化。那么只需多引入一个参数,告诉一些属性名能够作为该节点的唯一标识。在刚才创建节点元素时,先判断当前节点数据在当前层级是不是已经创建节点,判断标准就用到新加的唯一标识。如果没有,则按之前的步骤创建节点,并在当前父节点上添加一个标记,以唯一标识做key,节点li元素做value(这个在javascript中实现十分简单,直接为父节点jQuery对象上添加属性即可,属性名就是key,属性值就是value);如果已经有节点元素,则直接修改节点文字。这种更新模式仅限于树的数据节点只会增加和修改,而不能处理减少的情况。有兴趣的同学可以研究一下如何高效的处理节点减少的情况,大家一起讨论讨论。这就完成了树的更新,要做实事更新,就很简单了,setInterval每隔一段时间取数据,然后更新树。

最后,就是如何将改变的节点高亮的显示出来,实现类似Google Reader更新的效果。有了前面的工作,剩下很简单。先在修改文字的时候,判断数据确实有改变(比如,我是比较节点文字是否一样)。如果数据改变了,就添加高亮效果;没有改变则可以什么都不做。至于这个高亮的效果,先变黄,这个很简单background-color设为yellow或#FFFF00,停顿一会,然后背景颜色淡出。

淡出我做过两种方案:第一,我首先想到的是jQuery的animate函数,只需提供元素的目标样式和动画时长,animate函数就能实现效果。经试验,这个函数是不支持background-color的,jQuery官方文档也特意提到了这点。后来上网查了一下,确实有人实现了background-color的渐变效果,还是官方插件,后来集成到jQuery UI中。试了一下确实有效果,但是目标样式只能为某一具体颜色,不能是透明的。而我的节点是有背景色的。渐变成白色有些突兀,那就只有渐变成当前的背景颜色了。因为没有办法获得当前元素的背景色(因为有可能是某个父元素的,或者当前这个位置重叠元素的背景色),只能hardcode了。想了半天实在不想写hardcode,想把这个树做成独立的控件,于是换了个思路,即第二个方案。

第二个方案就是做高亮的假象,即在节点文字元素上覆盖一个文字,大小和位置一样的元素。高亮时,创建这个元素盖住原文字元素,背景设为黄色。此时,熟悉jQuery效果函数的同学就会会心一笑啦,淡出太容易了,直接把这个假元素fadeOut就好啦。当然,最好是在fadeOut的回调函数中将这个假元素给删掉,不然更新太多DOM也越来越大了。这个假的文字元素,我是直接插在原文字元素后,然后用position:relative,left为负的元素宽度,这样便重合了。

        if (options.updateMode && createdNode[key]) {
            treeNode = createdNode[key].li;
            treeNode.data("data", this);
            var textSpan = createdNode[key].span;
            var originText = textSpan.text();
            var currentText = this[options.dataTextField];
            if (originText != currentText) {
                textSpan.text(this[options.dataTextField]);

                var effectSpan = $("").text(this[options.dataTextField]).css({ left: -textSpan.width(), top: 0 });
                textSpan.parent().append(effectSpan);
                effectSpan.delay(500).fadeOut(2000, function() {
                    $(this).remove();
                });
            }
        }

另外,我用的是jQuery 1.3.2,文字高亮时需停顿一段时间,此时jQuery中还没有delay函数,于是我将jQuery 1.4中的delay函数直接copy过来了:P。

Jackson Zhang

Jackson Zhang

Odd-e敏捷教练,主要涉及组织,团队,产品,技术,工程实践等,曾为多家知名企业提供教练与培训服务。译有《用户故事与敏捷方法》,《.NET单元测试的艺术》和《实例化需求说明》。擅长工程实践(如测试驱动开发,单元测试,重构,持续集成等),产品探索(Impact Mapping,Pretotyping,Lean Startup等)与团队协作。zbcjackson AT gmail.com

Read More