转载

移动web:翻页场景动画

在移动web,特别是在微信中,经常看到一种翻页动画效果,也称为场景动画。

一页一页的翻过,像在看书,每页的内容以各种 "炫酷" 的效果出现在你的眼里,配上一首动听的音乐,你有没有喜欢上呢。

这里没有音乐,没有炫酷的出场,只有实实在在的翻页。z

先看看效果( 如果不能查看 复制下面的代码保存在本地查看

一生就这么一次,

谈一场以结婚为目的的恋爱吧。

不再因为任性而不肯低头,

不再因为固执而轻言分手。

最后地坚信一次,一直走,就可以到白头。

惟愿这一生,执子之手,与子偕老。

你敢天长,我就敢地久。

深思熟虑之后我们决定

为爱情做出重要的延续

邀请您一起见证
移动web:翻页场景动画

先贴上代码, 仅供参考

  /**  * LBS slidePage 绝对定位方式 支持WP  * Date: 2014-11-20  * ===================================================  * opts.el 外围包裹容器/滑动事件对象(一个字符串的CSS选择器或者元素对象)  * opts.index 索引(默认0) 指定显示哪个索引的页  * opts.current    当前页添加的类名(默认'current')  * opts.navShow 是否需要导航指示 默认false不需要   * opts.navClass 导航指示容器的类名 方便设置样式 (默认'slide-nav')   * opts.auto 是否自动播放 默认false  * opts.delay 自动播放间隔时间 默认5000(单位毫秒) 自动播放时有效  * opts.locked 是否锁定头尾滑动  默认false 如果开启则不能使用自动播放  * opts.effect 动画效果(平移=translate 缩放=scale 重叠=overlap) 默认平移  * opts.duration 动画持续时间 默认300(单位毫秒)   * opts.minScale 动画效果为缩放时的最小缩放比率(0 ~ 1) 1为没有缩放效果 默认0.5  * opts.start 手指按下时 执行函数  * opts.move 手指移动中 执行函数  * opts.end 手指收起后 执行函数  * ===================================================  * this.box 包裹页的容器对象  * this.index 当前索引  * this.length 有多少页 最后一页的索引为 this.length-1  * this.play 调用自动播放的方法   * this.stop 清除自动播放的方法  * this.up 手动调用向上滑动翻页的方法 方便增加点击按钮时调用  * this.down 手动调用向下滑动翻页的方法  * ===================================================  **/ ;(function() {     window.slidePage = function(opts) {         opts = opts || {};         if (opts.el === undefined) return;         this.box = typeof opts.el === 'string' ? document.querySelector(opts.el) : opts.el;         this.pages = this.box.children;         this.length = this.pages.length;         if (this.length < 1) return;         if (opts.index > this.length - 1) opts.index = 0;          this.body = document.getElementsByTagName('body')[0];         this.nav = null;         this.navs = [];         this.navShow = !!opts.navShow || false;         this.navClass = opts.navClass || 'slide-nav';          this.index = this.oIndex = opts.index || 0;         this.current = opts.current || 'current';         this.locked = !!opts.locked || false;         this.auto = !!opts.auto || false;         this.auto && (this.delay = opts.delay || 5000);         this.effect = opts.effect || 'translate';         this.duration = opts.duration || 300;         this.minScale = opts.minScale || 0.5;          this.start = opts.start || function() {};         this.move = opts.move || function() {};         this.end = opts.end || function() {};          this.timer = null;         this.animated = true;         this.touch = {};         this.point = '';          this.init();     };     slidePage.prototype = {         init: function() {             this.navShow && this.createNav();             this.initSet();             this.bind();         },         createNav: function() {             var li = null,                 i = 0;             this.nav = document.createElement('ul');             for (; i < this.length; i++) {                 li = document.createElement('li');                 this.navs.push(li);                 this.nav.appendChild(li);             }             this.nav.className = this.navClass;             this.body.appendChild(this.nav);         },         initSet: function() {             this.height = document.documentElement.clientHeight || document.body.clientHeight;             if (this.css(this.box, 'position') !== 'absolute' || this.css(this.box, 'position') !== 'relative') this.box.style.position = 'relative';             if (this.css(this.box, 'overflow') !== 'hidden') this.box.style.overflow = 'hidden';             this.box.style.height = this.height + 'px';             for (var i = 0; i < this.length; i++) {                 if (this.css(this.pages[i], 'display') !== 'none') this.pages[i].style.display = 'none';                 if (this.css(this.pages[i], 'position') !== 'absolute') this.pages[i].style.position = 'absolute';                 this.pages[i].style.height = this.height + 'px';             }             if (this.navShow) {                 this.nav.style.marginTop = -this.nav.offsetHeight / 2 + 'px';                 this.navs[this.index].className = this.current;             }             this.pages[this.index].className += ' ' + this.current;             this.zIndex = parseInt(this.css(this.pages[this.index], 'zIndex') === 'auto' ? 1 : this.css(this.pages[this.index], zIndex)) + 10;             this.pages[this.index].style.display = 'block';         },         bind: function() {             var _this = this;             this.on(this.box, ['touchstart', 'pointerdown', 'mousedown'], function(e) {                 _this.touchStart(e);                 _this.auto && _this.stop();             });             this.on(this.box, ['touchmove', 'pointermove', 'mousemove'], function(e) {                 _this.touchMove(e);                 _this.auto && _this.stop();             });             this.on(this.box, ['touchend', 'touchcancel', 'pointerup', 'mouseup'], function(e) {                 _this.touchEnd(e);                 _this.auto && _this.play();             });         },         touchStart: function(e) {             this.point = e.type.indexOf('down') < 0 ? 'touch' : 'pointer';             if (this.point === 'pointer') {                 this.touch.x = e.pageX;                 this.touch.y = e.pageY;             }else if (this.point === 'touch') {                 this.touch.x = e.touches[0].pageX;                 this.touch.y = e.touches[0].pageY;             }             this.touch.disX = 0;             this.touch.disY = 0;             this.touch.fixed = '';             this.start && this.start();         },         touchMove: function(e) {             e.stopPropagation();             e.preventDefault();             if (this.point === '') return;             if (this.touch.fixed === 'left') return;             if (!this.animated) return;             if (this.point === 'pointer') {                 this.touch.disX = e.pageX - this.touch.x;                 this.touch.disY = e.pageY - this.touch.y;             }else if (this.point === 'touch') {                 if (e.touches.length > 1) return;                 this.touch.disX = e.touches[0].pageX - this.touch.x;                 this.touch.disY = e.touches[0].pageY - this.touch.y;             }             if (this.touch.fixed === '') {                 if (Math.abs(this.touch.disY) > Math.abs(this.touch.disX)) {                     this.touch.fixed = 'up';                 } else {                     this.touch.fixed = 'left';                 }             }             if (this.touch.fixed === 'up') {                 if (this.effect === 'scale') {                     this.scale = ((this.height - Math.abs(this.touch.disY)) / this.height).toFixed(3);                     this.scale < this.minScale && (this.scale = this.minScale);                 }                 if (this.touch.disY > 0) {                     if (this.locked && this.oIndex === 0) return;                     this.dis = -this.height;                     this.index = this.oIndex - 1;                     this.index < 0 && (this.index = this.length - 1);                     if (this.effect === 'scale') this.setOrigin(this.oIndex, 'center bottom');                 } else {                     if (this.locked && this.oIndex === this.length - 1) return;                     this.dis = this.height;                     this.index = this.oIndex + 1;                     this.index > this.length - 1 && (this.index = 0);                     if (this.effect === 'scale') this.setOrigin(this.oIndex, 'center top');                 }                  if (this.nIndex !== undefined && this.nIndex !== this.index && this.nIndex !== this.oIndex) {                     this.pages[this.nIndex].style.display = 'none';                     this.pages[this.nIndex].style.zIndex = '';                     this.pages[this.nIndex].style.webkitTransform = this.pages[this.nIndex].style.transform = '';                 }                 this.nIndex = this.index;                  this.pages[this.oIndex].style.zIndex = this.zIndex;                 this.pages[this.index].style.zIndex = this.zIndex + 10;                 this.setTransform(this.index, this.dis);                 this.pages[this.index].style.display = 'block';                  this.setTransform(this.index, this.touch.disY + this.dis);                 if (this.effect === 'translate') this.setTransform(this.oIndex, this.touch.disY);                 if (this.effect === 'scale') this.setScale(this.oIndex, this.scale);                  this.move && this.move();             }         },         touchEnd: function(e) {             this.point = '';             if (this.index === this.oIndex) return;             if (this.touch.fixed === 'up') {                 var Y = Math.abs(this.touch.disY);                 if ((this.animated && Y > 10) || Y > this.height / 2) {                     this.slide();                 } else {                     this.goback();                 }                 this.end && this.end();             }         },         css: function(o, n) {             return getComputedStyle(o, null)[n];         },         on: function(el, types, handler) {             for (var i = 0, l = types.length; i < l; i++) el.addEventListener(types[i], handler, false);         },         setScale: function(index, v) {             this.setStyle(this.pages[index], 'transform', 'scale(' + v + ')');         },         setOrigin: function(index, dir) {             this.setStyle(this.pages[index], 'transform-origin', dir);         },         setTransform: function(index, v) {             this.setStyle(this.pages[index], 'transform', 'translate3d(0,' + v + 'px,0)');         },         setTransition: function(index, v) {             this.setStyle(this.pages[index], 'transition', 'all ' + v + 'ms');         },         setStyle: function(el, p, v) {             var prefix = ['o', 'moz', 'ms', 'webkit', ''],                 i = 0,                 l = prefix.length;             for (; i < l; i++) {                 (function(i) {                     var s = prefix[i] + '-' + p;                     s = s.replace(/-/D/g, function(match) {                         return match.charAt(1).toUpperCase();                     });                     el.style[s] = v;                 }(i));             }         },         slide: function() {             var _this = this;             this.animated = false;             this.setTransition(this.index, this.duration);             this.setTransition(this.oIndex, this.duration);             this.setTransform(this.index, 0);             if (this.effect === 'translate') this.setTransform(this.oIndex, -this.dis);             if (this.effect === 'scale') this.setScale(this.oIndex, this.minScale);             setTimeout(function() {                 if (_this.index !== _this.oIndex) _this.update();                 _this.animated = true;             }, this.duration);         },         goback: function() {             var _this = this;             this.setTransition(this.index, 100);             this.setTransition(this.oIndex, 100);             this.setTransform(this.index, this.dis);             if (this.effect === 'translate') this.setTransform(this.oIndex, 0);             if (this.effect === 'scale') this.setScale(this.oIndex, 1);             setTimeout(function() {                 _this.clear();                 _this.pages[_this.index].style.display = 'none';                 _this.index = _this.oIndex;             }, 100);         },         update: function() {             if (this.navShow) {                 this.navs[this.index].className = this.current;                 this.navs[this.oIndex].className = '';             }             this.pages[this.oIndex].style.display = 'none';             this.pages[this.index].className += ' ' + this.current;             this.pages[this.oIndex].className = this.pages[this.oIndex].className.replace(this.current, '').trim();             this.clear();             this.oIndex = this.index;         },         clear: function() {             this.pages[this.index].style.webkitTransition = this.pages[this.index].transition = '';             this.pages[this.oIndex].style.webkitTransition = this.pages[this.oIndex].transition = '';             this.pages[this.index].style.webkitTransform = this.pages[this.index].style.transform = '';             this.pages[this.oIndex].style.webkitTransform = this.pages[this.oIndex].style.transform = '';             this.pages[this.oIndex].style.zIndex = '';             this.pages[this.index].style.zIndex = '';             if (this.effect === 'scale') this.setOrigin(this.oIndex, '');         },         animate: function() {             var _this = this;             this.setTransform(this.index, this.dis);             this.pages[this.index].style.display = 'block';             this.pages[this.oIndex].style.zIndex = this.zIndex;             this.pages[this.index].style.zIndex = this.zIndex + 10;             this.setTransition(this.index, 0);             this.setTransition(this.oIndex, 0);             setTimeout(function() {                 _this.slide();             }, 50);         },         up: function() {             this.dis = this.height;             this.index++;             this.index > this.length - 1 && (this.index = 0);             if (this.effect === 'scale') this.setOrigin(this.oIndex, 'center top');             this.animate();         },         down: function() {             this.dis = -this.height;             this.index--;             this.index < 0 && (this.index = this.length - 1);             if (this.effect === 'scale') this.setOrigin(this.oIndex, 'center bottom');             this.animate();         },         play: function() {             var _this = this;             if (this.locked) return;             this.timer = setInterval(function() {                 _this.up();             }, this.delay);         },         stop: function() {             this.timer && clearInterval(this.timer);             this.timer = null;         }     }; }());  查看完整代码

和以前写的 图片切换   有许多共同的地方,整个翻页和图片切换原理都是类似的。

这个支持自动播放,三种翻页效果,限制头尾滑动等。

翻页动画,就是在一个容器内滑动, 在这个容器中 每次只显示一页,每一页都有一些css3动画效果的元素出现。

一个简单的HTML结构如下:

 <div id="slidePageBox" class="slide-box">     <section class="slide-page page1">          <!-- 1 -->     </section>     <section class="slide-page page2">         <!-- 2 -->     </section>     <section class="slide-page page3">         <!-- 3 -->     </section>     <section class="slide-page page4">         <!-- 4 -->     </section>     <section class="slide-page page5">         <!-- 5 -->     </section> </div> 

类名为slide-box的div标签就是容器,类名为slide-page的section标签就是每个要翻的页。

 this.box = typeof opts.el === 'string' ? document.querySelector(opts.el) : opts.el; this.pages = this.box.children; this.length = this.pages.length; 

这里this.box就是容器,this.pages就是所有要翻的页。

 this.height = document.documentElement.clientHeight; this.box.style.height = this.height + 'px'; for (var i = 0; i < this.length; i++) {     //..     this.pages[i].style.height = this.height + 'px'; } 

获取浏览器窗口的高,并设置容器和页的高为这个值。这个翻页是上下滑动,JS就没有获取宽度值了。

容器宽用css设置为width:100%,做个最大宽度限制为max-width:640px,这可根据实际情况设置。

现在切换翻页,当手指在容器滑动时,这里为判断上下滑动,满足滑动一定距离,手指离开容器时,开始翻页。

触摸事件:手指移入,手指移动,手指离开

指针事件:指针按下,指针移动,指针收起

鼠标事件: 鼠标按下,鼠标移动,鼠标收起

 //.. bind: function() {     var _this = this;     this.on(this.box, ['touchstart', 'pointerdown', 'mousedown'], function(e) {         _this.touchStart(e);          //..     });     this.on(this.box, ['touchmove', 'pointermove', 'mousemove'], function(e) {         _this.touchMove(e);         //..     });     this.on(this.box, ['touchend', 'touchcancel', 'pointerup', 'mouseup'], function(e) {         _this.touchEnd(e);         //..     }); }, //.. 

绑定了触摸事件,WP的指针事件,鼠标事件。

 //.. touchStart: function(e) {     this.point = e.type.indexOf('down') < 0 ? 'touch' : 'pointer';     if (this.point === 'pointer') {         this.touch.x = e.pageX;         this.touch.y = e.pageY;     }else if (this.point === 'touch') {         this.touch.x = e.touches[0].pageX;         this.touch.y = e.touches[0].pageY;     }     //.. }, //.. 

获取移入时的初始位置(触摸,指针,鼠标)

 //.. touchMove: function(e) {     //..     if (this.touch.fixed === 'left') return;     //..     if (this.point === 'pointer') {         this.touch.disX = e.pageX - this.touch.x;         this.touch.disY = e.pageY - this.touch.y;     }else if (this.point === 'touch') {         //..         this.touch.disX = e.touches[0].pageX - this.touch.x;         this.touch.disY = e.touches[0].pageY - this.touch.y;     }     if (this.touch.fixed === '') {         if (Math.abs(this.touch.disY) > Math.abs(this.touch.disX)) {             this.touch.fixed = 'up';         } else {             this.touch.fixed = 'left';         }     }     if (this.touch.fixed === 'up') {         //..     } }, //.. 

移动时(触摸,指针,鼠标)获取移动了多少距离,根据距离判断向左右还是上下移动的,这里翻页只要上下移动。

  //.. if (this.touch.fixed === 'up') {     if (this.effect === 'scale') {         this.scale = ((this.height - Math.abs(this.touch.disY)) / this.height).toFixed(3);         this.scale < this.minScale && (this.scale = this.minScale);     }     if (this.touch.disY > 0) {         if (this.locked && this.oIndex === 0) return;         this.dis = -this.height;         this.index = this.oIndex - 1;         this.index < 0 && (this.index = this.length - 1);         if (this.effect === 'scale') this.setOrigin(this.oIndex, 'center bottom');     } else {         if (this.locked && this.oIndex === this.length - 1) return;         this.dis = this.height;         this.index = this.oIndex + 1;         this.index > this.length - 1 && (this.index = 0);         if (this.effect === 'scale') this.setOrigin(this.oIndex, 'center top');     }      if (this.nIndex !== undefined && this.nIndex !== this.index && this.nIndex !== this.oIndex) {         this.pages[this.nIndex].style.display = 'none';         this.pages[this.nIndex].style.zIndex = '';         this.pages[this.nIndex].style.webkitTransform = this.pages[this.nIndex].style.transform = '';     }     this.nIndex = this.index;      this.pages[this.oIndex].style.zIndex = this.zIndex;     this.pages[this.index].style.zIndex = this.zIndex + 10;     this.setTransform(this.index, this.dis);     this.pages[this.index].style.display = 'block';      this.setTransform(this.index, this.touch.disY + this.dis);     if (this.effect === 'translate') this.setTransform(this.oIndex, this.touch.disY);     if (this.effect === 'scale') this.setScale(this.oIndex, this.scale);      this.move && this.move(); } //..  移动中

移动中做了很多事情,是整个程序比较重要的部分。在上下移动时,当前显示的页跟着移动,向上移动时,下一页显示出来一部分,向下移动时,上一页显示一部分。

如果锁定了头尾,第一页向下移动时不能移动,同样最后一页向上移动时不能移动。

opts.effect 动画效果(平移=translate 缩放=scale 重叠=overlap) 默认平移

根据每种翻页效果做初始化设置(如果只需要简单的平移切换可以省去很多代码)。

 //.. touchEnd: function(e) {     this.point = '';     if (this.index === this.oIndex) return;     if (this.touch.fixed === 'up') {         var Y = Math.abs(this.touch.disY);         if ((this.animated && Y > 10) || Y > this.height / 2) {             this.slide();         } else {             this.goback();         }        //..     } }, //.. 

离开时(触摸,指针,鼠标)根据条件执行滑动切换。

 //.. slide: function() {     var _this = this;     this.animated = false;     this.setTransition(this.index, this.duration);     this.setTransition(this.oIndex, this.duration);     this.setTransform(this.index, 0);     if (this.effect === 'translate') this.setTransform(this.oIndex, -this.dis);     if (this.effect === 'scale') this.setScale(this.oIndex, this.minScale);     setTimeout(function() {         if (_this.index !== _this.oIndex) _this.update();         _this.animated = true;     }, this.duration); }, //.. 

滑动切换完成时会更新一些设置,比如为当前页增加当前标志类名。每页的css3动画元素也就可以根据这个类名执行动画效果。

 //.. update: function() {     if (this.navShow) {         this.navs[this.index].className = this.current;         this.navs[this.oIndex].className = '';     }     this.pages[this.oIndex].style.display = 'none';     this.pages[this.index].className += ' ' + this.current;     this.pages[this.oIndex].className = this.pages[this.oIndex].className.replace(this.current, '').trim();     this.clear();     this.oIndex = this.index; }, //.. 

切换了页,执行了每页的动画,整个场景动画就差不多完成了。

最后就是一些简单的了,自动播放什么的。

好了,到此结束。

正文到此结束
Loading...