在痛苦的IE8时代,所有的动画都只能基于自己计算相关动画属性,开定时器setTimeout/setInterval轮询动画任务。

而肩负重任的HTML5,早已注意到了日益增强的动画,随着HTML5的降临,带来了强劲的CSS3动画,本文主要探讨:乘着CSS3的风,实现JS动画——探索现代画风的js动画。

> 本文内容如下:
> - CSS3动画
> - 基于CSS3的动画本质
> - 封装基于CSS3的动画API
> - 事件处理
> - 结语
> - 参考和引用

JavaScript - 前端开发交流群:377786580

CSS3动画

CSS3的动画各种文章漫天飞已经讲烂了,CSS3到目前为止总共新增了两个动画属性:transitionanimation。这里只关注我们目前要用的的部分:transition。至于animation的部分请参考《MDN - 使用CSS动画》。

CSS3让动画前所未有的简单,下面的例子演示了transition,当点击div.demo的时候,div.demo向右偏移200px:

  1. <style>
  2. .demo { background-color: #0094ff; width: 100px; height: 100px; position: absolute;left: 0;
  3. transition: left /*要执行动画的属性*/
  4. linear /*动画曲线*/
  5. 1s; /*动画执行时间*/ }
  6. </style>
  7. <div class="demo" onclick="javascript: this.style.left = '200px';"></div>

上面一段transition的意思就是:

  • 指定动画的属性为left,对left所有的操作都会触发动画
  • 指定动画的曲线(贝塞尔函数)
  • 指定动画的执行时间

仔细看看代码也就能发现,其实transitionbackground一样也是简写属性,是这几个CSS3新增属性的简写:

  • transition-property - 指定动画的属性,可以指定多个,用,号分隔
  • transition-timing-function - 指定动画的曲线(贝塞尔曲线)
  • transition-duration - 指定动画的执行时间
  • transition-delay - 指定动画延迟时间

这两段代码意义相同:

  1. transition: left /*要执行动画的属性*/
  2. linear: ; /*动画曲线*/
  3. 1s; /*动画执行时间*/
  4. transition-property:left; /*动画属性*/
  5. transition-timing-function:linear; /*动画曲线*/
  6. transition-duration:1s; /*执行时间*/
  7. transition-delay:0; /*动画延迟时间*/

效果如图:

早期实现动画比较麻烦,需要使用类似下面JS的原理来实现:

  1. <div class="demo" id="demo" style="left:0"></div>
  2. <script>
  3. var elem = document.getElementById('demo'),//获取元素
  4. elemStyleSheet = elem.style,//元素的内联样式对象
  5. left = parseInt(elemStyleSheet.left),//获取当前left
  6. targetLeft = 200,//目标left
  7. time = 13,//动画每帧间隔
  8. offsetValue = targetLeft / parseInt(1000 / 13),//每帧偏移量
  9. intervalId,
  10. temp;//临时变量
  11. elem.onclick = function () {
  12. intervalId = setInterval(function () {
  13. //追加偏移量
  14. temp = parseInt(elemStyleSheet.left) + offsetValue;
  15. elemStyleSheet.left = temp + 'px';
  16. if (temp >= targetLeft)//完成动画
  17. clearInterval(intervalId);
  18. }, time);
  19. };
  20. </script>

效果和上面的css3实现的一样。大体意思就是计算出动画的帧数、每帧间隔、每帧动画的偏移量,然后开个定时器一直重复执行,直到动画完成。具体高能版实现可以参阅jQuery.animate

这里需要注意:早期的动画都是基于定时器setTimeout/setInterval来轮询动画任务,它们本身的模型就不是为了动画而打造的,实现动画的性能上实在堪忧,所以现代浏览器都部署了新的API requestAnimationFrame来弥补setTimeout/setInterval在动画方面天生的表现力不足。

近期jQuery发布了jQuery3.0 预览版,就使用了requestAnimationFrame来完成动画。

 

基于CSS3的动画本质

transition可以驱动(作为动画)的属性太多太多,例如:

  • 位置:left、top、right、bottom
  • CSS3的transform变形和z轴偏移,参阅CSS3 Transform
  • 透明度:opacity
  • 宽高:height/width
  • 颜色(color)
  • 边框(border)
  • 边距(margin/padding)
  • 等等等等

在支持CSS3的情况下,如果我们想执行动画,本质上都可以使用transition来驱动。因为transition已经封装好了动画的行为,我们只需要指定transition需要的一些关键属性值即可。

所以这个动画的实现,本质上就是一个给DOM赋上CSS3的属性transition,然而我们早已就看透了一切。

 

封装基于CSS3的动画API

既然CSS3自身就已经实现了相关动画属性,那么封装API这种事情就变得十分简单了,拿我们最初的例子来说,可以使用如下js代码:

  1. <style>
  2. .demo { background-color: #0094ff; width: 100px; height: 100px; position: absolute; }
  3. </style>
  4. <div class="demo" id="demo"></div>
  5. <script>
  6. var elem = document.getElementById('demo'),
  7. elemStyleSheet = elem.style;//元素的内联样式对象
  8. //赋上transition
  9. elemStyleSheet.cssText = 'left:0; transition: left linear 1s;';
  10. elem.onclick = function () {
  11. elemStyleSheet.left = '200px';
  12. }
  13. </script>

核心代码其实就2行,附上transition和关键属性即可。是不是突然觉得动画真是so easy~~~

程序是要有健壮性的,既然我们发现了这么个"天大的秘密",是不是封装成API以后给自己、给基友使用更好呢?看起来就觉得很高大上一样,那我们来封装下API吧。

最简短的封装就是把transition的四个属性封装传递进来就可以了:

  1. var animate = function (elem, propertys, ease, duration, delay) {
  2. var cssText = [],
  3. props = [];
  4. for (var name in propertys) {
  5. props.push(name);//提取要执行动画的属性
  6. //提取动画目标样式
  7. cssText.push(name + ':' +
  8. //如果是number,则追加px单位
  9. (typeof propertys[name] === 'number' ? propertys[name] + 'px' : propertys[name]));
  10. }
  11. //添加transition样式
  12. cssText.push('transition-property:' + props.join(''));
  13. cssText.push('transition-timing-function:' + (ease || 'linear'));
  14. cssText.push('transition-duration:' + (duration || 300) + 's')
  15. cssText.push('transition-delay:' + (delay || 0));
  16. //添加元素样式
  17. elem.style.cssText += ';' + cssText.join(';');
  18. };

大体意思就是把transition-*的属性通过外面的参数传递进来,然后我们做下拼接的处理,然后给元素新增上样式属性就可以了。

代码到了这里,我们来看看成果,毕竟我们仅使用了12行代码就完成了JS的动画。戳这里查看运行demo

 

事件处理

到了这里,我们会发现其实API和jQuery.animate很像很像,哟吼,看起来很不错的样子,等等,好像还缺了点什么。

仔细想想,我们还缺少一个重要的东西:动画结束事件

我们往往有很多的需要的任务都是在动画结束事件里完成的,但是我们怎么能没有动画结束这么重要的事件呢,别着急,国外那群搞浏览器的,也已经为大家讨论出了结果(当然也还有其他动画事件):

使用transition的动画,提供一个动画结束的事件:onTransitionEnd

我们再在刚才的demo下追加一行添加onTransitionEnd事件的代码:

  1. //动画结束事件
  2. elem.addEventListener('transitionend', function () {
  3. alert('动画结束了!!!');
  4. });

戳这里查看运行demo

我们再来看看onTransitionEnd事件的兼容性:

嗯,其实有些浏览器很早以前的实现都是私有实现,这里为了防止出现意外,还是嗅探一下浏览器吧,针对浏览器的私有实现,我们绑定私有事件。

当然我们应该考虑的更周全一点,既然都已经嗅探了私有事件,我们一同嗅探出私有属性吧,防止有些浏览器不支持标准的CSS但是私有实现了transition

  1. var testElem = document.createElement('div'),
  2. //各大浏览器私有属性:transitionProperty、webkitTransitionProperty、transitionProperty、oTransitionProperty、msTransitionProperty
  3. vendors = { '': '', 'Webkit': 'webkit', 'Moz': '', 'O': 'o', 'ms': 'ms' },
  4. /*
  5. https://github.com/madrobby/zepto/pull/742
  6. firefox从未支持过mozTransitionEnd或MozTransitionEnd,firefox一直支持标准的事件transitionend
  7. */
  8. normalizeEvent = function (name) {
  9. return eventPrefix ? eventPrefix + name : name.toLowerCase();
  10. },
  11. //私有css前缀
  12. cssPrefix = null,
  13. //私有事件前缀
  14. eventPrefix = null,
  15. //私有事件
  16. onTransitionEnd = null;
  17. for (var name in vendors) {
  18. //嗅探特性
  19. if (testElem.style[(name ? name + 'T' : 't') + 'ransitionProperty'] !== undefined) {
  20. cssPrefix = name ? '-' + name.toLowerCase() + '-' : name;
  21. eventPrefix = vendors[name];
  22. onTransitionEnd = normalizeEvent('TransitionEnd');
  23. break;
  24. }
  25. }

 

最后,我们整理一下代码,把这些私有属性的嗅探和之前的代码进行融合,同时做一些优雅降级的处理。一个轻量级的,基于CSS3的JS动画就这么实现了。

 

  1. (function (window) {
  2. var Support = {
  3. cssPrefix: null,
  4. eventPrefix: null,
  5. onTransitionEnd: null
  6. },
  7. testElem = document.createElement('div'),
  8. //transitionProperty、webkitTransitionProperty、transitionDuration、oTransitionDuration、msTransitionProperty
  9. vendors = { '': '', 'Webkit': 'webkit', 'Moz': '', 'O': 'o', 'ms': 'ms' },
  10. /*
  11. https://github.com/madrobby/zepto/pull/742
  12. firefox从未支持过mozTransitionEnd或MozTransitionEnd,firefox一直支持标准的事件transitionend
  13. */
  14. normalizeEvent = function (name) {
  15. return Support.eventPrefix ? Support.eventPrefix + name : name.toLowerCase();
  16. };
  17. Object.keys(vendors).some(function (name) {
  18. var eventPrefix = vendors[name];
  19. //嗅探特性
  20. if (testElem.style[(name ? name + 'T' : 't') + 'ransitionProperty'] !== undefined) {
  21. Support.cssPrefix = name ? '-' + name.toLowerCase() + '-' : name;
  22. Support.eventPrefix = vendors[eventPrefix];
  23. Support.onTransitionEnd = normalizeEvent('TransitionEnd');
  24. return true;
  25. }
  26. })
  27. //动画结束事件
  28. var onTransitionEnd = Support.onTransitionEnd !== null ?
  29. //animationEnd从android 4.1支持
  30. function (el, callback, time) {
  31. //支持transition
  32. var onEndCallbackFn = function (e) {
  33. if (typeof e !== 'undefined') {
  34. if (e.target !== e.currentTarget) return;//防止冒泡
  35. }
  36. this.removeEventListener(Support.onTransitionEnd, onEndCallbackFn);
  37. callback.call(el);
  38. };
  39. el.addEventListener(Support.onTransitionEnd, onEndCallbackFn);
  40. } : function (el, callback, time) {
  41. //不支持就使用setTimeout
  42. setTimeout(function () {
  43. callback.call(el);
  44. }, time);
  45. };
  46. if (Support.cssPrefix == null) {
  47. Support.cssPrefix = '';
  48. Support.eventPrefix = '';
  49. }
  50. //动画
  51. var animatePrototypes = {
  52. transitionProperty: Support.cssPrefix + 'transition-property',
  53. transitionDuration: Support.cssPrefix + 'transition-duration',
  54. transitionDelay: Support.cssPrefix + 'transition-delay',
  55. transitionTiming: Support.cssPrefix + 'transition-timing-function'
  56. };
  57. /**
  58. * 动画
  59. * animate(elem, properties, duration)
  60. * animate(elem, properties, duration, delay)
  61. * animate(elem, properties, duration, ease)
  62. * animate(elem, properties, duration, callback, delay)
  63. * animate(elem, properties, duration, ease, callback, delay)
  64. * @param {int} elem - 要执行动画的元素
  65. * @param {function|string} properties - 动画执行的目标属性,为String则表示是animation-name,为object则是transition-property
  66. * @param {int} duration - 动画执行时间(ms)
  67. * @param {string} [ease = linear] - 动画线性
  68. * @param {function} [callback = null] - 动画执行完成的回调函数
  69. * @param {int} [delay = 0] - 动画延迟(s)
  70. * @returns {null}
  71. */
  72. var animate = function (elem, properties, duration, ease, callback, delay) {
  73. //修正参数支持重载
  74. if (typeof ease === 'function') {
  75. //重载
  76. delay = callback;
  77. callback = ease;
  78. ease = null;
  79. }
  80. if (ease > 0) {
  81. delay = ease;
  82. ease = null;
  83. };
  84. var cssProperties = [],
  85. cssValues = {},
  86. transformValues = '',
  87. eventCallback,
  88. cssStr = [],
  89. value;
  90. Object.keys(properties).forEach(function (name) {
  91. value = properties[name];
  92. cssValues[name] = typeof value === 'number' ? value + 'px' : value;
  93. cssProperties.push(name);
  94. });
  95. //填补transition样式
  96. cssValues[animatePrototypes.transitionProperty] = cssProperties.join(', ');
  97. cssValues[animatePrototypes.transitionDuration] = duration + 's';
  98. cssValues[animatePrototypes.transitionDelay] = (delay || 0) + 's';
  99. cssValues[animatePrototypes.transitionTiming] = (ease || 'linear');
  100. if (callback) {
  101. onTransitionEnd(elem, callback, duration);
  102. }
  103. //设置样式
  104. for (var key in cssValues) {
  105. cssStr.push(key + ':' + cssValues[key]);
  106. }
  107. //聪明的同学,想想为什么?
  108. setTimeout(function () {
  109. elem.style.cssText += ';' + cssStr.join(';');
  110. }, 0);
  111. };
  112. window.animate = animate;
  113. })(window);

运行效果如图:

戳这里查看完整demo

结语

HTML5和CSS3为前端注入了巨大的力量,CSS3强大的动画现在各大网站随处可见,而现在web前端的技术更新又异常的快,前端的技术也层出不穷,各种新的卓越的概念各种涌现。

但是我们在这一片繁华的背后,也应该深刻的思考技术的根基。在眼花缭乱的技术背后,看破本质,务必时刻掌控住你的代码和思想。

这篇文章主要分析了transition,其实这里还可以兼容CSS另外一个动画:animation,聪明的童鞋,思考思考如何去做?

再谈点自己:最近很忙,离职了又入职。重构组里的前端开发,各种评估框架,完善基础类库,组里准备给前端中间驾一层Node,自己又在开发自己的个人博客网站,忙的各种没时间更新。

另外,招人,要求只有一点:对代码有追求。

JavaScript - 前端开发交流群:377786580

参考和引用

zepto.js - fx模块源码

Modernizr.js源码

swipe.js源码

作者:linkFly
声明:嘿!你都拷走上面那么一大段了,我觉得你应该也不介意顺便拷走这一小段,希望你能够在每一次的引用中都保留这一段声明,尊重作者的辛勤劳动成果,本文与博客园共享。

JavaScript - 基于CSS3动画的实现的更多相关文章

  1. 使用 CSS3 动画实现的 3D 图片过渡特效

    这是一个基于 CSS3 动画实现的图片过渡效果,共有 Flip.Rotation.Multi-flip.Cube.Unfold 等6种效果,它们将证明 CSS3 Transform 和 Transit ...

  2. jQuery Animation实现CSS3动画

    jQuery Animation的工作原理是通过将元素的CSS样式从一个状态改变为另一个状态.CSS属性值是逐渐改变的,这样就可以创建动画效果.只有数字值可创建动画(比如 "margin:3 ...

  3. css3动画简单应用

    CSS3添加了几个动画效果的属性,通过设置这些属性,可以做出一些简单的动画效果而不需要再去借助JavaScript.CSS3动画的属性主要分为三类:transform.transition以及anim ...

  4. CSS3 动画

      通过 CSS3,我们能够创建动画,这可以在许多网页中取代动画图片.Flash 动画以及 JavaScript. CSS3 动画 CSS3 @keyframes 规则 如需在 CSS3 中创建动画, ...

  5. JavaScript基于时间的动画算法

    转自:https://segmentfault.com/a/1190000002416071 前言 前段时间无聊或有聊地做了几个移动端的HTML5游戏.放在不同的移动端平台上进行测试后有了诡异的发现, ...

  6. 5个基于css3超炫的鼠标滑动按钮动画

    今天给大家分享5个基于css3超炫的鼠标滑动按钮动画.这5个按钮鼠标经过的时候有超炫的动画效果.这5个按钮适用浏览器:360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之 ...

  7. 基于CSS3制作的鼠标悬停动画菜单

    之前分享了好多款css3实现的鼠标悬停效果.今天再给大家带来一款基于CSS3制作的鼠标悬停动画菜单.这款菜单适用浏览器:360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界 ...

  8. Scrollanim – CSS3 & JavaScript 创建滚动动画

    Scrollanim 是结合 CSS3 和 JavaScript 来创建令人惊叹的滚动动画的开源库. Scrolanim 支持在页面上的所有可用的元素的位置.有很多的自定义参数可以配置使用,构建出精彩 ...

  9. 基于css3炫酷页面加载动画特效代码

    基于CSS3实现35个动画SVG图标.这是一款基于jQuery+CSS3实现的SVG图标动画代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class=&qu ...

随机推荐

  1. 【WP 8.1开发】如何动态生成Gif动画

    相信如何为gif文件编码,很多朋友都会,而难点在于怎么让GIF文件中的帧动起来,也就是创建gif动画. Gif文件编码方法 先简单介绍一下编码的方法. 1.调用BitmapEncoder.Create ...

  2. XenServer pool 移除server 设置master

    如果因为Pool中Master主机由于某种原因导致失效,会引起整个Pool进入紧急模式,恢复步骤如下: 在成员服务器上输入如下命令 # xe host-emergency-ha-disable     ...

  3. [转]浏览器退出之后php还会继续执行么?

    原文链接:http://www.cnblogs.com/yjf512/p/5362025.html 前提:这里说的是典型的lnmp结构,nginx+php-fpm的模式 如果我有个php程序执行地非常 ...

  4. 纯CSS照片墙

    css中transform参考CSS3属性transform详解之(旋转:rotate,缩放:scale,倾斜:skew,移动:translate 效果图:

  5. Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用

    转载博客:http://blog.csdn.net/xiaanming/article/details/26810303 大家好!差不多两个来月没有写文章了,前段时间也是在忙换工作的事,准备笔试面试什 ...

  6. Android随笔之——按键长按事件onKeyLongPress

    现在安卓手机实体键是越来越少了,但还是有的,恰好自己就碰上了:按键的长按事件...百度了一些博客,内容都基本上是完全一样的,虽然可以捕获到长按事件,但却会和正常的单击冲突.幸好最近开个VPN,goog ...

  7. geotrellis使用(十一)实现空间数据库栅格化以及根据属性字段进行赋值

    Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 安装空间数据库 空间数据库栅格化 根据属性字段进行赋 ...

  8. 让自己也能使用Canvas

    <canvas> 是 HTML5 新增的元素,可使用JavaScript脚本来绘制图形.例如:画图,合成照片,创建动画甚至实时视频处理与渲染. 兼容性方面,除了一些骨灰级浏览器IE6.IE ...

  9. canvas实现拖动页面时显示窗口视频

    简介 当前主流的视频网站目前有不少新鲜好玩的功能,最明显的莫过于小视频的显示--当视频不在当前视口范围 时,会在右下角用一个小窗口来显示当前的视频,而且可以拖拽. 今晚心血来潮,起了动手试试的念头.我 ...

  10. table-cell实现宽度自适应布局

    利用table-cell可以实现宽度自适应布局. table-cell有一些比较好用的属性,比如垂直居中,自适应高度宽度等,为元素设置table-cell布局之后,元素的margin失效,paddin ...