18 October 2014

Kontext是什么?

Kontext是国外大牛hakimel写的页面转场的动画效果,灵感来源于ios。
Kontext的项目展示demo

Kontext怎么用?

html代码:

<section class="kontext">
    <div class="layer show">...</div>
    <div class="layer">...</div>
    <div class="layer">...</div>
</section>

JS代码:

// 创建一个kontext实例
var k = kontext( document.querySelector( '.kontext' ) );

// API METHODS:
k.prev(); // 展示上一个场景
k.next(); // 展示下一个场景
k.show( 3 ); // 展示指定索引值的场景
k.getIndex(); // 现在展示的场景的索引值
k.getTotal(); // 场景的数量

Kontext的炫酷效果是怎么实现的?

css代码分析

// kontext对象全屏包裹
.kontext {
    width: 100%;
    height: 100%;
}

// 每一个场景绝对定位,并默认看不见
.kontext .layer {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    visibility: hidden;
}

// 设置show的场景显示出来  
.kontext .layer.show {
    visibility: visible;
}

// 如果当前浏览器支持css3的景深效果,则加上capable,这个会在js中操作实现
// 在ie6等等不支持css3景深效果的低级浏览器中,则表现为正常的场景切换,没有动画效果
.kontext.capable {
    -webkit-perspective: 1000px;
       -moz-perspective: 1000px;
            perspective: 1000px;

    -webkit-transform-style: preserve-3d;
       -moz-transform-style: preserve-3d;
            transform-style: preserve-3d;
}

// 场景延Z轴向里缩100px
.kontext.capable .layer {
    -webkit-transform: translateZ( -100px );
       -moz-transform: translateZ( -100px );
            transform: translateZ( -100px );
}

// 对于显示的场景保持原位置
.kontext.capable .layer.show {
    -webkit-transform: translateZ( 0px );
       -moz-transform: translateZ( 0px );
            transform: translateZ( 0px );
}

// 向右翻页时,需要显示的页面,用js加上show和right的class命,给予它名为show-right的动画
.kontext.capable.animate .layer.show.right {
    -webkit-animation: show-right 1s forwards ease;
       -moz-animation: show-right 1s forwards ease;
            animation: show-right 1s forwards ease;
}

// 向右翻页时,需要隐藏的页面,用js加上hide和right的class命,给予它名为hide-right的动画
.kontext.capable.animate .layer.hide.right {
    -webkit-animation: hide-right 1s forwards ease;
       -moz-animation: hide-right 1s forwards ease;
            animation: hide-right 1s forwards ease;
}

/* 这里即以下把left的代码给省略了,原理和right一样
/* CSS animation keyframes */

// show页面
// 0%时:延Z轴向里偏移200px
// 40%时:向右偏移40%,缩小至0.8,向y轴的负方向旋转20度
// 100%时:延Z轴方向恢复0px
@keyframes show-right {
    0%   { transform: translateZ( -200px ); }
    40%  { transform: translate( 40%, 0 ) scale( 0.8 ) rotateY( -20deg ); }
    100% { transform: translateZ( 0px ); }
}

// hide页面
// 0%时:延Z轴方向0px,显示效果为显示
// 40%时:向左偏移40%,缩小至0.8,向y轴的正方向旋转20度
// 100%时:延Z轴向里偏移200px,显示效果为隐藏
@keyframes hide-right {
    0%   { transform: translateZ( 0px ); visibility: visible; }
    40%  { transform: translate( -40%, 0 ) scale( 0.8 ) rotateY( 20deg ); }
    100% { transform: translateZ( -200px ); visibility: hidden; }
}

/* Dimmer */
.kontext .layer .dimmer {
    display: block;
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    visibility: hidden;
    background: transparent;
    z-index: 100;
}

.kontext.capable.animate .layer .dimmer {
    -webkit-transition: background 0.7s ease;
       -moz-transition: background 0.7s ease;
            transition: background 0.7s ease;
}

// 需要隐藏的场景,让遮罩显示出来,0.7s变化背景颜色
.kontext.capable.animate .layer.hide .dimmer {
    visibility: visible;
    background: rgba( 0, 0, 0, 0.8 );
}

js代码分析

// window上挂载kontext全局对象
window.kontext = function( container ) {

    // 这是一个观察者,也可称为自定义事件,用于观察场景是否发生了变化,实现方法在最下
    var changed = new kontext.Signal();

    //找到所有的场景
    var layers = Array.prototype.slice.call( container.querySelectorAll( '.layer' ) );

    // 判断浏览器是否支持3d景深
    var capable =   'WebkitPerspective' in document.body.style ||
                    'MozPerspective' in document.body.style ||
                    'msPerspective' in document.body.style ||
                    'OPerspective' in document.body.style ||
                    'perspective' in document.body.style;

    // 支持效果,则加上class名capable
    if( capable ) {
        container.classList.add( 'capable' );
    }

    // 给每一场景加上dimmer遮罩
    layers.forEach( function( el, i ) {
        if( !el.querySelector( '.dimmer' ) ) {
            var dimmer = document.createElement( 'div' );
            dimmer.className = 'dimmer';
            el.appendChild( dimmer );
        }
    } );

    // show(1,'left') || show(1,'right')
    function show( target, direction ) {

        layers = Array.prototype.slice.call( container.querySelectorAll( '.layer' ) );

        // 给kontext加上class名animate,用于准备动画
        container.classList.add( 'animate' );

        // 如果没有指定方向,则自行判断要运行的方向
        direction = direction || ( target > getIndex() ? 'right' : 'left' );

        if( typeof target === 'string' ) target = parseInt( target );
        if( typeof target !== 'number' ) target = getIndex( target );

        target = Math.max( Math.min( target, layers.length ), 0 );

        if( layers[ target ] && !layers[ target ].classList.contains( 'show' ) ) {

            layers.forEach( function( el, i ) {
                // 取消原先的动画
                el.classList.remove( 'left', 'right' );
                // 加上现在的动画方向
                el.classList.add( direction );
                if( el.classList.contains( 'show' ) ) {
                    // 给现在显示的场景加上class名hide
                    el.classList.remove( 'show' );
                    el.classList.add( 'hide' );
                }
                else {
                    // 除了有class名show之外的场景去掉class名hide
                    el.classList.remove( 'hide' );
                }
            } );

            // 给目标场景加上class名show
            layers[ target ].classList.add( 'show' );

            // 场景转换时派发事件
            changed.dispatch( layers[target], target );

        }

    }

    /**
     * 显示上一个场景
     */
    function prev() {

        var index = getIndex() - 1;
        show( index >= 0 ? index : layers.length + index, 'left' );

    }

    /**
     * 显示下一个场景
     */
    function next() {

        show( ( getIndex() + 1 ) % layers.length, 'right' );

    }

    /**
     * 返回现在显示场景的索引值
     */
    function getIndex( of ) {

        var index = 0;

        layers.forEach( function( layer, i ) {
            if( ( of && of == layer ) || ( !of && layer.classList.contains( 'show' ) ) ) {
                index = i;
                return;
            }
        } );

        return index;

    }

    /**
     * 返回场景的数量
     */
    function getTotal() {

        return layers.length;

    }

    // API
    return {

        show: show,
        prev: prev,
        next: next,

        getIndex: getIndex,
        getTotal: getTotal,

        changed: changed

    };

};

/**
 * 观察者
 */
kontext.Signal = function() {
    this.listeners = [];
}

kontext.Signal.prototype.add = function( callback ) {
    this.listeners.push( callback );
}

kontext.Signal.prototype.remove = function( callback ) {
    var i = this.listeners.indexOf( callback );

    if( i >= 0 ) this.listeners.splice( i, 1 );
}

kontext.Signal.prototype.dispatch = function() {
    var args = Array.prototype.slice.call( arguments );
    this.listeners.forEach( function( f, i ) {
        f.apply( null, args );
    } );
}

好简单!

怎么样,通过这个分析可以清楚的看到这个效果实现起来并不难,小伙伴们快来把css3和js联合起来使用吧。

那么,问题来了?

前端动画哪家强?