Skip to content
Browse files

Use feature detection as much as possible for CSS transitions. Also u…

…se feature detection for vendor property map. Defer style application for Opera only.
  • Loading branch information...
1 parent 5ae2f3f commit d3b9a29b4f0798993c67de34284504549a0a0fd6 @charettes charettes committed with savetheclocktower
Showing with 73 additions and 69 deletions.
  1. +45 −49 src/css.js
  2. +28 −20 src/effects/css_transitions.js
View
94 src/css.js
@@ -49,38 +49,29 @@ S2.CSS = {
// An attempt to support vendor-specific properties in a sane way.
// TODO: Do more research into what ought to be added to this list.
- VENDOR_MAP: {
- webkit: {
- // DEFAULT means that the vendorized prefix is obtained simply by
- // prepending the vendor prefix. Some properties (like
- // `border-radius-top-left`) are named differently in different
- // browsers, so their equivalents must be specified outright.
- DEFAULT: $w('border-radius box-shadow transform transition ' +
- 'transition-duration transition-timing-function transition-property ' +
- 'transition-delay ' +
- 'border-top-left-radius border-top-right-radius border-bottom-left-radius ' +
- 'border-bottom-right-radius'
- )
- },
- moz: {
- DEFAULT: $w('border-radius box-shadow transform transition ' +
- 'transition-duration transition-timing-function transition-property ' +
- 'transition-delay '
- ),
- 'border-top-left-radius': '-moz-border-radius-topleft',
- 'border-top-right-radius': '-moz-border-radius-topright',
- 'border-bottom-left-radius': '-moz-border-radius-bottomleft',
- 'border-bottom-right-radius': '-moz-border-radius-bottomright'
+ VENDOR: {
+ // Determined below.
+ PREFIX: null,
+
+ // Lookups for possible supported vendor-specific properties
+ LOOKUP_PREFIXES: ['webkit', 'Moz', 'O'],
+ LOOKUP_PROPERTIES: $w('BorderRadius BoxShadow Transform Transition ' +
+ 'TransitionDuration TransitionTimingFunction TransitionProperty ' +
+ 'TransitionDelay ' +
+ 'BorderTopLeftRadius BorderTopRightRadius BorderBottomLeftRadius ' +
+ 'BorderBottomRightRadius'
+ ),
+ LOOKUP_EDGE_CASES: {
+ 'BorderTopLeftRadius': 'BorderRadiusTopleft',
+ 'BorderTopRightRadius': 'BorderRadiusTopright',
+ 'BorderBottomLeftRadius': 'BorderRadiusBottomleft',
+ 'BorderBottomRightRadius': 'BorderRadiusBottomright'
},
-
- o: {
- // TODO: Figure out what Opera supports.
- }
+
+ // Populated below.
+ PROPERTY_MAP: {}
},
- // Determined below.
- VENDOR_PREFIX: null,
-
/**
* S2.CSS.LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/
* Regular expression for a CSS length, for example 12px, 8.4in, 13% or 0.
@@ -198,22 +189,11 @@ S2.CSS = {
*
**/
vendorizeProperty: function(property) {
- var prefix = S2.CSS.VENDOR_PREFIX;
-
property = property.underscore().dasherize();
-
- if (prefix) {
- var table = S2.CSS.VENDOR_MAP[prefix.toLowerCase()];
- if (table.DEFAULT.include(property)) {
- property = prefix + '-' + property;
- } else if (table[property]) {
- property = table[property];
- }
- }
- // Ensure vendor-prefixed properties begin with a hyphen.
- if (property.match(/^(?:webkit|moz|ms|o|khtml)-/))
- property = '-' + property;
+ if (property in S2.CSS.VENDOR.PROPERTY_MAP) {
+ property = S2.CSS.VENDOR.PROPERTY_MAP[property];
+ }
return property;
},
@@ -413,16 +393,32 @@ if (!(document.defaultView && document.defaultView.getComputedStyle)) {
Element.addMethods(S2.CSS.ElementMethods);
(function() {
- // Use border-radius as a test to see which vendor prefix we should
- // be using.
var div = document.createElement('div');
- var style = div.style, prefixes = $w('webkit Moz');
+ var style = div.style, prefix = null;
+ var edgeCases = S2.CSS.VENDOR.LOOKUP_EDGE_CASES;
+ var uncamelize = function(prop, prefix) {
+ if (prefix) {
+ prop = '-' + prefix.toLowerCase() + '-' + uncamelize(prop);
+ }
+ return prop.underscore().dasherize();
+ }
- var prefix = prefixes.detect( function(p) {
- return typeof style[p + 'BorderRadius'] !== 'undefined';
+ S2.CSS.VENDOR.LOOKUP_PROPERTIES.each(function(prop) {
+ if (!prefix) { // We attempt to detect a prefix
+ prefix = S2.CSS.VENDOR.LOOKUP_PREFIXES.detect( function(p) {
+ return !Object.isUndefined(style[p + prop]);
+ });
+ }
+ if (prefix) { // If we detected a prefix
+ if ((prefix + prop) in style) {
+ S2.CSS.VENDOR.PROPERTY_MAP[uncamelize(prop)] = uncamelize(prop, prefix);
+ } else if (prop in edgeCases && (prefix + edgeCases[prop]) in style) {
+ S2.CSS.VENDOR.PROPERTY_MAP[uncamelize(prop)] = uncamelize(edgeCases[prop], prefix);
+ }
+ }
});
- S2.CSS.VENDOR_PREFIX = prefix;
+ S2.CSS.VENDOR.PREFIX = prefix;
div = null;
})();
View
48 src/effects/css_transitions.js
@@ -9,6 +9,7 @@
// Test if CSS transitions are supported.
var supported = false;
var hardwareAccelerationSupported = false;
+ var transitionEndEventName = null;
function isHWAcceleratedSafari() {
var ua = navigator.userAgent, av = navigator.appVersion;
@@ -17,20 +18,26 @@
}
(function() {
- var div = document.createElement('div');
+ var eventNames = {
+ 'WebKitTransitionEvent': 'webkitTransitionEnd',
+ 'TransitionEvent': 'transitionend'
+ };
+ if (S2.CSS.VENDOR.PREFIX) {
+ var p = S2.CSS.VENDOR.PREFIX;
+ eventNames[p + 'TransitionEvent'] = p + 'TransitionEnd';
+ }
- try {
- document.createEvent("WebKitTransitionEvent");
- supported = true;
-
- hardwareAccelerationSupported = isHWAcceleratedSafari();
- } catch(e) {
- if (typeof div.style.MozTransition !== 'undefined') {
+ for (var e in eventNames) {
+ try {
+ document.createEvent(e);
+ transitionEndEventName = eventNames[e];
supported = true;
- }
+ if (e == 'WebkitTransitionEvent') {
+ hardwareAccelerationSupported = isHWAcceleratedSafari();
+ }
+ return;
+ } catch (e) { }
}
-
- div = null;
})();
if (!supported) return;
@@ -270,11 +277,19 @@
s[v('transition-duration').camelize()] = (effect.duration / 1000).toFixed(3) + 's';
s[v('transition-timing-function').camelize()] = timingFunctionForTransition(effect.options.transition);
- this.element.setStyle(style.toObject());
+ // We make sure the browser interpreted the transitions properties
+ // Opera needs deferring
+ if (Prototype.Browser.Opera) {
+ this._setStyle.bind(this).defer(style.toObject());
+ } else this._setStyle(style.toObject());
this.running = true;
// Replace ourselves with a no-op.
this.render = Prototype.emptyFunction;
+ },
+
+ _setStyle: function(style) {
+ this.element.setStyle(style);
}
});
@@ -364,17 +379,10 @@
}
});
- var EVENT_NAMES = {
- "webkit": "webkitTransitionEnd",
- "moz": "transitionend"
- };
-
- var eventName = EVENT_NAMES[S2.CSS.VENDOR_PREFIX.toLowerCase()];
-
// We listen for the `transitionEnd` event that fires when a CSS transition
// is done, so that we can mark the effect as "finished," fire the `after`
// callback, and do other custodial tasks.
- document.observe(eventName, function(event) {
+ document.observe(transitionEndEventName, function(event) {
var element = event.element();
if (!element) return;

0 comments on commit d3b9a29

Please sign in to comment.
Something went wrong with that request. Please try again.