Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kontext项目的技术分析 #12

Open
matthew-sun opened this issue Jan 2, 2015 · 0 comments
Open

Kontext项目的技术分析 #12

matthew-sun opened this issue Jan 2, 2015 · 0 comments

Comments

@matthew-sun
Copy link
Owner

Kontext是国外大牛hakimel写的页面转场的动画效果,灵感来源于ios。我的卡路里消耗计算器项目中用到了这个效果,想在这篇博文中介绍Kontext实现原理,希望能帮助到大家对css3与Js相结合的动画有一个更好的理解。

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联合起来使用吧。

那么,问题来了?

前端动画哪家强?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant