iOS like Javascript photo gallery breakdown
This post was originally published on Coding Glamour.
Vandaag een breakdown van de swipe-events in de photo gallery die we op onze mobiele website gebruiken.
Verwacht niet een line-to-line breakdown, maar alleen de wijzigingen die
hebben moeten doen om van onze more-or-less statische versie een versie
te maken die swipe events ondersteunt.
Let's get it started
Omdat we gebruik willen maken van de 'touchmove' events gebruiken
we een zelfgeschreven jQuery plugin die de interface iets handiger maakt:
jquery.(s)wipe.js. De syntax voor deze plugin is:
$('#een-element').wipe({
wipeStart: /* */,
wipe: /* */,
wipeEnd: /* */
});
We kunnen aan deze events functies koppelen waardoor we alle logica voor
het swipen op één plaats hebben.
Verschillende manieren voor animaties
Om de animaties te maken hebben we een aantal manieren geprobeerd:
- 1. Voeg twee extra afbeeldingen toe aan de DOM, 1 links van je huidige afbeelding, 1 rechts van je huidige afbeelding. Zet hier een container omheen, en positioneer deze absoluut met de CSS property 'left'. Nadat de animatie klaar is verwissel je de afbeeldingen.
- 2. Teken de afbeeldingen die zichtbaar zijn (bij naar rechts slepen de vorige en de huidige) op een canvas.
- 3. Doe 1. maar dan i.p.v. met 'left' met '-webkit-transform'
Teken voor elke afbeelding voor een object een <img/> tag, en zet deze in een container. Positioneer de container met '-webkit-transform'. De afbeeldingen worden uiteraard niet allemaal ingeladen; dat doen we pas als we verwachten dat we de afbeelding nodig gaan hebben.
N.B. We doen nu alleen webkit omdat iOS, Android en BB OS 6 allen een webkit browser hebben. Wanneer we ook Phone 7 gaan ondersteunen voegen we ook normale CSS3 transforms toe.
Breakdown
De volledige javascript voor de gallery is hier te vinden. Het toevoegen van alle <img/>'s vind je op regel 341. Op regel 26 doen we de eerste test, namelijk het controleren of we webkit transforms supporten:
var isWebkitTransform = (function () {
var div = document.createElement('div');
div.innerHTML = '<div style="-webkit-transition:color 1s linear;"></div>';
var cssTransitionsSupported = (div.firstChild.style.webkitTransition !== undefined);
delete div;
return cssTransitionsSupported;
})();
Op regel 311 gaan we hier uiteindelijk iets mee doen, in de 'herpositioneer' functie:
// x = nieuwe positie
// duration = tijd in ms; als 0 dan positioneren we direct zonder animatie. Handig tijdens swipen met je vinger.
// finishedCallback = functie die aangeroepen moeten worden als de anim klaar is
var repositionContainer = function (x, duration, finishedCallback) {
x = (x | 0); // rond af op hele pixels; is goed voor de performance
// snelle hack om te zorgen dat we hier geen check voor hoeven te doen
finishedCallback = finishedCallback || function () { };
// als we geen webkitTransform hebben kunnen we gebruiken maken van jQuery animate
if (!isWebkitTransform) {
if (!duration) {
scroller.self.css({ left: -x });
finishedCallback();
} else {
scroller.self.animate({ left: -x }, duration, finishedCallback);
}
} else {
// met webkit transform moeten we aanhaken aan het webkitTransitionEnd event
var cb = function () {
finishedCallback();
scroller.self.unbind('webkitTransitionEnd');
}
scroller.self.bind('webkitTransitionEnd', cb);
// al deze properties moeten gezet worden voor een smooth anim op alle devices
scroller.self.css({
'-webkit-transform': 'translate(' + -x + 'px,0px)',
'-webkit-transition-duration': (duration || 0) + 'ms',
'-webkit-backface-visibility': 'hidden',
'-webkit-transition-property': '-webkit-transform'
});
}
};
We haken nu in op de swipe plugin om de container te verplaatsen als iemand met zijn vinger over het scherm gaat (regel 226).
// Swipe events
$(document).ready(function () {
// dit is de scroller container
var $this = scroller.self;
// we applyen het swipe event op onze media container
$(media.self).wipe({
events: {
wipeStart: function () {
if (inTouchMove) {
// zijn we nog in de touch move? dan stop anim
if (isWebkitTransform) {
// in webkit doe je dit door de duration naar 0ms te zetten
// dan eindigt de animatie direct op de juiste locatie
$this.css({
'-webkit-transition-duration': '0ms'
});
} else {
// in jQuery hebben we 'stop'
$this.stop(true, true);
}
}
// setImages() handelt het positioneren en het preloaden van afbeeldingen
setImages();
},
// deze methode gaat af op het 'onTouchMove' event
wipe: function (dx, dy) {
// current = huidige index; dit is dus onze originele positie
var baseLine = current * itemWidth;
// wanneer je aan het begin / einde van de stream bent mag je niet helemaal door swipen
if ((current === 0 && dx > 0) || (current === data.length - 1 && dx < 0)) {
dx = 0.4 * dx;
}
// herpositioneer de container op de originele positie - verplaatsing
repositionContainer(baseLine - dx, 0);
},
// einde van de swipe (gebruiker laat los)
wipeEnd: function (dx, dy) {
// we hanteren een threshold van 20% van de afbeelding; anders blijven we op de huidige
if (dx < 0 && (Math.abs(dx) > itemWidth * 0.2) && data[current + 1]) {
// animeer naar volgende afbeelding
next(true);
} else if (dx > 0 && (Math.abs(dx) > itemWidth * 0.2) && data[current - 1]) {
// animeer naar vorige afbeelding
previous(true);
} else {
// anders teruganimeren naar de originele positie
var baseLine = current * itemWidth;
repositionContainer(baseLine, options.animationSpeed);
setTimeout(function () { inTouchMove = false; }, options.animationSpeed);
}
}
},
minTimeBetweenSwipeEvents: 0
});
});
Omdat we ook via de thumbs onderin kunnen navigeren hebben we in onze refresh() functie een 'smooth' parameter nodig die doorgegeven wordt door next() en previous() (zie vorige functie). Regel 113:
var refresh = function (smooth, previousIdx) {
// wanneer we via een anim willen, en we uberhaupt een andere afbeelding hebben
if (smooth && previousIdx !== current) {
// verander de positie via een animatie
repositionContainer(current * itemWidth, options.animationSpeed, function () {
inTouchMove = false;
setImages();
});
} else {
// anders zet de positie direct zonder vertraging
setImages(true);
}
refreshUi();
};
There are 14 comments on this article, read them on Coding Glamour.