3

“更多|收起”交互中渐进使用transition动画

 4 years ago
source link: https://www.zhangxinxu.com/wordpress/2012/10/more-display-show-hide-tranisition/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=2717

一、这是个平淡的开头

在web页面上,类似于“展开更多”、“显示全部”这类显隐效果,就跟大上海的私家车一样,随处可见。随便Open两个页面,就可见到影分身:
豆瓣网上的大众点评的

一般而言,且平心而论,这类交互效果应当就是这样子的——“唐突的显隐交互”,即内容“啪”一下子呈现或隐藏。

简洁明快,快速消费,且性能不错,实现以及后期维护成本也很低;对于功能性,多交互的商业站点而言,诸多权衡,确实是不错的选择。不过,类似广告宣传的展示性网站,更注重炫酷效果,唐突以及生硬的效果显然是接受不了的。

还有可能接受不了的是年轻气盛的交互设计师——其可能无法忍受一段内容唐突地出现在用户面前——而不是slideDown这类柔和的动画形式——因为前者可能会让用户觉得不舒服,体验不够好。

我突然想到了网上常常会出现的一句话:强奸着强奸着,也就习惯了~~

想到这句话,说明自己也矛盾,或者说自己还没有想明白:整站流畅交互固然不错,就像Apple的产品一样,会有非常不错的体验。但是,实际上,自己内心真正想法是:如果网站交互都带有动画性质,反而很不好;即使将实现以及性能等因素撇开不谈,也是不合适的;因为,网站可能需要自己的气质,过多的效果可能会显得浮躁~~

这种感觉就像是:一个中国妹子,全身上下都是扎眼的高档装饰品,不见得好看(Apple这样的名媛或贵妇另当别论);反而只有头上插了朵小花,更好看。所谓画龙点睛,1~2处惊艳即可,如果龙身上画满了眼睛——我勒个去,密集恐惧~~

听说有个“二八法则”的,如果应用在显隐toggle交互上,8分“啪”显隐,2分“呼”显隐??——我不确定,你怎么看呢?

二、“更多|收起”喜欢的动画

虽然,web世界中,交互动画效果N多多,但是,很多都是约定俗成的,或者称之为“有固定套路的”。

根据David Kaneda创建的Transitions动画CSS代码,我们可以将效果归结为这几大类:slide(滑来滑去), fade(淡入淡出), flip(飞来飞去), pop(大大小小).

如居中弹框呈现与隐藏,适合pop效果;绝对定位浮动层(如智能提示下拉框,自定义时间选择控件)等的呈现与隐藏使用fade效果;幻灯片播放的广告位效果一般为slide效果;点击某商品飞入页面右下角或左上角的购物车就是flip效果(类似最新FireFox浏览器关闭标签页效果)。

而对于页面上,“展开更多|收起更多”这类交互,由于元素默认隐藏,且显示内容就在点击区域附近,因此,想要避免唐突,适合的效果只能是从无到有慢慢“滑出来”-也就是slide效果。

正统的slide效果与伪slide效果
熟知jQuery的人应该都知道,其效果API中有slideDown(), slideUp(), slideToggle()的动画效果API. 那这几个方法是正统的slide效果呢还是伪slide效果?

答案是:伪slide效果!

正统的slide效果,应该是元素整体位置的移动,如这个demo所示的效果,您可以狠狠地点击这里:正统slide效果演示demo

正统slide元素下面先显示

而jQuery slideDown()/slideUp()效果中,元素本身并没有移动,类似这个demo所示效果:伪slide效果演示demo

伪slide效果元素的上部分先显示

在我们实际制作页面的时候,采用的都是“伪slide效果”,为何?

因为更低的成本!归根结底是因为:一般的元素(都是非定位元素)是从上往下一次排下来的。因此,通过控制height来实现slide效果的时候,都是元素的上半部分先显示。正统的slide效果的实现需要将动画呈现元素改成定位元素(应用position:absolute/fixed/…),这显然只适用于某些特殊的情况……

因此,作为API, 插件或公共方法,所实现的slide效果都是“伪slide效果”。

三、jQuery中滑动API

实现的原理
一图胜前言,下图为slideDown效果进行中的时候,对动画元素HTML代码的截图:

jQuery slide效果进行中的截图 张鑫旭-鑫空间-鑫生活

因此,可以知道,jQuery的slide效果是同时变更元素的height/margin-top/margin-bottom/padding-top/padding-bottom/ + 设置overflow:hidden值实现的。

注意:没有同时变更border-top/border-bottom的宽度值,因此,如果元素的边框较大,slide效果在结束的瞬间会有顿一下的感觉。在jQuery1.6版本中,宽边框元素无论是slideDown效果还是slideUp效果,在动画要结束的时候,都会有明显的顿感;在jQuery1.8版本中,slideDown效果从头到尾都算流程;但是slideUp效果结束时候有顿感。

您可以狠狠地点击这里:jQuery1.6以及jQuery1.8版本slide效果对比demo

IE7浏览器下效果的顿感以及bug截图 张鑫旭-鑫空间-鑫生活

应用局限
个人观点,jQuery中的slide滑动效果,看上去还那么回事,实际上,是个鸡肋API.

1. 依赖jQuery库。jQuery库本身越来越庞大,比方说jQuery1.8 gzip后居然还有30多K,个人觉得相当大,也相当笨重了。实际上,对于实际项目,选择器没有必要那么强大,有一半的API可以阉割掉,gzip后8~9K足矣,下图我最近某项目使用jQuery文件:
jQuery核心代码压缩后gzip后大小

2. 效果本身不是很完美。如上面演示的,边框元素顿感,以及IE6/IE7浏览器下直接显示bug.

3. 性能问题。如果页面复杂,本身交互比较多。对于IE6这类浏览器,如此动画效果,必定会有卡、顿的感觉;甚至会浏览器扛不住直接崩掉。因此,在实际项目的时候,slide动画使用并不多。尤其对于“更多|收起”交互,因为切换元素都不是绝对定位元素,因此,强烈的重绘会让CPU激动不已!

4. 过分兼容。我觉得,就算应用slide效果,最好IE6~7下直接“唐突显示”,其他现代浏览器浏览器则动画什么的。什么样车有什么样的的马力,什么样的马力跑什么样的速度!……

因此,如果我们非得应用一些slide交互效果,我们可以需求其他更简单,更方便,更灵活的方法。例如,借助CSS3中的transition, 渐进实现一些动画效果。

五、transition实现 – 真的很简单

拿上面出现的“正统slide效果页面”同样效果举例,您可以狠狠地点击这里:transition实现正统slide效果demo

上demo IE10+, 最近版本的FireFox, Chrome, Opera浏览器都可以看到非常流畅的slide动画效果。

相比之前模拟动画demo,这里多了这么行CSS代码:

.container {
    transition: height 0.6s;
}

然后,JS代码就基本上全部消灭了,只留下改变高度的几行代码:

var display = false;

button.onclick = function() {
    display = !display;
    container.style.height = display? "192px": "0px"
    return false;
};
})();

简单,真好!我突然诗兴大发:“脱裤子般简单,拉大便般舒畅;啊!真好,真好!”
众人:“好诗!好诗!!”

六、transition实现的公共方法

在展示transition slide公共方法前,有必要讲下CSS3 transition动画效果出现的条件:

  1. 动画属性前后不同值必须含数值,例如height:0height: auto是没有动画效果的
  2. 不能属性值的应用前后必须有时间差,例如:
    element.style.height = "0px";
    element.style.height = "100px";

    虽然这里元素高度设置了0, 也设置了100像素。但是,由于浏览器的性能机制,只会直接渲染后面100像素高度,因此,是不会出现动画效果的!

因此,考虑到在实际的交互中,显示元素的高度是不可能都已知的,我们要实现transition的公共方法的最难点,就是得到展开元素的精确高度值(height:auto是不会有动画效果的)。

本着简单,人人可以上手的指导思想,我们需要一些约定,关于CSS以及HTML结构的。

HTML结构:

container
    box
        list
        list

其中container以下CSS是必不可少的(如果内部子元素没有绝对定位元素,position:relative可以省略),就是下面这样(私有前缀这里省掉了):

.container {
    height: 0; position: relative;  overflow: hidden; transition: height 0.6s;
}

其中,动画时间你自己控制,你还可以增加缓动类型,例如ease:

.container {
    transition: height 0.3s ease;
}

对于HTML结构,只有唯一一个要求,就是container的子元素只有一个(方便高度的获取,多子元素,高度计算麻烦多了)。

<div class="container">
    <p>我是孤独的根号3...</p>
</div>
<div class="container">
    <ul>
        <li>列表1</li>
        <li>列表2</li>
    </ul>
</div>

准备工作完毕,下面就是方法了(IE6~8浏览器直接设置height:auto即可):

var slideToggleTrans = function(element, display) { //  display表示默认更多展开元素是显示状态还是隐藏
    if (typeof window.screenX === "number") {
        // 现代浏览器
        element.addEventListener("click", function() {
            display = !display;
            var rel = this.getAttribute("data-rel"), eleMore = document.querySelector("#" + rel);    

            eleMore && (eleMore.style.height = display? (function() {
                var height = 0;
                Array.prototype.slice.call(eleMore.childNodes).forEach(function(child) {
                    if (child.nodeType === 1) {
                        var oStyle = window.getComputedStyle(child);
                        height = child.clientHeight + (parseInt(oStyle.borderTopWidth) || 0) + (parseInt(oStyle.borderBottomWidth) || 0);
                    }
                });    
                return height;
            })() + "px": "0px");
        });
    } else {
        // IE6-IE8浏览器
        element.attachEvent("onclick", function() {    
            display = !display;
            var rel = element.getAttribute("data-rel"), eleMore = document.getElementById(rel);
            eleMore && (eleMore.style.height = display? "auto": "0px");
            return false;
        });
    }
};

触发按钮,以及对应的显示隐藏元素,通过按钮元素上面自定义的”data-rel“属性相关联。

data-rel与显隐元素的id相关联 张鑫旭-鑫空间-鑫生活

您可以狠狠地点击这里:渐进使用transition的显隐公共方法demo

不支持transition的直接“啪啪”显示,支持transition的“呼呼”显示。渐进增强,简单又高性能。

需要额外说明的

  1. 以上公共的slideToggleTrans方法如果直接用在实际项目中,是有所欠缺的。缺在哪里呢?就是没有添加回调。因此,您可能需要添加一个额外的参数以及两行代码:
    var slideToggleTrans = function(element, display, callback) {
        // click事件中: ...; callback.call(element, eleMore, display);
    }
  2. 我们平时显隐更多地是通过display属性控制。然而,display无法触发transition动画。如果我们先设置display:block,再去改变高度,行也行。但是,这种感觉就像是:平时用地下水冲马桶的,后来自来水也能冲干净,但非要再用地下水冲一次。而且,在脚本的编写上,更折腾(display:none的元素无法获取子元素的真实高度)。
    还是那句话,为了让一切都显得那么简单,我们要稍微改变下习惯,学习使用height控制元素的显隐。
  3. 以上公共方法不依赖任何JavaScript库。我这里想说的不是称赞其灵活性,而是想说,我们平时开发,都应该有自己的JS框架的。因此,上述代码有必要在自己框架基础上进行一番整改,可以大大简单代码;同时提高参数的可定制性(data-rel, 回调等);以及其他相关扩展——如ajax更多加载的动画显示等!
    这里的,仅仅是最简单原始的,抛砖引玉而已。
  4. 定高其实是件伴随风险的事情,因为会有这样的情况:1. 页面内容宽度自适应,窄屏下元素应该更高;2. 更多元素内部还有类似展开收起的交互,高度可变。
    因此,我们还需要额外处理。情况1,需要在动画结束的时候,设置height: auto; 情况2则是,内部再出现2次交互的时候,再次改动元素的高度值(自动触发动画)。

七、这是一个平淡的结语

我脑中突然冒出两个名字:“社会框架”和“团队框架”。“社会框架”就像我们使用的完整版的jQuery,因为其要兼容千万开发者受众各种环境,各种使用情况,因此,变得庞大是在所难免的。但是,如果放在单一的团队的,很多很多的东西都是多余的。因为,团队中可以有一个名叫“规范”的东西进行约束。——比方说,这种UI交互,我们的HTML结构上需要注意什么,需要什么样的CSS配合就可以使用极简的JS代码实现我们想要的效果等等。

说点更实际的,本文的内容,如果要让我写一个面向万千使用者的transition动画插件,我需要考虑的就非常多:
1. 有的用户喜欢默认设置display:none, 以及其他CSS属性(例如其对容器设置了会冲突的overflow:auto);
2. 显隐元素里面就是文本,或者是列表子节点们。我需要获取各种情况下子元素们的高度。这很头疼;
3. 交互场景千千万。显隐元素内部还有其他域高度打交道的交互,我该如何处理;
4. 等等其他N多……

要全部兼顾上面的问题,可以想象,我的JS代码不是几十行就可以搞定了。肯定会有很多与核心逻辑不相干的代码,去处理这些个特殊情况。于是,洋洋洒洒N多代码,这是我很不喜欢的,大家也一定不会喜欢的。

但是,如果code只是面向一个规范的团队,或是自己内部小组,OK,我们就可以通过一些约定俗成,大大简化我们的代码,我们的开发,最后是大家都开心!

我越来越不喜欢过于肥胖的jQuery了!

(本篇完)1f44d.svg 是不是学到了很多?可以分享到微信
1f44a.svg 有话要说?点击这里


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK