Skip to content

Instantly share code, notes, and snippets.

@iwyg
Last active December 12, 2015 00:48
Show Gist options
  • Save iwyg/4686181 to your computer and use it in GitHub Desktop.
Save iwyg/4686181 to your computer and use it in GitHub Desktop.
simple Backbone Slideview
(function (define, Modernizr, undefined) {
define(['underscore', 'modules/mod_sequence', 'plugins/bgpos'], function (_, sequence) {
if ($.browser.opera) {
Modernizr.csstransitions = false;
Modernizr.csstransforms = false;
$('html').removeClass('csstransitions csstransforms').addClass('no-csstransitions no-csstransforms');
}
var slice = Array.prototype.slice,
hasTransition = Modernizr.csstransitions,
isOldOpera = ($.browser.opera && $.browser.version < 12) ? true : false,
hasTransforms = Modernizr.csstransforms,
has3dTransforms = Modernizr.csstransforms3d,
translateOpen = has3dTransforms ? 'translate3d(' : 'translate(',
translateClose = has3dTransforms ? ', 0)' : ')',
cubicOpen = 'cubic-bezier(',
cubicClose = ')',
hasOpacity = $.support.opacity,
transform = Modernizr.prefixed('transform'),
transition = Modernizr.prefixed('transition'),
transitionEnd = $.browser.webkit ? 'webkitTransitionEnd' : $.browser.msie ? 'msTransitionEnd' : $.browser.opera ? 'oTransitionEnd' : $.browser.mozilla ? 'transitionend' : 'transitionend',
cssTransform = (function () {
var l = 'transform'.length,
trans;
if (transform.length > l) {
return '-' + transform.toLowerCase().split('transform')[0] + '-transform';
}
else {
return 'transform';
}
}()),
clearTransition = (function () {
var props = 'Property Duration TimingFunction Delay'.split(' '),
l = props.length, i = 0, exports = {};
for (; i < l; ++i) {
exports[transition + props[i]] = '';
}
return exports;
}());
$.wait = function (time) {
var dfr = $.Deferred();
setTimeout(dfr.resolve, time);
return dfr.promise();
};
var killTimers = [];
var transM = {
transform: function (coords) {
return translateOpen + coords.join(', ') + translateClose;
},
scale: function (coords) {
return 'scale(' + coords.join(', ') + ')';
}
}
function removeFilter() {
this.style.removeAttribute('filter');
}
function getCssProperty(prop) {
prop = prop.toLowerCase();
var p = Modernizr.prefixed(prop), l = p.length;
if (prop.length < l) {
return '-' + p.toLowerCase().split(prop)[0] + '-' + prop;
} else {
return prop;
}
}
function setTiming(coords) {
return cubicOpen + coords.join(', ') + cubicClose;
}
function setTransform(coords) {
return translateOpen + coords.join(', ') + translateClose;
}
// param property = {
// property: position
// }
// param timing = {
// duration: seconds,
// delay: seconds,
// easing: timing
// }
function setTransition(property, timing) {
var prop = Object.keys(property),
//val = property[prop],
idx = prop.indexOf('type'),
p = {};
idx >= 0 && prop.splice(idx, 1);
//p[transition] = [prop.join(', '), timing.duration + 's', setTiming(timing.easing), timing.delay && timing.delay + 's' || ''].join(' ');
p[transition + 'Property'] = prop.join(', ');
p[transition + 'Duration'] = timing.duration + 's';
p[transition + 'TimingFunction'] = setTiming(timing.easing);
p[transition + 'Delay'] = timing.delay && timing.delay + 's' || 'initial';
this.css(p);
}
function handleCallback(elem) {
var elem = evt.data.element, ki;
if (evt.target !== elem[0]) {
return;
}
elem.off(transitionEnd, handleCallback);
elem.css(clearTransition);
if ($.isFunction(this)) {
this.call(elem, {prop: cssTransform});
}
}
var cbStore = {};
function EventObj(elem, id, cb) {
this.elem = elem;
this.id = id;
this.callback = cb;
}
function _fxq(id, callback) {
cbStore[id] = callback;
}
function doAnimation(prop, val, callback, id) {
var el = this;
setTransition.apply(this, arguments);
el[0].addEventListener(transitionEnd, new EventObj(el, id, callback));
$.wait(35).then(function () {
var trans = prop[cssTransform];
if (prop.type && trans) {
prop[transform] = transM[prop.type](trans);
// getting DOM property forces a reflow;
// forcing a reflow ensures all browsers will apply transition (I'm looking at you, FF)
el.height();
//p[transform] = $.isArray(value) ? transM[prop.type](value) : value;
}
delete prop[cssTransform];
delete prop.type;
el.css(prop);
});
}
EventObj.prototype = {
handleEvent : function (evt) {
if (evt.target !== this.elem[0]) {
return;
}
this.elem[0].removeEventListener(transitionEnd, this);
this.elem.css(clearTransition);
if ($.isFunction(this.callback)) {
this.callback.call(this.elem, {prop: cssTransform});
delete cbStore[this.id];
}
}
};
function animate(elem, prop, val, callback, q) {
q = typeof q === 'boolean' ? q : true;
var animID = 'anim' + _.uniqueId();
//_fxq.apply(elem, [animID, prop, val, callback, q]);
_fxq.call(elem, animID, callback);
doAnimation.apply(elem, [prop, val, callback, animID]);
return this;
}
/*
if (typeof navigator.platform === 'string' && navigator.platform.toLowerCase().match(/mac/)) {
if (/chrome/.test(navigator.userAgent.toLowerCase())) {
hasTransforms = false;
hasTransition = false;
}
}
*/
if (hasTransforms && hasTransition && !isOldOpera) {
return {
cssAnimation: true,
getCssProperty: getCssProperty,
slideVertical: function (elem, props, timing, callback) {
var p = {};
props[cssTransform] = [0, props.to];
props.type = 'transform';
delete props.to;
delete props.direction;
if (timing.duration === 0) {
elem.css(props);
return this;
}
animate(elem, props, timing, callback);
return this;
},
slideHorizontal: function (elem, props, timing, callback) {
var p = {};
props[cssTransform] = [props.to, 0];
props.type = 'transform';
delete props.to;
delete props.direction;
if (timing.duration === 0) {
elem.css(props);
return this;
}
animate(elem, props, timing, callback);
return this;
},
fadeOut: function (elem, timing, callback, q) {
var p = {opacity: 0};
animate(elem, p, timing, function () {
elem.css({display: 'none'});
$.isFunction(callback) && callback();
}, q);
return this;
},
fadeIn: function (elem, timing, callback, q) {
var p = {opacity: 1};
elem.css({display: 'block', opacity: 0});
animate(elem, p, timing, function () {
$.isFunction(callback) && callback();
}, q);
return this;
},
fadeTo: function (elem, props, timing, callback, q) {
var p = {opacity: props.to};
animate(elem, p, timing, callback, q);
return this;
},
fadeToSeq: function (elem, props, timing, callback) {
var that = this,
elems = elem, l = elems.length;
$.when.apply($, sequence(timing.steps || elems.length, elems.length, timing.seqDuration || 4050, function (i, delay, dfr) {
timing.delay = Math.round(delay) / 1000;
that.fadeTo($(elems[i]), props, timing, dfr.resolve, false);
})).done($.isFunction(callback) && callback);
return this;
},
animate: animate
};
} else {
function handle(callback, prop) {
var elem = $(this);
/*if (!hasOpacity) {
if (prop && (prop.prop && (prop.prop === 'opacity'))) {
//removeFilter.call(this);
}
}*/
if ($.isFunction(callback)) {
callback.call(elem, prop);
}
}
return {
cssAnimation: false,
getCssProperty: function () {
return false;
},
slideVertical : function (elem, props, timing, callback) {
var dir = props.direction;
if (dir) {
props[dir] = props.to;
} else {
props.top = props.to;
}
delete props.to;
delete props.direction;
elem.animate(props, timing.duration * 1000, $.bez(timing.easing), function () {
handle.call(this, callback, {prop: dir || 'top'});
});
return this;
},
slideHorizontal: function (elem, props, timing, callback) {
var dir = props.direction;
if (dir) {
props[dir] = props.to;
} else {
props.left = props.to;
}
delete props.to;
delete props.direction;
elem.animate(props, timing.duration * 1000, $.bez(timing.easing), function () {
handle.call(this, callback, {prop: dir || 'left'});
});
return this;
},
fadeIn: function (elem, timing, callback) {
var p = {opacity: 1};
elem.css({opacity: 0, display: 'block'});
elem.animate(p, timing.duration * 1000, $.bez(timing.easing), function () {
handle.call(this, callback, {prop: 'opacity'});
});
return this;
},
fadeOut: function (elem, timing, callback) {
var p = {opacity: 0};
elem.animate(p, timing.duration * 1000, $.bez(timing.easing), function () {
handle.call(this, callback);
elem.css({display: 'none'});
});
return this;
},
fadeTo: function (elem, props, timing, callback) {
var p = {opacity: props.to}, pp = props.to === 1 ? {prop: 'opacity'} : undefined;
elem.delay(timing.delay).animate(p, timing.duration * 1000, $.bez(timing.easing), function () {
handle.call(this, callback, pp);
});
return this;
},
fadeToSeq: function (elem, props, timing, callback) {
var that = this,
elems = elem, l = elems.length;
$.when.apply($, sequence(timing.steps || elems.length, elems.length, timing.seqDuration || 450, function (i, delay, dfr) {
timing.delay = delay;
that.fadeTo($(elems[i]), props, timing, function () {
dfr.resolve();
handle.call(this);
});
})).done(callback && callback);
return this;
},
animate: function (elem, props, timing, callback) {
var t = props.backgroundPosition || props['background-position'], tt;
if (t) {
tt = t.split(' ');
props.backgroundPositionX = tt[0] || 0;
props.backgroundPositionY = tt[1] || 0;
delete props.backgroundPosition;
delete props['background-position'];
}
elem.animate(props, timing.duration * 1000, $.bez(timing.easing), function () {
handle.call(this, callback);
});
return this;
}
};
}
});
}(this.define, this.Modernizr));
(function (define) {
define(['jquery', 'underscore', 'modules/mod_rotator', 'templates/templates'], function ($, _, Rotator, templates) {
var cachedItems = {}, Slider;
Slider = Rotator.extend({
initialize: function () {
this._constructOrigin.apply(this, arguments);
this.render = _.once(this.render);
},
render: function () {
var cached = this.getItems(), view = this, list = this.addItem(this.model), items;
this.$el.html(list);
items = _.map(this.$el.children(), function (item) {
return $(item);
});
cached.push.apply(cached, items);
this.trigger('ready.' + this.cid, this);
},
renderItem: function (model) {
var template = templates.gallery_image;
return template(model.toJSON());
},
addItem: function (model) {
var html = templates.gallery_image(model.toJSON());
return html;
},
getCurrentIndex: function () {
return this.model.get('displays');
},
setCurrentIndex: function (index) {
this.model.set('displays', index);
},
getItemAt: function (index) {
return this.getItems()[index];
},
});
return Slider;
});
}(this.define));
(function (define) {
define(function (require) {
var $ = require('jquery'),
_ = require('underscore'),
Backbone = require('backbone'),
fx = require('modules/mod_animation'),
animators = {},
slidelock = {},
cachedItems = {},
Rotator;
function slide(type, position, time, easing, callback) {
fx[type](this, {to: position}, {duration: time, easing: easing, delay: 0}, callback);
}
function ready() {
this.getItems()[this.getCurrentIndex()]
.removeClass('prev next')
.addClass('current')
.nextAll()
.addClass('next')
.end()
.prevAll()
.addClass('prev');
this.trigger('ready');
}
function setClasses(item) {
item.removeClass('next prev').addClass('current');
item.prevAll()
.removeClass('current next')
.addClass('prev').end()
.nextAll().removeClass('current prev')
.addClass('next');
}
Rotator = Backbone.View.extend({
_constructOrigin: function () {
Rotator.prototype.initialize.apply(this, arguments);
},
initialize: function (options) {
this.options = _.extend(_.clone(Rotator.defaults), options || {});
this.el.id = this.cid;
this.el.className = this.options.type === 'slideHorizontal' ? 'slide-h' : this.options.type === 'slideVertical' ? 'slide-v' : '';
this.setCurrentIndex(this.options.start || 0);
this.on('ready.' + this.cid, _.bind(ready, this));
cachedItems[this.cid] = [];
},
next: function () {
var current = this.getCurrentIndex(),
items = this.getItems(),
item = current + 1 === items.length ? (this.options.carousel ? 0 : false) : current + 1;
this.to(item);
return this;
},
prev: function () {
var current = this.getCurrentIndex(),
items = this.getItems(),
item = current === 0 ? (this.options.carousel ? items.length - 1 : false) : current - 1;
this.to(item);
return this;
},
to: function (i) {
var this_id = this.cid, current = this.getCurrentIndex();
if ((i === false || i < 0) || !!slidelock[this_id] || i === current) {
return false;
}
slidelock[this_id] = true;
var item = this.getItems()[i],
currentItem = this.getItemAt(current).css({display: 'block'}),
nextItem = this.getItemAt(i).css({display: 'block'}),
that = this, to,
easing = this.options.easing,
oldZIndex = parseInt(currentItem.css('z-index'), 10),
zIndex = isNaN(oldZIndex) ? 2 : oldZIndex;
this.current = this.requested = i;
this.requested = i;
this.trigger('beforeSlide', i, nextItem, currentItem);
to = current < i ? '-100%' : '100%';
this.setCurrentIndex(i);
nextItem.css({zIndex: zIndex});
currentItem.css({zIndex: zIndex - 1});
slide.call(currentItem, this.options.type, to, this.options.transitionTime || 1, this.calcEasing(easing), function (property) {
currentItem[0].style.display = 'none';
property && currentItem.css(property.prop, '');
});
slide.call(nextItem, this.options.type, 0, this.options.transitionTime || 1, easing, function (property) {
setClasses(this);
//delete that.slidelock;
slidelock[this_id] = false;
that.trigger('afterSlide', current, nextItem, 'afterSlide', that.id, that.name);
});
return this;
},
getItems: function (easing) {
return cachedItems[this.cid];
},
getItemAt: function (index) {
return this.getItems()[index];
},
// @return the current index of item to be inserted
getPosition: function () {
throw ('Abstract method `getPosition` has not been declared');
},
getCurrentIndex: function () {
throw ('Abstract method `getCurrentIndex` has not been declared');
},
setCurrentIndex: function () {
throw ('Abstract method `setCurrentIndex` has not been declared');
},
calcEasing: function (easing) {
return [Math.min(easing[0] + 0.04, 1), Math.min(easing[1] - 0.18, 1), Math.min(easing[2] + 0.21, 1), Math.min(easing[3], 1)];
}
});
Rotator.defaults = {
transitionTime: 0.8,
carousel: true,
start: 0,
className: 'revolve',
type: 'slideHorizontal',
easing: [0.1, 0.6, 0, 1]
};
return Rotator;
});
}(this.define));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment