Created
June 26, 2015 03:18
-
-
Save CodingMonkeyzh/7c43c9a25f0d132b83d6 to your computer and use it in GitHub Desktop.
图片倾斜效果
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* tiltfx.js | |
* http://www.codrops.com | |
* | |
* Licensed under the MIT license. | |
* http://www.opensource.org/licenses/mit-license.php | |
* | |
* Copyright 2015, Codrops | |
* http://www.codrops.com | |
*/ | |
;(function(window) { | |
'use strict'; | |
/** | |
* ************************************************************************** | |
* utils | |
* ************************************************************************** | |
*/ | |
// from https://gist.github.com/desandro/1866474 | |
var lastTime = 0; | |
var prefixes = 'webkit moz ms o'.split(' '); | |
// 获取动画属性 rAF & cAF | |
// get unprefixed rAF and cAF, if present | |
var requestAnimationFrame = window.requestAnimationFrame; | |
var cancelAnimationFrame = window.cancelAnimationFrame; | |
// 通过一个循环,处理rAF & cAF 的前缀 | |
// loop through vendor prefixes and get prefixed rAF and cAF | |
var prefix; | |
for( var i = 0; i < prefixes.length; i++ ) { | |
if ( requestAnimationFrame && cancelAnimationFrame ) { | |
break; | |
} | |
prefix = prefixes[i]; | |
requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ]; | |
cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] || | |
window[ prefix + 'CancelRequestAnimationFrame' ]; | |
} | |
// 如果不支持CSS3 动画属性,则用一个定时器来模拟动画 | |
// fallback to setTimeout and clearTimeout if either request/cancel is not supported | |
if ( !requestAnimationFrame || !cancelAnimationFrame ) { | |
requestAnimationFrame = function( callback, element ) { | |
var currTime = new Date().getTime(); | |
var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); | |
var id = window.setTimeout( function() { | |
callback( currTime + timeToCall ); | |
}, timeToCall ); | |
lastTime = currTime + timeToCall; | |
return id; | |
}; | |
cancelAnimationFrame = function( id ) { | |
window.clearTimeout( id ); | |
}; | |
} | |
function extend( a, b ) { | |
for( var key in b ) { | |
if( b.hasOwnProperty( key ) ) { | |
a[key] = b[key]; | |
} | |
} | |
return a; | |
} | |
// 获取鼠标位置 | |
// from http://www.quirksmode.org/js/events_properties.html#position | |
function getMousePos(e) { | |
var posx = 0; | |
var posy = 0; | |
if (!e) var e = window.event; | |
if (e.pageX || e.pageY) { | |
posx = e.pageX; | |
posy = e.pageY; | |
} | |
else if (e.clientX || e.clientY) { | |
posx = e.clientX + document.body.scrollLeft | |
+ document.documentElement.scrollLeft; | |
posy = e.clientY + document.body.scrollTop | |
+ document.documentElement.scrollTop; | |
} | |
return { | |
x : posx, | |
y : posy | |
} | |
} | |
// 节能器, delay 这个时间段只能执行一次 fn | |
// from http://www.sberry.me/articles/javascript-event-throttling-debouncing | |
function throttle(fn, delay) { | |
var allowSample = true; | |
return function(e) { | |
if (allowSample) { | |
allowSample = false; | |
setTimeout(function() { allowSample = true; }, delay); | |
fn(e); | |
} | |
}; | |
} | |
/***************************************************************************/ | |
/** | |
* 构造函数 | |
* TiltFx fn | |
*/ | |
function TiltFx(el, options) { | |
this.el = el; | |
this.options = extend( {}, this.options ); | |
extend( this.options, options ); | |
this._init(); | |
this._initEvents(); | |
} | |
/** | |
* 默认参数 | |
* TiltFx options. | |
*/ | |
TiltFx.prototype.options = { | |
// number of extra image elements (div with background-image) to add to the DOM - min:1, max:5 (for a higher number, it's recommended to remove the transitions of .tilt__front in the stylesheet. | |
extraImgs : 2, | |
// the opacity value for all the image elements. | |
opacity : 0.7, | |
// by default the first layer does not move. | |
bgfixed : true, | |
// image element's movement configuration | |
movement : { | |
perspective : 1000, // perspective value | |
translateX : -10, // a relative movement of -10px to 10px on the x-axis (setting a negative value reverses the direction) | |
translateY : -10, // a relative movement of -10px to 10px on the y-axis | |
translateZ : 20, // a relative movement of -20px to 20px on the z-axis (perspective value must be set). Also, this specific translation is done when the mouse moves vertically. | |
rotateX : 2, // a relative rotation of -2deg to 2deg on the x-axis (perspective value must be set) | |
rotateY : 2, // a relative rotation of -2deg to 2deg on the y-axis (perspective value must be set) | |
rotateZ : 0 // z-axis rotation; by default there's no rotation on the z-axis (perspective value must be set) | |
} | |
} | |
/** | |
* 初始化操作:建立DOM结构 | |
* Initialize: build the necessary structure for the image elements and replace it with the HTML img element. | |
*/ | |
TiltFx.prototype._init = function() { | |
this.tiltWrapper = document.createElement('div'); | |
this.tiltWrapper.className = 'tilt'; | |
// main image element. | |
this.tiltImgBack = document.createElement('div'); | |
this.tiltImgBack.className = 'tilt__back'; | |
this.tiltImgBack.style.backgroundImage = 'url(' + this.el.src + ')'; | |
this.tiltWrapper.appendChild(this.tiltImgBack); | |
// image elements limit. | |
if( this.options.extraImgs < 1 ) { | |
this.options.extraImgs = 1; | |
} | |
else if( this.options.extraImgs > 5 ) { | |
this.options.extraImgs = 5; | |
} | |
if( !this.options.movement.perspective ) { | |
this.options.movement.perspective = 0; | |
} | |
// add the extra image elements. | |
this.imgElems = []; | |
for(var i = 0; i < this.options.extraImgs; ++i) { | |
var el = document.createElement('div'); | |
el.className = 'tilt__front'; | |
el.style.backgroundImage = 'url(' + this.el.src + ')'; | |
el.style.opacity = this.options.opacity; | |
this.tiltWrapper.appendChild(el); | |
this.imgElems.push(el); | |
} | |
if( !this.options.bgfixed ) { | |
this.imgElems.push(this.tiltImgBack); | |
++this.options.extraImgs; | |
} | |
// add it to the DOM and remove original img element. | |
this.el.parentNode.insertBefore(this.tiltWrapper, this.el); | |
this.el.parentNode.removeChild(this.el); | |
// tiltWrapper properties: width/height/left/top | |
this.view = { width : this.tiltWrapper.offsetWidth, height : this.tiltWrapper.offsetHeight }; | |
}; | |
/** | |
* 初始化事件处理函数 | |
* Initialize the events on the main wrapper. | |
*/ | |
TiltFx.prototype._initEvents = function() { | |
var self = this, | |
moveOpts = self.options.movement; | |
// mousemove event.. | |
this.tiltWrapper.addEventListener('mousemove', function(ev) { | |
requestAnimationFrame(function() { | |
// mouse position relative to the document. | |
var mousepos = getMousePos(ev), | |
// document scrolls. | |
docScrolls = {left : document.body.scrollLeft + document.documentElement.scrollLeft, top : document.body.scrollTop + document.documentElement.scrollTop}, | |
bounds = self.tiltWrapper.getBoundingClientRect(), | |
// mouse position relative to the main element (tiltWrapper). | |
relmousepos = { | |
x : mousepos.x - bounds.left - docScrolls.left, | |
y : mousepos.y - bounds.top - docScrolls.top | |
}; | |
// configure the movement for each image element. | |
for(var i = 0, len = self.imgElems.length; i < len; ++i) { | |
var el = self.imgElems[i], | |
rotX = moveOpts.rotateX ? 2 * ((i+1)*moveOpts.rotateX/self.options.extraImgs) / self.view.height * relmousepos.y - ((i+1)*moveOpts.rotateX/self.options.extraImgs) : 0, | |
rotY = moveOpts.rotateY ? 2 * ((i+1)*moveOpts.rotateY/self.options.extraImgs) / self.view.width * relmousepos.x - ((i+1)*moveOpts.rotateY/self.options.extraImgs) : 0, | |
rotZ = moveOpts.rotateZ ? 2 * ((i+1)*moveOpts.rotateZ/self.options.extraImgs) / self.view.width * relmousepos.x - ((i+1)*moveOpts.rotateZ/self.options.extraImgs) : 0, | |
transX = moveOpts.translateX ? 2 * ((i+1)*moveOpts.translateX/self.options.extraImgs) / self.view.width * relmousepos.x - ((i+1)*moveOpts.translateX/self.options.extraImgs) : 0, | |
transY = moveOpts.translateY ? 2 * ((i+1)*moveOpts.translateY/self.options.extraImgs) / self.view.height * relmousepos.y - ((i+1)*moveOpts.translateY/self.options.extraImgs) : 0, | |
transZ = moveOpts.translateZ ? 2 * ((i+1)*moveOpts.translateZ/self.options.extraImgs) / self.view.height * relmousepos.y - ((i+1)*moveOpts.translateZ/self.options.extraImgs) : 0; | |
el.style.WebkitTransform = 'perspective(' + moveOpts.perspective + 'px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(1,0,0,' + rotX + 'deg) rotate3d(0,1,0,' + rotY + 'deg) rotate3d(0,0,1,' + rotZ + 'deg)'; | |
el.style.transform = 'perspective(' + moveOpts.perspective + 'px) translate3d(' + transX + 'px,' + transY + 'px,' + transZ + 'px) rotate3d(1,0,0,' + rotX + 'deg) rotate3d(0,1,0,' + rotY + 'deg) rotate3d(0,0,1,' + rotZ + 'deg)'; | |
} | |
}); | |
}); | |
// reset all when mouse leaves the main wrapper. | |
this.tiltWrapper.addEventListener('mouseleave', function(ev) { | |
setTimeout(function() { | |
for(var i = 0, len = self.imgElems.length; i < len; ++i) { | |
var el = self.imgElems[i]; | |
el.style.WebkitTransform = 'perspective(' + moveOpts.perspective + 'px) translate3d(0,0,0) rotate3d(1,1,1,0deg)'; | |
el.style.transform = 'perspective(' + moveOpts.perspective + 'px) translate3d(0,0,0) rotate3d(1,1,1,0deg)'; | |
} | |
}, 60); | |
}); | |
// window resize | |
window.addEventListener('resize', throttle(function(ev) { | |
// recalculate tiltWrapper properties: width/height/left/top | |
self.view = { width : self.tiltWrapper.offsetWidth, height : self.tiltWrapper.offsetHeight }; | |
}, 50)); | |
}; | |
function init() { | |
// 遍历所有拥有‘title-effect’类的img元素 | |
// search for imgs with the class "tilt-effect" | |
[].slice.call(document.querySelectorAll('img.tilt-effect')).forEach(function(img) { | |
new TiltFx(img, JSON.parse(img.getAttribute('data-tilt-options'))); | |
}); | |
} | |
init(); | |
window.TiltFx = TiltFx; | |
})(window); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment