• 移动web:翻页场景动画


      在移动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设置为100%,做个最大宽度限制为max-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;
    },
    //..

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

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

      好了,到此结束。

       ---------------- 补充 ------------------------

       为了兼容各大浏览器,css3动画属性前缀值得注意。

    //..
    setStyle: function(el, p, v) {
        !this.cache[el] && (this.cache[el] = {});
        !this.cache[el][p] && (this.cache[el][p] = this.prefix(p));
        el.style[this.cache[el][p] || this.prefix(p)] = v;
    },
    prefix: function(p) {
        var style = document.createElement('div').style;
        if (p in style) return p;
        var prefix = ['webkit', 'Moz', 'ms', 'O'],
            i = 0,
            l = prefix.length,
            s = '';
        for (; i < l; i++) {
            s = prefix[i] + '-' + p;
            s = s.replace(/-D/g, function(match) {
                return match.charAt(1).toUpperCase();
            });
            if (s in style) return s;
        }
    },
    //..

      这里做了PC端查看兼容,注意IE的pointer移动事件会有个很有意思的表现,移动端的程序还是在移动设备中查看比较好。

     

  • 相关阅读:
    69期-Java SE-046_JSP-3
    69期-Java SE-045_JSP-2
    69期-Java SE-044_JSP-1
    69期-Java SE-043_Servlet-1
    69期-Java SE-042_Tomcat-1
    Sublime Text3快捷键
    http协议与https协议的前世今生
    解决并发问题,数据库常用的两把锁——悲观锁,乐观锁
    springboot解决文件上传大小限制
    docker基本操作
  • 原文地址:https://www.cnblogs.com/eyeear/p/5006943.html
Copyright © 2020-2023  润新知