Skip to content

Commit

Permalink
Refactor to use jQuery fx queue and native transition events
Browse files Browse the repository at this point in the history
  • Loading branch information
bjester committed May 18, 2014
1 parent cd77286 commit b61fece
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 200 deletions.
252 changes: 130 additions & 122 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,157 +3,146 @@
*/
(function($)
{
var global = this,
queue = 'transition',
queueHandler = '_transitionQueueHandler',
QueueHandler = global['Transition']['queueHandler'];

var defaultOptions =
{
duration: 400, // ms
easing: 'ease', // CSS3 transition easing
start: $.noop, // start callback that handlers add to
complete: $.noop // complete callback
};

var handlerCallbacks = {
start: $.noop,
complete: $.noop
};

var global = this;

/**
*
* @type Object
*/
var Transition = global.Transition =
var Transition =
{
prefixes: ['webkit', 'moz', 'o', 'ms'],

START: 'transitionStart',
END: 'transitionEnd',

prefixes: ['', 'webkit', 'moz', 'o', 'ms'],
supportPrefix: '',
supportEvent: '',
support: {},
handlers: {},
generators: {},
defaultOptions:
{
duration: 400, // ms
easing: 'ease' // CSS3 transition easing
},

/**
* Addes prefixes
*
* @param {Object} css A css attr object to convert
* @returns {Object}
*/
build: function(css)
*
* @returns {undefined}
*/
init: function()
{
var out = $.extend({}, css);

for (var attr in css)
{
for (var i = 0; i < this.prefixes.length; i++)
{
out['-' + this.prefixes[i] + '-' + attr] = css[attr];
}
}

return out;
this.detectSupport();
},

/**
*
* @param {jQuery} el
* @param {Object} properties
* @param {Object} options
* @param {Function} callback
*/
core: function(el, properties, options)
core: function(el, properties, options, callback)
{
var me = this, opts = $.extend({}, defaultOptions, options);

// Cache complete callback in scope
var complete = opts.complete, start = opts.start;

opts.start = $.Callbacks();
opts.complete = $.Callbacks();
var me = this;

opts.start.add(start);
opts.complete.add(complete);
if ($.type(options) === 'function')
{
callback = options;
options = defaultOptions;
}
else if ($.type('options') === 'object')
{
callback = callback || $.noop;
options = $.extend({}, defaultOptions, options);
}
else
{
callback = $.noop;
options = defaultOptions;
}

// Add start callback to start timer
opts.start.add(function(cb)
var transitionNames = new Array();
for (var property in properties)
{
global.setTimeout(function()
// When a property isn't supported
if (!(property in me.handlers))
{
opts.complete.fire();
(cb || $.noop)();
}, opts.duration);
});
continue;
}

this.handlers[property].call(el, properties[property], options);
transitionNames.push(property);
}

// Add complete callback
opts.complete.add(function()
{
// Set timeout to add a little buffer just incase JS is too fast for CSS
global.setTimeout(function()
// Bind to end event and queue
el.one(this.supportEvent, function()
{
el.css(me.build({
// Clear transition
el.css(me.build(
{
'transition-property': '',
'transition-timing-function': '',
'transition-duration': ''
}));
}, 150);
});

// Add to queue
el.queue(queue, this.getQueueFunc(el, properties, opts));

// Get the handler if set
var qHandler = el.data(queueHandler);

if (!(qHandler instanceof QueueHandler))
{
qHandler = new QueueHandler(el);
el.data(queueHandler, qHandler);
}

qHandler.next();
callback.call(el);
el.trigger(me.END).dequeue();
})
.queue(function()
{
// Add transition
el.css(me.build(
{
'transition-property': $.unique(transitionNames).join(', '),
'transition-timing-function': options.easing || 'ease',
'transition-duration': options.duration + 'ms'
}));

el.trigger(me.START);
});
},

/**
*
* @param {DomElement} el
* @param {Object} properties
* @param {Object} opts
* @returns {Function}
*/
getQueueFunc: function(el, properties, opts)
* Adds the support prefix to a set of css properties
*
* @param {Object} css A css attr object to convert
* @returns {Object}
*/
build: function(css)
{
var me = this;
var out = $.extend({}, css);

return function()
for (var attr in css)
{
var transitionNames = new Array();
for (var handler in properties)
{
if (!(handler in me.handlers))
{
continue;
}

var handlerObj = me.handlers[handler];
transitionNames.push(handlerObj.property);
var cbs = $.extend({}, handlerCallbacks,
handlerObj.call(el, properties[handler], opts));

opts.start.add(cbs.start);
opts.complete.add(cbs.complete);
}

el.css(me.build({
'transition-property': $.unique(transitionNames).join(', '),
'transition-timing-function': opts.easing || 'ease',
'transition-duration': opts.duration + 'ms'
}));

return opts;
};
out['-' + this.supportPrefix + '-' + attr] = css[attr];
}

return out;
},

/**
*
* @returns {Object}
*/
* @returns {String}
*/
detectSupport: function()
{
// Test prefix
var el = global.document.createElement('div'),
test = 'transition';

for (var i = 0; i < this.prefixes.length; i++)
{
var style = this.getStyleString(test, this.prefixes[i]);

if (el.style[style] !== undefined)
{
this.supportPrefix = this.prefixes[i];
break;
}
}

// Test handler support
var support = {};

for (var name in this.handlers)
Expand All @@ -164,9 +153,33 @@
}
}

return this.support = support;
this.supportEvent = (this.supportPrefix === '')
? 'transitionend'
: this.supportPrefix + 'TransitionEnd';
return this.supportPrefix;
},

/**
*
* @param {String} property
* @param {String} prefix
* @returns {String}
*/
getStyleString: function(property, prefix)
{
prefix = prefix || this.supportPrefix;

if (prefix.trim() === '')
{
return property;
}

return [prefix, property].map(function(s)
{
return s.charAt(0).toUpperCase() + s.slice(1);
}).join('');
},

/**
* To add a transition property generator
*
Expand All @@ -188,23 +201,18 @@
}
};

// Setup namespaces
Transition.generators = {};
Transition.handlers = {};
Transition.support = {};

// Detect support on load
$(function()
{
Transition.detectSupport();
Transition.init();
});

// Add to jQuery
$.fn.extend(
{
transition: function(properties, options)
transition: function(properties, options, callback)
{
Transition.core($(this), properties, options);
Transition.core($(this), properties, options, callback);
return $(this);
}
});
Expand Down
4 changes: 2 additions & 2 deletions lib/generator/duration.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
var global = this;

var Transition = {
var Duration = {
/**
* Adds browser specific prefixes to transition
*
Expand All @@ -32,6 +32,6 @@
}
};

global.Transition.addGenerator({transition: Transition});
global.Transition.addGenerator({duration: Duration});

})(jQuery);
26 changes: 13 additions & 13 deletions lib/handler/opacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,36 @@
*/
(function($)
{
var global = this,
Transition = global.Transition;

var Opacity = function(props)
{
var el = $(this);

return {
start: function()
$(this)
.one(Transition.START, function()
{
var opacity = el.css('opacity');
var opacity = $(this).css('opacity');

if (opacity === 0 || !el.is(':visible'))
if (opacity === 0 || !$(this).is(':visible'))
{
el.show();
$(this).show();
};

el.css('opacity', props);
},
complete: function()
$(this).css('opacity', props);
})
.one(Transition.END, function()
{
if (props === 0)
{
el.hide();
}
}
};
});
};

// IMPORTANT, allows core to know which attrs to set for transition
Opacity.property = 'opacity';

this.Transition.extend(
Transition.addHandler(
{
opacity: Opacity
});
Expand Down
Loading

0 comments on commit b61fece

Please sign in to comment.