Kontext项目的技术分析
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联合起来使用吧。
那么,问题来了?
前端动画哪家强?
- Categories:
- frontend 19
- Tags:
- javascript,开源项目 3