Created
April 10, 2015 12:36
-
-
Save dimsemenov/c84a2f64b9ec70fd2ec3 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
//function(template, UiClass, items, options) | |
var self = this; | |
/** | |
* Static vars, don't change unless you know what you're doing. | |
*/ | |
var DOUBLE_TAP_RADIUS = 25, | |
NUM_HOLDERS = 3; | |
/** | |
* Options | |
*/ | |
var _options = { | |
allowPanToNext:true, | |
spacing: 0.12, | |
bgOpacity: 1, | |
mouseUsed: false, | |
loop: true, | |
pinchToClose: true, | |
closeOnScroll: true, | |
closeOnVerticalDrag: true, | |
hideAnimationDuration: 333, | |
showAnimationDuration: 333, | |
showHideOpacity: false, | |
focus: true, | |
escKey: true, | |
arrowKeys: true, | |
mainScrollEndFriction: 0.35, | |
panEndFriction: 0.35, | |
isClickableElement: function(el) { | |
return el.tagName === 'A'; | |
}, | |
getDoubleTapZoom: function(isMouseClick, item) { | |
if(isMouseClick) { | |
return 1; | |
} else { | |
return item.initialZoomLevel < 0.7 ? 1 : 1.5; | |
} | |
}, | |
maxSpreadZoom: 2, | |
// not fully implemented yet | |
scaleMode: 'fit', // TODO | |
modal: true, // TODO | |
alwaysFadeIn: false // TODO | |
}; | |
framework.extend(_options, options); | |
/** | |
* Private helper variables & functions | |
*/ | |
var _getEmptyPoint = function() { | |
return {x:0,y:0}; | |
}; | |
var _isOpen, | |
_isDestroying, | |
_closedByScroll, | |
_currentItemIndex, | |
_containerStyle, | |
_containerShiftIndex, | |
_currPanDist = _getEmptyPoint(), | |
_startPanOffset = _getEmptyPoint(), | |
_panOffset = _getEmptyPoint(), | |
_upMoveEvents, // drag move, drag end & drag cancel events array | |
_downEvents, // drag start events array | |
_globalEventHandlers, | |
_viewportSize = {}, | |
_currZoomLevel, | |
_startZoomLevel, | |
_translatePrefix, | |
_translateSufix, | |
_updateSizeInterval, | |
_itemsNeedUpdate, | |
_currPositionIndex = 0, | |
_offset = {}, | |
_slideSize = _getEmptyPoint(), // size of slide area, including spacing | |
_itemHolders, | |
_prevItemIndex, | |
_indexDiff = 0, // difference of indexes since last content update | |
_dragStartEvent, | |
_dragMoveEvent, | |
_dragEndEvent, | |
_dragCancelEvent, | |
_transformKey, | |
_pointerEventEnabled, | |
_isFixedPosition = true, | |
_likelyTouchDevice, | |
_modules = [], | |
_requestAF, | |
_cancelAF, | |
_initalClassName, | |
_initalWindowScrollY, | |
_oldIE, | |
_currentWindowScrollY, | |
_features, | |
_windowVisibleSize = {}, | |
// Registers PhotoSWipe module (History, Controller ...) | |
_registerModule = function(name, module) { | |
framework.extend(self, module.publicMethods); | |
_modules.push(name); | |
}, | |
_getLoopedId = function(index) { | |
var numSlides = _getNumItems(); | |
if(index > numSlides - 1) { | |
return index - numSlides; | |
} else if(index < 0) { | |
return numSlides + index; | |
} | |
return index; | |
}, | |
// Micro bind/trigger | |
_listeners = {}, | |
_listen = function(name, fn) { | |
if(!_listeners[name]) { | |
_listeners[name] = []; | |
} | |
return _listeners[name].push(fn); | |
}, | |
_shout = function(name) { | |
var listeners = _listeners[name]; | |
if(listeners) { | |
var args = Array.prototype.slice.call(arguments); | |
args.shift(); | |
for(var i = 0; i < listeners.length; i++) { | |
listeners[i].apply(self, args); | |
} | |
} | |
}, | |
_getCurrentTime = function() { | |
return new Date().getTime(); | |
}, | |
_applyBgOpacity = function(opacity) { | |
_bgOpacity = opacity; | |
self.bg.style.opacity = opacity * _options.bgOpacity; | |
}, | |
_applyZoomTransform = function(styleObj,x,y,zoom) { | |
styleObj[_transformKey] = _translatePrefix + x + 'px, ' + y + 'px' + _translateSufix + ' scale(' + zoom + ')'; | |
}, | |
_applyCurrentZoomPan = function() { | |
if(_currZoomElementStyle) { | |
_applyZoomTransform(_currZoomElementStyle, _panOffset.x, _panOffset.y, _currZoomLevel); | |
} | |
}, | |
_applyZoomPanToItem = function(item) { | |
if(item.container) { | |
_applyZoomTransform(item.container.style, | |
item.initialPosition.x, | |
item.initialPosition.y, | |
item.initialZoomLevel); | |
} | |
}, | |
_setTranslateX = function(x, elStyle) { | |
elStyle[_transformKey] = _translatePrefix + x + 'px, 0px' + _translateSufix; | |
}, | |
_moveMainScroll = function(x, dragging) { | |
if(!_options.loop && dragging) { | |
// if of current item during scroll (float) | |
var newSlideIndexOffset = _currentItemIndex + (_slideSize.x * _currPositionIndex - x)/_slideSize.x; | |
var delta = Math.round(x - _mainScrollPos.x); | |
if( (newSlideIndexOffset < 0 && delta > 0) || | |
(newSlideIndexOffset >= _getNumItems()-1 && delta < 0) ) { | |
x = _mainScrollPos.x + delta * _options.mainScrollEndFriction; | |
} | |
} | |
_mainScrollPos.x = x; | |
_setTranslateX(x, _containerStyle); | |
}, | |
_calculatePanOffset = function(axis, zoomLevel) { | |
var m = _midZoomPoint[axis] - _offset[axis]; | |
return _startPanOffset[axis] + _currPanDist[axis] + m - m * ( zoomLevel / _startZoomLevel ); | |
}, | |
_equalizePoints = function(p1, p2) { | |
p1.x = p2.x; | |
p1.y = p2.y; | |
if(p2.id) { | |
p1.id = p2.id; | |
} | |
}, | |
_roundPoint = function(p) { | |
p.x = Math.round(p.x); | |
p.y = Math.round(p.y); | |
}, | |
_mouseMoveTimeout = null, | |
_onFirstMouseMove = function() { | |
// Wait until mouse move event is fired at least twice during 100ms | |
// We do this, because some mobile browsers trigger it on touchstart | |
if(_mouseMoveTimeout ) { | |
framework.unbind(document, 'mousemove', _onFirstMouseMove); | |
framework.addClass(template, 'pswp--has_mouse'); | |
_options.mouseUsed = true; | |
_shout('mouseUsed'); | |
} | |
_mouseMoveTimeout = setTimeout(function() { | |
_mouseMoveTimeout = null; | |
}, 100); | |
}, | |
_bindEvents = function() { | |
framework.bind(document, 'keydown', self); | |
if(_features.transform) { | |
// don't bind click event in browsers that don't support transform (mostly IE8) | |
framework.bind(self.scrollWrap, 'click', self); | |
} | |
if(!_options.mouseUsed) { | |
framework.bind(document, 'mousemove', _onFirstMouseMove); | |
} | |
framework.bind(window, 'resize scroll', self); | |
_shout('bindEvents'); | |
}, | |
_unbindEvents = function() { | |
framework.unbind(window, 'resize', self); | |
framework.unbind(window, 'scroll', _globalEventHandlers.scroll); | |
framework.unbind(document, 'keydown', self); | |
framework.unbind(document, 'mousemove', _onFirstMouseMove); | |
if(_features.transform) { | |
framework.unbind(self.scrollWrap, 'click', self); | |
} | |
if(_isDragging) { | |
framework.unbind(window, _upMoveEvents, self); | |
} | |
_shout('unbindEvents'); | |
}, | |
_calculatePanBounds = function(zoomLevel, update) { | |
var bounds = _calculateItemSize( self.currItem, _viewportSize, zoomLevel ); | |
if(update) { | |
_currPanBounds = bounds; | |
} | |
return bounds; | |
}, | |
_getMinZoomLevel = function(item) { | |
if(!item) { | |
item = self.currItem; | |
} | |
return item.initialZoomLevel; | |
}, | |
_getMaxZoomLevel = function(item) { | |
if(!item) { | |
item = self.currItem; | |
} | |
return item.w > 0 ? _options.maxSpreadZoom : 1; | |
}, | |
// Return true if offset is out of the bounds | |
_modifyDestPanOffset = function(axis, destPanBounds, destPanOffset, destZoomLevel) { | |
if(destZoomLevel === self.currItem.initialZoomLevel) { | |
destPanOffset[axis] = self.currItem.initialPosition[axis]; | |
return true; | |
} else { | |
destPanOffset[axis] = _calculatePanOffset(axis, destZoomLevel); | |
if(destPanOffset[axis] > destPanBounds.min[axis]) { | |
destPanOffset[axis] = destPanBounds.min[axis]; | |
return true; | |
} else if(destPanOffset[axis] < destPanBounds.max[axis] ) { | |
destPanOffset[axis] = destPanBounds.max[axis]; | |
return true; | |
} | |
} | |
return false; | |
}, | |
_setupTransforms = function() { | |
if(_transformKey) { | |
// setup 3d transforms | |
var allow3dTransform = _features.perspective && !_likelyTouchDevice; | |
_translatePrefix = 'translate' + (allow3dTransform ? '3d(' : '('); | |
_translateSufix = _features.perspective ? ', 0px)' : ')'; | |
return; | |
} | |
// Override zoom/pan/move functions in case old browser is used (most likely IE) | |
// (so they use left/top/width/height, instead of CSS transform) | |
_transformKey = 'left'; | |
framework.addClass(template, 'pswp--ie'); | |
_setTranslateX = function(x, elStyle) { | |
elStyle.left = x + 'px'; | |
}; | |
_applyZoomPanToItem = function(item) { | |
var zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio, | |
s = item.container.style, | |
w = zoomRatio * item.w, | |
h = zoomRatio * item.h; | |
s.width = w + 'px'; | |
s.height = h + 'px'; | |
s.left = item.initialPosition.x + 'px'; | |
s.top = item.initialPosition.y + 'px'; | |
}; | |
_applyCurrentZoomPan = function() { | |
if(_currZoomElementStyle) { | |
var s = _currZoomElementStyle, | |
item = self.currItem, | |
zoomRatio = item.fitRatio > 1 ? 1 : item.fitRatio, | |
w = zoomRatio * item.w, | |
h = zoomRatio * item.h; | |
s.width = w + 'px'; | |
s.height = h + 'px'; | |
s.left = _panOffset.x + 'px'; | |
s.top = _panOffset.y + 'px'; | |
} | |
}; | |
}, | |
_onKeyDown = function(e) { | |
var keydownAction = ''; | |
if(_options.escKey && e.keyCode === 27) { | |
keydownAction = 'close'; | |
} else if(_options.arrowKeys) { | |
if(e.keyCode === 37) { | |
keydownAction = 'prev'; | |
} else if(e.keyCode === 39) { | |
keydownAction = 'next'; | |
} | |
} | |
if(keydownAction) { | |
// don't do anything if special key pressed to prevent from overriding default browser actions | |
// e.g. in Chrome on Mac cmd+arrow-left returns to previous page | |
if( !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey ) { | |
if(e.preventDefault) { | |
e.preventDefault(); | |
} else { | |
e.returnValue = false; | |
} | |
self[keydownAction](); | |
} | |
} | |
}, | |
_onGlobalClick = function(e) { | |
if(!e) { | |
return; | |
} | |
// don't allow click event to pass through when triggering after drag or some other gesture | |
if(_moved || _zoomStarted || _mainScrollAnimating || _verticalDragInitiated) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
} | |
}, | |
_updatePageScrollOffset = function() { | |
self.setScrollOffset(0, framework.getScrollY()); | |
}; | |
// Micro animation engine | |
var _animations = {}, | |
_numAnimations = 0, | |
_stopAnimation = function(name) { | |
if(_animations[name]) { | |
if(_animations[name].raf) { | |
_cancelAF( _animations[name].raf ); | |
} | |
_numAnimations--; | |
delete _animations[name]; | |
} | |
}, | |
_registerStartAnimation = function(name) { | |
if(_animations[name]) { | |
_stopAnimation(name); | |
} | |
if(!_animations[name]) { | |
_numAnimations++; | |
_animations[name] = {}; | |
} | |
}, | |
_stopAllAnimations = function() { | |
for (var prop in _animations) { | |
if( _animations.hasOwnProperty( prop ) ) { | |
_stopAnimation(prop); | |
} | |
} | |
}, | |
_animateProp = function(name, b, endProp, d, easingFn, onUpdate, onComplete) { | |
var startAnimTime = _getCurrentTime(), t; | |
_registerStartAnimation(name); | |
var animloop = function(){ | |
if ( _animations[name] ) { | |
t = _getCurrentTime() - startAnimTime; // time diff | |
//b - beginning (start prop) | |
//d - anim duration | |
if ( t >= d ) { | |
_stopAnimation(name); | |
onUpdate(endProp); | |
if(onComplete) { | |
onComplete(); | |
} | |
return; | |
} | |
onUpdate( (endProp - b) * easingFn(t/d) + b ); | |
_animations[name].raf = _requestAF(animloop); | |
} | |
}; | |
animloop(); | |
}; | |
var publicMethods = { | |
// make a few local variables and functions public | |
shout: _shout, | |
listen: _listen, | |
viewportSize: _viewportSize, | |
options: _options, | |
isMainScrollAnimating: function() { | |
return _mainScrollAnimating; | |
}, | |
getZoomLevel: function() { | |
return _currZoomLevel; | |
}, | |
getCurrentIndex: function() { | |
return _currentItemIndex; | |
}, | |
isDragging: function() { | |
return _isDragging; | |
}, | |
isZooming: function() { | |
return _isZooming; | |
}, | |
setScrollOffset: function(x,y) { | |
_offset.x = x; | |
_currentWindowScrollY = _offset.y = y; | |
_shout('updateScrollOffset', _offset); | |
}, | |
applyZoomPan: function(zoomLevel,panX,panY) { | |
_panOffset.x = panX; | |
_panOffset.y = panY; | |
_currZoomLevel = zoomLevel; | |
_applyCurrentZoomPan(); | |
}, | |
init: function() { | |
if(_isOpen || _isDestroying) { | |
return; | |
} | |
var i; | |
self.framework = framework; // basic function | |
self.template = template; // root DOM element of PhotoSwipe | |
self.bg = framework.getChildByClass(template, 'pswp__bg'); | |
_initalClassName = template.className; | |
_isOpen = true; | |
_features = framework.detectFeatures(); | |
_requestAF = _features.raf; | |
_cancelAF = _features.caf; | |
_transformKey = _features.transform; | |
_oldIE = _features.oldIE; | |
self.scrollWrap = framework.getChildByClass(template, 'pswp__scroll-wrap'); | |
self.container = framework.getChildByClass(self.scrollWrap, 'pswp__container'); | |
_containerStyle = self.container.style; // for fast access | |
// Objects that hold slides (there are only 3 in DOM) | |
self.itemHolders = _itemHolders = [ | |
{el:self.container.children[0] , wrap:0, index: -1}, | |
{el:self.container.children[1] , wrap:0, index: -1}, | |
{el:self.container.children[2] , wrap:0, index: -1} | |
]; | |
// hide nearby item holders until initial zoom animation finishes (to avoid extra Paints) | |
_itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'none'; | |
_setupTransforms(); | |
// Setup global events | |
_globalEventHandlers = { | |
resize: self.updateSize, | |
scroll: _updatePageScrollOffset, | |
keydown: _onKeyDown, | |
click: _onGlobalClick | |
}; | |
// disable show/hide effects on old browsers that don't support CSS animations or transforms, | |
// old IOS, Android and Opera mobile. Blackberry seems to work fine, even older models. | |
var oldPhone = _features.isOldIOSPhone || _features.isOldAndroid || _features.isMobileOpera; | |
if(!_features.animationName || !_features.transform || oldPhone) { | |
_options.showAnimationDuration = _options.hideAnimationDuration = 0; | |
} | |
// init modules | |
for(i = 0; i < _modules.length; i++) { | |
self['init' + _modules[i]](); | |
} | |
// init | |
if(UiClass) { | |
var ui = self.ui = new UiClass(self, framework); | |
ui.init(); | |
} | |
_shout('firstUpdate'); | |
_currentItemIndex = _currentItemIndex || _options.index || 0; | |
// validate index | |
if( isNaN(_currentItemIndex) || _currentItemIndex < 0 || _currentItemIndex >= _getNumItems() ) { | |
_currentItemIndex = 0; | |
} | |
self.currItem = _getItemAt( _currentItemIndex ); | |
if(_features.isOldIOSPhone || _features.isOldAndroid) { | |
_isFixedPosition = false; | |
} | |
template.setAttribute('aria-hidden', 'false'); | |
if(_options.modal) { | |
if(!_isFixedPosition) { | |
template.style.position = 'absolute'; | |
template.style.top = framework.getScrollY() + 'px'; | |
} else { | |
template.style.position = 'fixed'; | |
} | |
} | |
if(_currentWindowScrollY === undefined) { | |
_shout('initialLayout'); | |
_currentWindowScrollY = _initalWindowScrollY = framework.getScrollY(); | |
} | |
// add classes to root element of PhotoSwipe | |
var rootClasses = 'pswp--open '; | |
if(_options.mainClass) { | |
rootClasses += _options.mainClass + ' '; | |
} | |
if(_options.showHideOpacity) { | |
rootClasses += 'pswp--animate_opacity '; | |
} | |
rootClasses += _likelyTouchDevice ? 'pswp--touch' : 'pswp--notouch'; | |
rootClasses += _features.animationName ? ' pswp--css_animation' : ''; | |
rootClasses += _features.svg ? ' pswp--svg' : ''; | |
framework.addClass(template, rootClasses); | |
self.updateSize(); | |
// initial update | |
_containerShiftIndex = -1; | |
_indexDiff = null; | |
for(i = 0; i < NUM_HOLDERS; i++) { | |
_setTranslateX( (i+_containerShiftIndex) * _slideSize.x, _itemHolders[i].el.style); | |
} | |
if(!_oldIE) { | |
framework.bind(self.scrollWrap, _downEvents, self); // no dragging for old IE | |
} | |
_listen('initialZoomInEnd', function() { | |
self.setContent(_itemHolders[0], _currentItemIndex-1); | |
self.setContent(_itemHolders[2], _currentItemIndex+1); | |
_itemHolders[0].el.style.display = _itemHolders[2].el.style.display = 'block'; | |
if(_options.focus) { | |
// focus causes layout, | |
// which causes lag during the animation, | |
// that's why we delay it untill the initial zoom transition ends | |
template.focus(); | |
} | |
_bindEvents(); | |
}); | |
// set content for center slide (first time) | |
self.setContent(_itemHolders[1], _currentItemIndex); | |
self.updateCurrItem(); | |
_shout('afterInit'); | |
if(!_isFixedPosition) { | |
// On all versions of iOS lower than 8.0, we check size of viewport every second. | |
// | |
// This is done to detect when Safari top & bottom bars appear, | |
// as this action doesn't trigger any events (like resize). | |
// | |
// On iOS8 they fixed this. | |
// | |
// 10 Nov 2014: iOS 7 usage ~40%. iOS 8 usage 56%. | |
_updateSizeInterval = setInterval(function() { | |
if(!_numAnimations && !_isDragging && !_isZooming && (_currZoomLevel === self.currItem.initialZoomLevel) ) { | |
self.updateSize(); | |
} | |
}, 1000); | |
} | |
framework.addClass(template, 'pswp--visible'); | |
}, | |
// Closes the gallery, then destroy it | |
close: function() { | |
if(!_isOpen) { | |
return; | |
} | |
_isOpen = false; | |
_isDestroying = true; | |
_shout('close'); | |
_unbindEvents(); | |
_showOrHide( self.currItem, null, true, self.destroy); | |
}, | |
// destroys gallery (unbinds events, cleans up intervals and timeouts to avoid memory leaks) | |
destroy: function() { | |
_shout('destroy'); | |
if(_showOrHideTimeout) { | |
clearTimeout(_showOrHideTimeout); | |
} | |
//if(_options.modal) { | |
template.setAttribute('aria-hidden', 'true'); | |
template.className = _initalClassName; | |
//} | |
if(_updateSizeInterval) { | |
clearInterval(_updateSizeInterval); | |
} | |
framework.unbind(self.scrollWrap, _downEvents, self); | |
// we unbind lost event at the end, as closing animation may depend on it | |
framework.unbind(window, 'scroll', self); | |
_stopDragUpdateLoop(); | |
_stopAllAnimations(); | |
_listeners = null; | |
}, | |
/** | |
* Pan image to position | |
* @param {Number} x | |
* @param {Number} y | |
* @param {Boolean} force Will ignore bounds if set to true. | |
*/ | |
panTo: function(x,y,force) { | |
if(!force) { | |
if(x > _currPanBounds.min.x) { | |
x = _currPanBounds.min.x; | |
} else if(x < _currPanBounds.max.x) { | |
x = _currPanBounds.max.x; | |
} | |
if(y > _currPanBounds.min.y) { | |
y = _currPanBounds.min.y; | |
} else if(y < _currPanBounds.max.y) { | |
y = _currPanBounds.max.y; | |
} | |
} | |
_panOffset.x = x; | |
_panOffset.y = y; | |
_applyCurrentZoomPan(); | |
}, | |
handleEvent: function (e) { | |
e = e || window.event; | |
if(_globalEventHandlers[e.type]) { | |
_globalEventHandlers[e.type](e); | |
} | |
}, | |
goTo: function(index) { | |
index = _getLoopedId(index); | |
var diff = index - _currentItemIndex; | |
_indexDiff = diff; | |
_currentItemIndex = index; | |
self.currItem = _getItemAt( _currentItemIndex ); | |
_currPositionIndex -= diff; | |
_moveMainScroll(_slideSize.x * _currPositionIndex); | |
_stopAllAnimations(); | |
_mainScrollAnimating = false; | |
self.updateCurrItem(); | |
}, | |
next: function() { | |
self.goTo( _currentItemIndex + 1); | |
}, | |
prev: function() { | |
self.goTo( _currentItemIndex - 1); | |
}, | |
// update current zoom/pan objects | |
updateCurrZoomItem: function(emulateSetContent) { | |
if(emulateSetContent) { | |
_shout('beforeChange', 0); | |
} | |
// itemHolder[1] is middle (current) item | |
if(_itemHolders[1].el.children.length) { | |
var zoomElement = _itemHolders[1].el.children[0]; | |
if( framework.hasClass(zoomElement, 'pswp__zoom-wrap') ) { | |
_currZoomElementStyle = zoomElement.style; | |
} else { | |
_currZoomElementStyle = null; | |
} | |
} else { | |
_currZoomElementStyle = null; | |
} | |
_currPanBounds = self.currItem.bounds; | |
_startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel; | |
_panOffset.x = _currPanBounds.center.x; | |
_panOffset.y = _currPanBounds.center.y; | |
if(emulateSetContent) { | |
_shout('afterChange'); | |
} | |
}, | |
invalidateCurrItems: function() { | |
_itemsNeedUpdate = true; | |
for(var i = 0; i < NUM_HOLDERS; i++) { | |
if( _itemHolders[i].item ) { | |
_itemHolders[i].item.needsUpdate = true; | |
} | |
} | |
}, | |
updateCurrItem: function(beforeAnimation) { | |
if(_indexDiff === 0) { | |
return; | |
} | |
var diffAbs = Math.abs(_indexDiff), | |
tempHolder; | |
if(beforeAnimation && diffAbs < 2) { | |
return; | |
} | |
self.currItem = _getItemAt( _currentItemIndex ); | |
_shout('beforeChange', _indexDiff); | |
if(diffAbs >= NUM_HOLDERS) { | |
_containerShiftIndex += _indexDiff + (_indexDiff > 0 ? -NUM_HOLDERS : NUM_HOLDERS); | |
diffAbs = NUM_HOLDERS; | |
} | |
for(var i = 0; i < diffAbs; i++) { | |
if(_indexDiff > 0) { | |
tempHolder = _itemHolders.shift(); | |
_itemHolders[NUM_HOLDERS-1] = tempHolder; // move first to last | |
_containerShiftIndex++; | |
_setTranslateX( (_containerShiftIndex+2) * _slideSize.x, tempHolder.el.style); | |
self.setContent(tempHolder, _currentItemIndex - diffAbs + i + 1 + 1); | |
} else { | |
tempHolder = _itemHolders.pop(); | |
_itemHolders.unshift( tempHolder ); // move last to first | |
_containerShiftIndex--; | |
_setTranslateX( _containerShiftIndex * _slideSize.x, tempHolder.el.style); | |
self.setContent(tempHolder, _currentItemIndex + diffAbs - i - 1 - 1); | |
} | |
} | |
// reset zoom/pan on previous item | |
if(_currZoomElementStyle && Math.abs(_indexDiff) === 1) { | |
var prevItem = _getItemAt(_prevItemIndex); | |
if(prevItem.initialZoomLevel !== _currZoomLevel) { | |
_calculateItemSize(prevItem , _viewportSize ); | |
_applyZoomPanToItem( prevItem ); | |
} | |
} | |
// reset diff after update | |
_indexDiff = 0; | |
self.updateCurrZoomItem(); | |
_prevItemIndex = _currentItemIndex; | |
_shout('afterChange'); | |
}, | |
updateSize: function(force) { | |
if(!_isFixedPosition) { | |
var windowScrollY = framework.getScrollY(); | |
if(_currentWindowScrollY !== windowScrollY) { | |
template.style.top = windowScrollY + 'px'; | |
_currentWindowScrollY = windowScrollY; | |
} | |
if(!force && _windowVisibleSize.x === window.innerWidth && _windowVisibleSize.y === window.innerHeight) { | |
return; | |
} | |
_windowVisibleSize.x = window.innerWidth; | |
_windowVisibleSize.y = window.innerHeight; | |
//template.style.width = _windowVisibleSize.x + 'px'; | |
template.style.height = _windowVisibleSize.y + 'px'; | |
} | |
_viewportSize.x = self.scrollWrap.clientWidth; | |
_viewportSize.y = self.scrollWrap.clientHeight; | |
_updatePageScrollOffset(); | |
_slideSize.x = _viewportSize.x + Math.round(_viewportSize.x * _options.spacing); | |
_slideSize.y = _viewportSize.y; | |
_moveMainScroll(_slideSize.x * _currPositionIndex); | |
_shout('beforeResize'); // even may be used for example to switch image sources | |
// don't re-calculate size on inital size update | |
if(_containerShiftIndex !== undefined) { | |
var holder, | |
item, | |
hIndex; | |
for(var i = 0; i < NUM_HOLDERS; i++) { | |
holder = _itemHolders[i]; | |
_setTranslateX( (i+_containerShiftIndex) * _slideSize.x, holder.el.style); | |
hIndex = _currentItemIndex+i-1; | |
if(_options.loop && _getNumItems() > 2) { | |
hIndex = _getLoopedId(hIndex); | |
} | |
// update zoom level on items and refresh source (if needsUpdate) | |
item = _getItemAt( hIndex ); | |
// re-render gallery item if `needsUpdate`, | |
// or doesn't have `bounds` (entirely new slide object) | |
if( item && (_itemsNeedUpdate || item.needsUpdate || !item.bounds) ) { | |
self.cleanSlide( item ); | |
self.setContent( holder, hIndex ); | |
// if "center" slide | |
if(i === 1) { | |
self.currItem = item; | |
self.updateCurrZoomItem(true); | |
} | |
item.needsUpdate = false; | |
} else if(holder.index === -1 && hIndex >= 0) { | |
// add content first time | |
self.setContent( holder, hIndex ); | |
} | |
if(item && item.container) { | |
_calculateItemSize(item, _viewportSize); | |
_applyZoomPanToItem( item ); | |
} | |
} | |
_itemsNeedUpdate = false; | |
} | |
_startZoomLevel = _currZoomLevel = self.currItem.initialZoomLevel; | |
_currPanBounds = self.currItem.bounds; | |
if(_currPanBounds) { | |
_panOffset.x = _currPanBounds.center.x; | |
_panOffset.y = _currPanBounds.center.y; | |
_applyCurrentZoomPan(); | |
} | |
_shout('resize'); | |
}, | |
// Zoom current item to | |
zoomTo: function(destZoomLevel, centerPoint, speed, easingFn, updateFn) { | |
/* | |
if(destZoomLevel === 'fit') { | |
destZoomLevel = self.currItem.fitRatio; | |
} else if(destZoomLevel === 'fill') { | |
destZoomLevel = self.currItem.fillRatio; | |
} | |
*/ | |
if(centerPoint) { | |
_startZoomLevel = _currZoomLevel; | |
_midZoomPoint.x = Math.abs(centerPoint.x) - _panOffset.x ; | |
_midZoomPoint.y = Math.abs(centerPoint.y) - _panOffset.y ; | |
_equalizePoints(_startPanOffset, _panOffset); | |
} | |
var destPanBounds = _calculatePanBounds(destZoomLevel, false), | |
destPanOffset = {}; | |
_modifyDestPanOffset('x', destPanBounds, destPanOffset, destZoomLevel); | |
_modifyDestPanOffset('y', destPanBounds, destPanOffset, destZoomLevel); | |
var initialZoomLevel = _currZoomLevel; | |
var initialPanOffset = { | |
x: _panOffset.x, | |
y: _panOffset.y | |
}; | |
_roundPoint(destPanOffset); | |
// _startZoomLevel = destZoomLevel; | |
var onUpdate = function(now) { | |
if(now === 1) { | |
_currZoomLevel = destZoomLevel; | |
_panOffset.x = destPanOffset.x; | |
_panOffset.y = destPanOffset.y; | |
} else { | |
_currZoomLevel = (destZoomLevel - initialZoomLevel) * now + initialZoomLevel; | |
_panOffset.x = (destPanOffset.x - initialPanOffset.x) * now + initialPanOffset.x; | |
_panOffset.y = (destPanOffset.y - initialPanOffset.y) * now + initialPanOffset.y; | |
} | |
if(updateFn) { | |
updateFn(now); | |
} | |
_applyCurrentZoomPan(); | |
}; | |
if(speed) { | |
_animateProp('customZoomTo', 0, 1, speed, easingFn || framework.easing.sine.inOut, onUpdate); | |
} else { | |
onUpdate(1); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment