Skip to content

Instantly share code, notes, and snippets.

@vitalyrotari
Created August 1, 2012 15:27
Show Gist options
  • Save vitalyrotari/3227802 to your computer and use it in GitHub Desktop.
Save vitalyrotari/3227802 to your computer and use it in GitHub Desktop.
Mobile Gallery
((window) ->
"use strict"
$ = window.Zepto
screen = window.screen
document = window.document
hasOwn = (obj, key) ->
obj.hasOwnProperty key
Device =
UA_MOBILE: (/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(window.navigator.userAgent.toLowerCase()))
UA_TABLET: (/ipad|android|android\s3\.0|xoom|sch-i800|playbook|tablet|kindle/i.test(window.navigator.userAgent.toLowerCase()))
TYPE_DESKTOP: 0
TYPE_PHONE: 1
TYPE_TABLET: 2
phone:
width:
min: 240
max: 960
height:
min: 320
max: 960
tablet:
width:
min: 240
max: 960
height:
min: 320
max: 960
matchResolutions: (width, height, type) ->
(width >= this[type].width.min and width <= this[type].width.max) and (height >= this[type].height.min and height <= this[type].height.max)
detect: ->
orientation = window.orientation
width = undefined
height = undefined
if orientation isnt `undefined`
width = (if (orientation is 0) then screen.width else screen.height)
height = (if (orientation is 0) then screen.height else screen.width)
if @UA_MOBILE and @matchResolutions(width, height, "phone")
return @TYPE_PHONE
else
return @TYPE_TABLET if @UA_TABLET and @matchResolutions(width, height, "tablet")
@TYPE_DESKTOP
class Gallery
currentPage: 1
constructor: (container) ->
@$ = container: $(container)
@images = (if (images instanceof Array) then images else [])
@init() if @images.length > 0
init: () ->
@deviceType = Device.detect()
@imagesPerPage = (if (@deviceType is Device.TYPE_PHONE) then 4 else 9)
@pagesCount = Math.round(@images.length / @imagesPerPage);
@create()
$(document).on("swipeLeft", (event) =>
event.preventDefault()
@(nextPage)
).on("swipeRight", (event) =>
event.preventDefault()
@prevPage()
)
@reflow()
@preloadImages()
create: () ->
@$.content = $("<div class=\"content\"></div>")
@$.container.append(@$.content)
while p < @pagesCount
content += "<div class=\"page\">"
i = 0
while i < @imagesPerPage
image = @images[lastIndex]
content += "<figure data-image=\"#{image.src}\">"
content += "<figcaption>#{image.caption}</figcaption>" if hasOwn(image, "caption")
content += "</figure>"
lastIndex++
i++
content += "</div>"
p++
@$.content.append(content)
destroy: () ->
$(document).off("swipeLeft swipeRight")
toPage: () ->
nextPage = @currentPage + num
pagesCount = @$.pages.length
if nextPage < 1
nextPage = 1
else
nextPage = pagesCount if pagesCount < nextPage
if @currentPage isnt nextPage
leftPosition = 100 * (nextPage - 1)
@currentPage = nextPage
@$.content.css(left: "-#{leftPosition}%")
@preloadImages()
nextPage: () ->
@toPage 1
prevPage: () ->
@toPage -1
preloadImages: () ->
$page = @$.pages.eq(@currentPage - 1)
$items = undefined
load = () ->
$item = $(this)
imagePath = $item.data("image")
image = document.createElement("img")
image.src = imagePath
$(image).on("load", () ->
$item.css(backgroundImage: "url('#{imagePath}')").addClass("loaded")
)
if $page.length isnt 0
$page.find("figure").not(".loaded").each (index) ->
load.call(this)
reflow: () ->
@$.pages = @$.content.find(".page")
count = @$.pages.length
@$.content.css width: (count * 100) + "%"
@$.pages.css width: (100 / count) + "%"
window.TN = {} unless window.TN instanceof Object
TN.Device = Device
TN.Gallery = Gallery
) @
(function (window) {
"use strict";
var $ = window.Zepto,
screen = window.screen,
document = window.document,
TN = window.TN = window.TN || {};
function hasOwn(obj, key) {
return obj.hasOwnProperty(key);
}
TN.Device = {
UA_MOBILE: (/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(window.navigator.userAgent.toLowerCase())),
UA_TABLET: (/ipad|android|android\s3\.0|xoom|sch-i800|playbook|tablet|kindle/i.test(window.navigator.userAgent.toLowerCase())),
TYPE_DESKTOP: 0,
TYPE_PHONE: 1,
TYPE_TABLET: 2,
phone: {
width: {min: 240, max: 960},
height: {min: 320, max: 960}
},
tablet: {
width: {min: 240, max: 960},
height: {min: 320, max: 960}
},
matchResolutions: function (width, height, type) {
return ((width >= this[type].width.min && width <= this[type].width.max) &&
(height >= this[type].height.min && height <= this[type].height.max));
},
detect: function () {
var orientation = window.orientation,
width,
height;
if (orientation !== undefined) {
width = (orientation === 0) ? screen.width : screen.height;
height = (orientation === 0) ? screen.height : screen.width;
if (this.UA_MOBILE && this.matchResolutions(width, height, 'phone')) {
return this.TYPE_PHONE;
} else {
if (this.UA_TABLET && this.matchResolutions(width, height, 'tablet')) {
return this.TYPE_TABLET;
}
}
}
return this.TYPE_DESKTOP;
}
};
TN.Gallery = function (container, images) {
this.$.container = $(container);
this.images = (images instanceof Array) ? images : [];
if (this.images.length > 0) {
this.init();
}
};
TN.Gallery.prototype.$ = {};
TN.Gallery.prototype.currentPage = 1;
TN.Gallery.prototype.init = function () {
var self = this;
this.deviceType = TN.Device.detect();
this.imagesPerPage = (this.deviceType === TN.Device.TYPE_PHONE) ? 4 : 9;
this.pagesCount = Math.round(this.images.length / this.imagesPerPage);
this.create();
$(document)
.on('swipeLeft', function (event) {
event.preventDefault();
self.nextPage();
})
.on('swipeRight', function (event) {
event.preventDefault();
self.prevPage();
});
this.reflow();
this.preloadImages();
};
TN.Gallery.prototype.create = function () {
var content = '',
lastIndex = 0,
image;
this.$.content = $('<div class="content"></div>');
$(this.$.container).append(this.$.content);
for (var p = 0, i; p < this.pagesCount; p++) {
content += '<div class="page">';
for (i=0; i < this.imagesPerPage; i++) {
image = this.images[lastIndex];
content += '<figure data-image="' + image.src + '">';
if (hasOwn(image, 'caption')) {
content += '<figcaption>' + image.caption + '<\/figcaption>';
}
content += '<\/figure>';
lastIndex++;
}
content += '<\/div>';
}
this.$.content.append(content);
};
TN.Gallery.prototype.destroy = function () {
$(document).off('swipeLeft swipeRight');
};
TN.Gallery.prototype.toPage = function (num) {
var nextPage = this.currentPage + num,
pagesCount = this.$.pages.length,
leftPosition = 0;
if (nextPage < 1) {
nextPage = 1;
} else {
if (pagesCount < nextPage) {
nextPage = pagesCount;
}
}
if (this.currentPage !== nextPage) {
leftPosition = 100 * (nextPage - 1);
this.currentPage = nextPage;
this.$.content
.css({left: String(-leftPosition) + '%'});
this.preloadImages();
}
};
TN.Gallery.prototype.nextPage = function () {
this.toPage(1);
};
TN.Gallery.prototype.prevPage = function () {
this.toPage(-1);
};
TN.Gallery.prototype.preloadImages = function () {
var $page = this.$.pages.eq(this.currentPage - 1),
$items,
load = function () {
var $item = $(this),
imagePath = $item.data('image'),
image = document.createElement('img');
image.src = imagePath;
$(image).on('load', function () {
$item
.css({backgroundImage: "url('" + imagePath + "')"})
.addClass('loaded');
});
};
if ($page.length !== 0) {
$page.find('figure')
.not('.loaded')
.each(function (index) {
load.call(this);
});
}
};
TN.Gallery.prototype.reflow = function () {
this.$.pages = this.$.content.find('.page');
var count = this.$.pages.length;
this.$.content.css({width: (count * 100) + '%'});
this.$.pages.css({width: (100 / count) + '%'});
};
}(this));
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href="css/styles.css" rel="stylesheet">
<script src="http://zeptojs.com/zepto.min.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
<section class="gallery"></section>
<script src="js/app.js"></script>
<script>
(function ($) {
"use strict";
var caption = '<h2>Description<\/h2><small>adition text here...<\/small>',
imagesLists = [{
"src": 'http://leclick.ru/files/restaurants/photo/5523.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5522.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5521.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5519.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5466.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5517.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5516.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5513.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5526.jpg',
"caption": caption
},
{
"src": 'http://leclick.ru/files/restaurants/photo/3259.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/3258.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/3257.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/3256.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/3255.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5697.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5696.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5695.jpg',
"caption": caption
}, {
"src": 'http://leclick.ru/files/restaurants/photo/5694.jpg',
"caption": caption
},
];
$(function () {
var gallery = new TN.Gallery('.gallery', imagesLists);
});
}(Zepto));
</script>
</body>
</html>
html, body {
height: 100%;
}
body {
background: #444;
padding: 0;
margin: 0;
font-family: "Helvetica Neue", Arial;
font-size: 13px;
line-height: 18px;
overflow: hidden;
}
.gallery {
background: red;
position: absolute;
top: 0;
left: 0;
button: 0;
width: 100%;
oveflow: hidden;
}
.gallery .content {
padding: 10px;
position: absolute;
top: 0;
left: 0;
height: 100%;
-webkit-transition: 0.2s all ease-in;
-moz-transition: 0.2s all ease-in;
-ms-transition: 0.2s all ease-in;
-o-transition: 0.2s all ease-in;
transition: 0.2s all ease-in;
-webkit-transform: translate3d(0,0,0);
-moz-transform: translate3d(0,0,0);
-ms-transform: translate3d(0,0,0);
-o-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
.gallery .page {
float: left;
margin: 0;
position: relative;
}
.gallery figure {
background-color: black;
background-image: url('../img/295.gif');
background-repeat: no-repeat;
background-position: 50% 50%;
box-shadow: 0 0 8px rgba(0, 0, 0, .6);
position: relative;
margin: 0 20px 20px 0;
width: 320px;
height: 198px;
float: left;
overflow: hidden;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.gallery figcaption {
background-color: rgba(0, 0, 0, .6);
color: white;
position: absolute;
left: -20px;
right: 0;
bottom: 0;
padding: 10px 10px;
opacity: 0;
-webkit-transition: 0.4s all ease-in .2s;
-moz-transition: 0.25s all ease-in;
-ms-transition: 0.25s all ease-in;
-o-transition: 0.25s all ease-in;
transition: 0.4s all ease-in .2s;
-webkit-transform: translate3d(0,0,0);
-moz-transform: translate3d(0,0,0);
-ms-transform: translate3d(0,0,0);
-o-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
.gallery figcaption h2 {
padding: 0;
margin: 0;
}
.gallery figure.loaded {
background-size: 100% auto;
}
.gallery figure.loaded figcaption {
opacity: 1;
left: 0;
}
/* Portrait */
@media screen and (orientation:portrait) {
.gallery figure {
width: 236px;
height: 283px;
}
.gallery figure.loaded {
background-size: 200% auto;
}
}
/* Landscape */
@media screen and (orientation:landscape) {
}
/* Tablets */
@media screen
and (min-width: 768px)
and (max-width: 1023px)
and (orientation: portrait) {
.gallery figure:nth-child(3n) {
margin-right: 0;
}
}
@media screen
and (min-width: 1024px)
and (max-width: 1280px)
and (orientation:landscape) {
.gallery figure:nth-child(3n) {
margin-right: 0;
}
}
/* iPhone Portrait */
@media screen
and (max-width: 320px)
and (orientation: portrait) {
.gallery figure {
width: 140px;
height: 187px
}
.gallery figure:nth-of-type(2n) {
margin-right: 0;
}
}
/* iPhone Landscape */
@media screen
and (min-width: 321px)
and (max-width: 480px)
and (orientation:landscape) {
.gallery figure {
width: 220px;
height: 140px;
}
.gallery figure:nth-of-type(2n) {
margin-right: 0;
}
}
@mixin transition(@params) {
-webkit-transition: @params;
-moz-transition: @params;
-ms-transition: @params;
-o-transition: @params;
transition: @params;
}
@mixin transform(@params) {
-webkit-transform: @params;
-moz-transform: @params;
-ms-transform: @params;
-o-transform: @params;
transform: @params;
}
@mixin user-select (@param: none) {
-webkit-touch-callout: @param;
-webkit-user-select: @param;
-khtml-user-select: @param;
-moz-user-select: @param;
-ms-user-select: @param;
user-select: @param;
}
html, body {
height: 100%;
}
body {
background: #444;
padding: 0;
margin: 0;
font-family: "Helvetica Neue", Arial;
font-size: 13px;
line-height: 18px;
overflow: hidden;
}
.gallery {
background: red;
position: absolute;
top: 0;
left: 0;
button: 0;
width: 100%;
oveflow: hidden;
.content {
padding: 10px;
position: absolute;
top: 0;
left: 0;
height: 100%;
@include transition(0.2s all ease-in);
@include transform(translate3d(0,0,0));
}
.page {
float: left;
margin: 0;
position: relative;
}
figure {
background-color: black;
background-image: url('../img/295.gif');
background-repeat: no-repeat;
background-position: 50% 50%;
box-shadow: 0 0 8px rgba(0, 0, 0, .6);
position: relative;
margin: 0 20px 20px 0;
width: 320px;
height: 198px;
float: left;
overflow: hidden;
@include user-select;
&.loaded {
background-size: 100% auto;
figcaption {
opacity: 1;
left: 0;
}
}
}
figcaption {
background-color: rgba(0, 0, 0, .6);
color: white;
position: absolute;
left: -20px;
right: 0;
bottom: 0;
padding: 10px 10px;
opacity: 0;
@include transition(0.4s all ease-in .2s);
@include transform(translate3d(0,0,0));
h2 {
padding: 0;
margin: 0;
}
}
}
/* Portrait */
@media screen and (orientation:portrait) {
.gallery figure {
width: 236px;
height: 283px;
&.loaded {
background-size: 200% auto;
}
}
}
/* Landscape */
@media screen and (orientation:landscape) {
}
/* Tablets */
@media screen
and (min-width: 768px)
and (max-width: 1023px)
and (orientation: portrait) {
.gallery figure:nth-child(3n) {
margin-right: 0;
}
}
@media screen
and (min-width: 1024px)
and (max-width: 1280px)
and (orientation:landscape) {
.gallery figure:nth-child(3n) {
margin-right: 0;
}
}
/* iPhone Portrait */
@media screen
and (max-width: 320px)
and (orientation: portrait) {
.gallery figure {
width: 140px;
height: 187px
&:nth-of-type(2n) {
margin-right: 0;
}
}
}
/* iPhone Landscape */
@media screen
and (min-width: 321px)
and (max-width: 480px)
and (orientation:landscape) {
.gallery figure {
width: 220px;
height: 140px;
&:nth-of-type(2n) {
margin-right: 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment