Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for synthetic animation/transition events. #6005

Merged
merged 1 commit into from Feb 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/docs/ref-05-events.md
Expand Up @@ -139,6 +139,7 @@ DOMEventTarget relatedTarget

These focus events work on all elements in the React DOM, not just form elements.


### Form Events

Event names:
Expand Down Expand Up @@ -246,6 +247,7 @@ number deltaY
number deltaZ
```


### Media Events

Event names:
Expand All @@ -254,10 +256,45 @@ Event names:
onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
```


### Image Events

Event names:

```
onLoad onError
```


### Animation Events

Event names:

```
onAnimationStart onAnimationEnd onAnimationIteration
```

Properties:

```javascript
string animationName
string pseudoElement
float elapsedTime
```


### Transition Events

Event names:

```
onTransitionEnd
```

Properties:

```javascript
string propertyName
string pseudoElement
float elapsedTime
```
50 changes: 7 additions & 43 deletions src/addons/transitions/ReactTransitionEvents.js
Expand Up @@ -13,56 +13,20 @@

var ExecutionEnvironment = require('ExecutionEnvironment');

/**
* EVENT_NAME_MAP is used to determine which event fired when a
* transition/animation ends, based on the style property used to
* define that event.
*/
var EVENT_NAME_MAP = {
transitionend: {
'transition': 'transitionend',
'WebkitTransition': 'webkitTransitionEnd',
'MozTransition': 'mozTransitionEnd',
'OTransition': 'oTransitionEnd',
'msTransition': 'MSTransitionEnd',
},

animationend: {
'animation': 'animationend',
'WebkitAnimation': 'webkitAnimationEnd',
'MozAnimation': 'mozAnimationEnd',
'OAnimation': 'oAnimationEnd',
'msAnimation': 'MSAnimationEnd',
},
};
var getVendorPrefixedEventName = require('getVendorPrefixedEventName');

var endEvents = [];

function detectEvents() {
var testEl = document.createElement('div');
var style = testEl.style;

// On some platforms, in particular some releases of Android 4.x,
// the un-prefixed "animation" and "transition" properties are defined on the
// style object but the events that fire will still be prefixed, so we need
// to check if the un-prefixed events are useable, and if not remove them
// from the map
if (!('AnimationEvent' in window)) {
delete EVENT_NAME_MAP.animationend.animation;
}
var animEnd = getVendorPrefixedEventName('animationend');
var transEnd = getVendorPrefixedEventName('transitionend');

if (!('TransitionEvent' in window)) {
delete EVENT_NAME_MAP.transitionend.transition;
if (animEnd) {
endEvents.push(animEnd);
}

for (var baseEventName in EVENT_NAME_MAP) {
var baseEvents = EVENT_NAME_MAP[baseEventName];
for (var styleName in baseEvents) {
if (styleName in style) {
endEvents.push(baseEvents[styleName]);
break;
}
}
if (transEnd) {
endEvents.push(transEnd);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/renderers/dom/client/ReactBrowserEventEmitter.js
Expand Up @@ -17,6 +17,7 @@ var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
var ViewportMetrics = require('ViewportMetrics');

var assign = require('Object.assign');
var getVendorPrefixedEventName = require('getVendorPrefixedEventName');
var isEventSupported = require('isEventSupported');

/**
Expand Down Expand Up @@ -83,6 +84,9 @@ var reactTopListenersCounter = 0;
// events so we don't include them here
var topEventMapping = {
topAbort: 'abort',
topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
topBlur: 'blur',
topCanPlay: 'canplay',
topCanPlayThrough: 'canplaythrough',
Expand Down Expand Up @@ -139,6 +143,7 @@ var topEventMapping = {
topTouchEnd: 'touchend',
topTouchMove: 'touchmove',
topTouchStart: 'touchstart',
topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
topVolumeChange: 'volumechange',
topWaiting: 'waiting',
topWheel: 'wheel',
Expand Down
38 changes: 38 additions & 0 deletions src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js
Expand Up @@ -15,13 +15,15 @@ var EventConstants = require('EventConstants');
var EventListener = require('EventListener');
var EventPropagators = require('EventPropagators');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var SyntheticAnimationEvent = require('SyntheticAnimationEvent');
var SyntheticClipboardEvent = require('SyntheticClipboardEvent');
var SyntheticEvent = require('SyntheticEvent');
var SyntheticFocusEvent = require('SyntheticFocusEvent');
var SyntheticKeyboardEvent = require('SyntheticKeyboardEvent');
var SyntheticMouseEvent = require('SyntheticMouseEvent');
var SyntheticDragEvent = require('SyntheticDragEvent');
var SyntheticTouchEvent = require('SyntheticTouchEvent');
var SyntheticTransitionEvent = require('SyntheticTransitionEvent');
var SyntheticUIEvent = require('SyntheticUIEvent');
var SyntheticWheelEvent = require('SyntheticWheelEvent');

Expand All @@ -39,6 +41,24 @@ var eventTypes = {
captured: keyOf({onAbortCapture: true}),
},
},
animationEnd: {
phasedRegistrationNames: {
bubbled: keyOf({onAnimationEnd: true}),
captured: keyOf({onAnimationEndCapture: true}),
},
},
animationIteration: {
phasedRegistrationNames: {
bubbled: keyOf({onAnimationIteration: true}),
captured: keyOf({onAnimationIterationCapture: true}),
},
},
animationStart: {
phasedRegistrationNames: {
bubbled: keyOf({onAnimationStart: true}),
captured: keyOf({onAnimationStartCapture: true}),
},
},
blur: {
phasedRegistrationNames: {
bubbled: keyOf({onBlur: true}),
Expand Down Expand Up @@ -365,6 +385,12 @@ var eventTypes = {
captured: keyOf({onTouchStartCapture: true}),
},
},
transitionEnd: {
phasedRegistrationNames: {
bubbled: keyOf({onTransitionEnd: true}),
captured: keyOf({onTransitionEndCapture: true}),
},
},
volumeChange: {
phasedRegistrationNames: {
bubbled: keyOf({onVolumeChange: true}),
Expand All @@ -387,6 +413,9 @@ var eventTypes = {

var topLevelEventsToDispatchConfig = {
topAbort: eventTypes.abort,
topAnimationEnd: eventTypes.animationEnd,
topAnimationIteration: eventTypes.animationIteration,
topAnimationStart: eventTypes.animationStart,
topBlur: eventTypes.blur,
topCanPlay: eventTypes.canPlay,
topCanPlayThrough: eventTypes.canPlayThrough,
Expand Down Expand Up @@ -441,6 +470,7 @@ var topLevelEventsToDispatchConfig = {
topTouchEnd: eventTypes.touchEnd,
topTouchMove: eventTypes.touchMove,
topTouchStart: eventTypes.touchStart,
topTransitionEnd: eventTypes.transitionEnd,
topVolumeChange: eventTypes.volumeChange,
topWaiting: eventTypes.waiting,
topWheel: eventTypes.wheel,
Expand Down Expand Up @@ -549,6 +579,14 @@ var SimpleEventPlugin = {
case topLevelTypes.topTouchStart:
EventConstructor = SyntheticTouchEvent;
break;
case topLevelTypes.topAnimationEnd:
case topLevelTypes.topAnimationIteration:
case topLevelTypes.topAnimationStart:
EventConstructor = SyntheticAnimationEvent;
break;
case topLevelTypes.topTransitionEnd:
EventConstructor = SyntheticTransitionEvent;
break;
case topLevelTypes.topScroll:
EventConstructor = SyntheticUIEvent;
break;
Expand Down
@@ -0,0 +1,47 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule SyntheticAnimationEvent
*/

'use strict';

var SyntheticEvent = require('SyntheticEvent');

/**
* @interface Event
* @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
* @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
*/
var AnimationEventInterface = {
animationName: null,
elapsedTime: null,
pseudoElement: null,
};

/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticEvent}
*/
function SyntheticAnimationEvent(
dispatchConfig,
dispatchMarker,
nativeEvent,
nativeEventTarget
) {
return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(
SyntheticAnimationEvent,
AnimationEventInterface
);

module.exports = SyntheticAnimationEvent;
@@ -0,0 +1,47 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule SyntheticTransitionEvent
*/

'use strict';

var SyntheticEvent = require('SyntheticEvent');

/**
* @interface Event
* @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
* @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
*/
var TransitionEventInterface = {
propertyName: null,
elapsedTime: null,
pseudoElement: null,
};

/**
* @param {object} dispatchConfig Configuration used to dispatch this event.
* @param {string} dispatchMarker Marker identifying the event target.
* @param {object} nativeEvent Native browser event.
* @extends {SyntheticEvent}
*/
function SyntheticTransitionEvent(
dispatchConfig,
dispatchMarker,
nativeEvent,
nativeEventTarget
) {
return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
}

SyntheticEvent.augmentClass(
SyntheticTransitionEvent,
TransitionEventInterface
);

module.exports = SyntheticTransitionEvent;