PhotoTilt

by HYEONGJINKIM

HTML

<!-- access to the HEAD element -->
<meta name='viewport' content='initial-scale=1.0, width=device-width' />

CSS

html,
body {
	overflow:hidden;
	height: 100%;
	width: 100%;
	padding: 0;
	margin: 0;
}

.mask {
	position: absolute;
	top:0;
	left: 0;
	right: 0;
	bottom: 0;
	overflow: hidden;
}

.mask img {
	position: absolute;
	left: 0;
	top: 0;
}

.tilt-bar {
	position: fixed;
	top: 30px;
	left: 10px;
	right: 10px;
	height: 3px;
	z-index: 1;
	background-color: rgba(255,255,255,0.3);
}

.tilt-indicator {
	background-color: rgba(255,255,255,0.8);
	height: 3px;
	position: absolute;
}

.mask img,
.tilt-indicator {
	-webkit-transition: -webkit-transform 0.2s linear;
	-moz-transition: -moz-transform 0.2s linear;
	-ms-transition: -ms-transform 0.2s linear;
	transition: transform 0.2s linear;

	-webkit-backface-visibility: hidden;
	-moz-backface-visibility: hidden;
	-ms-backface-visibility: hidden;
	backface-visibility: hidden;

	-webkit-perspective: 1000;
	-moz-perspective: 1000;
	-ms-perspective: 1000;
	perspective: 1000;

	-webkit-transform: translatez(0);
	-moz-transform: translatez(0);
	-ms-transform: translatez(0);
	transform: translatez(0);
}

JavaScript

var PhotoTilt = function(options) {

	'use strict';

	var imgUrl = options.url,
		lowResUrl = options.lowResUrl,
		container = options.container || document.body,
		latestTilt = 0,
		viewport,
		imgData,
		img,
		imgLoader,
		delta,
		centerOffset,
		tiltBarWidth,
		tiltCenterOffset,
		tiltBarIndicatorWidth,
		tiltBarIndicator,
		config;

	config = {
		maxTilt: options.maxTilt || 20,
		twoPhase: options.lowResUrl || false,
		reverseTilt: options.reverseTilt || false
	};

	window.requestAnimationFrame =  window.requestAnimationFrame ||
									window.mozRequestAnimationFrame ||
									window.webkitRequestAnimationFrame ||
									window.msRequestAnimationFrame;

	var init = function() {

		var url = config.twoPhase ? lowResUrl : imgUrl;

		generateViewPort();

		preloadImg(url, function() {

			img = imgLoader.cloneNode(false);
			generateImgData();
			imgLoader = null;

			if (config.twoPhase) {

				preloadImg(imgUrl, function() {
					img.src = imgLoader.src;
					imgLoader = null;
				});

			}

			render();
			addEventListeners();

		});

	};

	var updatePosition = function() {

		var tilt = latestTilt,
			pxToMove;

		if (tilt > 0) {
			tilt = Math.min(tilt, config.maxTilt);
		} else {
			tilt = Math.max(tilt, config.maxTilt * -1);
		}

		if (!config.reverseTilt) {
			tilt = tilt * -1;
		}

		pxToMove = (tilt * centerOffset) / config.maxTilt;

		updateImgPosition(imgData,  (centerOffset + pxToMove) * -1);

		updateTiltBar(tilt);

		window.requestAnimationFrame(updatePosition);

	};

	var updateTiltBar = function(tilt) {

		var pxToMove = (tilt * ((tiltBarWidth - tiltBarIndicatorWidth) / 2)) / config.maxTilt;
		setTranslateX(tiltBarIndicator, (tiltCenterOffset + pxToMove) );

	};

	var updateImgPosition = function(imgData, pxToMove) {
		setTranslateX(img, pxToMove);
	};

	var addEventListeners = function() {

		if (window.DeviceOrientationEvent) {

			var averageGamma = [];

			window.addEventListener('deviceorientation', function(eventData) {

				if...