merge InteractionManager stuff back into PanResponder

Reviewed By: bestander

Differential Revision: D3224519

fb-gh-sync-id: d041214b68fedfdd6b01aa2b710b02381c29dcfa
fbshipit-source-id: d041214b68fedfdd6b01aa2b710b02381c29dcfa
This commit is contained in:
Spencer Ahrens 2016-04-26 13:18:13 -07:00 committed by Facebook Github Bot 1
parent dad39eb502
commit b5d9bf0fb2
3 changed files with 114 additions and 145 deletions

View File

@ -26,6 +26,7 @@ import type {Task} from 'TaskQueue';
const _emitter = new EventEmitter();
const DEBUG_DELAY = 0;
const DEBUG = false;
/**
* InteractionManager allows long-running work to be scheduled after any
@ -100,6 +101,7 @@ var InteractionManager = {
* Notify manager that an interaction has started.
*/
createInteractionHandle(): Handle {
DEBUG && console.log('create interaction handle');
_scheduleUpdate();
var handle = ++_inc;
_addInteractionSet.add(handle);
@ -110,6 +112,7 @@ var InteractionManager = {
* Notify manager that an interaction has completed.
*/
clearInteractionHandle(handle: Handle) {
DEBUG && console.log('clear interaction handle');
invariant(
!!handle,
'Must provide a handle to clear.'
@ -119,56 +122,6 @@ var InteractionManager = {
_deleteInteractionSet.add(handle);
},
/**
* Can be used to turn a regular repsonder into one that holds interaction handles
* when the responder is granted. This makes it easier to acheive 60fps responder
* interactions in JS, e.g. for drag-and-drop gestures with PanResponder.
*/
createResponderFactory(baseResponderFactory: {create: (config: Object) => Object}) {
function clearInteractionHandle(
interactionState: {handle: ?Handle},
callback: Function,
event: Object,
gestureState: Object
) {
if (interactionState.handle) {
InteractionManager.clearInteractionHandle(interactionState.handle);
interactionState.handle = null;
}
if (callback) {
callback(event, gestureState);
}
}
return {
create: function(config: Object) {
const interactionState = {
handle: (null: ?Handle),
};
const newConfig = {
...config,
onPanResponderGrant: function (e, gestureState) {
if (!interactionState.handle) {
interactionState.handle = InteractionManager.createInteractionHandle();
}
if (config.onPanResponderGrant) {
config.onPanResponderGrant(e, gestureState);
}
},
onPanResponderReject: function (e, gestureState) {
clearInteractionHandle(interactionState, config.onPanResponderReject, e, gestureState);
},
onPanResponderRelease: function (e, gestureState) {
clearInteractionHandle(interactionState, config.onPanResponderRelease, e, gestureState);
},
onPanResponderTerminate: function (e, gestureState) {
clearInteractionHandle(interactionState, config.onPanResponderTerminate, e, gestureState);
},
};
return baseResponderFactory.create(newConfig);
}
};
},
addListener: _emitter.addListener.bind(_emitter),
/**

View File

@ -11,15 +11,15 @@
'use strict';
var InteractionManager = require('./InteractionManager');
var TouchHistoryMath = require('TouchHistoryMath');
const InteractionManager = require('./InteractionManager');
const TouchHistoryMath = require('TouchHistoryMath');
var currentCentroidXOfTouchesChangedAfter = TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
var currentCentroidYOfTouchesChangedAfter = TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
var previousCentroidXOfTouchesChangedAfter = TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
var previousCentroidYOfTouchesChangedAfter = TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
var currentCentroidX = TouchHistoryMath.currentCentroidX;
var currentCentroidY = TouchHistoryMath.currentCentroidY;
const currentCentroidXOfTouchesChangedAfter = TouchHistoryMath.currentCentroidXOfTouchesChangedAfter;
const currentCentroidYOfTouchesChangedAfter = TouchHistoryMath.currentCentroidYOfTouchesChangedAfter;
const previousCentroidXOfTouchesChangedAfter = TouchHistoryMath.previousCentroidXOfTouchesChangedAfter;
const previousCentroidYOfTouchesChangedAfter = TouchHistoryMath.previousCentroidYOfTouchesChangedAfter;
const currentCentroidX = TouchHistoryMath.currentCentroidX;
const currentCentroidY = TouchHistoryMath.currentCentroidY;
/**
* `PanResponder` reconciles several touches into a single gesture. It makes
@ -119,7 +119,7 @@ var currentCentroidY = TouchHistoryMath.currentCentroidY;
* [PanResponder example in UIExplorer](https://github.com/facebook/react-native/blob/master/Examples/UIExplorer/PanResponderExample.js)
*/
var PanResponder = {
const PanResponder = {
/**
*
@ -226,16 +226,16 @@ var PanResponder = {
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
gestureState.moveX = currentCentroidXOfTouchesChangedAfter(touchHistory, gestureState._accountsForMovesUpTo);
gestureState.moveY = currentCentroidYOfTouchesChangedAfter(touchHistory, gestureState._accountsForMovesUpTo);
var movedAfter = gestureState._accountsForMovesUpTo;
var prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
var x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
var prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
var y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
var nextDX = gestureState.dx + (x - prevX);
var nextDY = gestureState.dy + (y - prevY);
const movedAfter = gestureState._accountsForMovesUpTo;
const prevX = previousCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
const x = currentCentroidXOfTouchesChangedAfter(touchHistory, movedAfter);
const prevY = previousCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
const y = currentCentroidYOfTouchesChangedAfter(touchHistory, movedAfter);
const nextDX = gestureState.dx + (x - prevX);
const nextDY = gestureState.dy + (y - prevY);
// TODO: This must be filtered intelligently.
var dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
const dt = touchHistory.mostRecentTimeStamp - gestureState._accountsForMovesUpTo;
gestureState.vx = (nextDX - gestureState.dx) / dt;
gestureState.vy = (nextDY - gestureState.dy) / dt;
@ -277,12 +277,15 @@ var PanResponder = {
* are the responder.
*/
create: function (config) {
var gestureState = {
const interactionState = {
handle: (null: ?number),
};
const gestureState = {
// Useful for debugging
stateID: Math.random(),
};
PanResponder._initializeGestureState(gestureState);
var panHandlers = {
const panHandlers = {
onStartShouldSetResponder: function (e) {
return config.onStartShouldSetPanResponder === undefined ?
false :
@ -306,7 +309,7 @@ var PanResponder = {
},
onMoveShouldSetResponderCapture: function (e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
// Responder system incorrectly dispatches should* to current responder
// Filter out any touch moves past the first one - we would have
// already processed multi-touch geometry during the first event.
@ -320,6 +323,9 @@ var PanResponder = {
},
onResponderGrant: function (e) {
if (!interactionState.handle) {
interactionState.handle = InteractionManager.createInteractionHandle();
}
gestureState.x0 = currentCentroidX(e.touchHistory);
gestureState.y0 = currentCentroidY(e.touchHistory);
gestureState.dx = 0;
@ -334,20 +340,16 @@ var PanResponder = {
},
onResponderReject: function (e) {
if (config.onPanResponderReject) {
config.onPanResponderReject(e, gestureState);
}
clearInteractionHandle(interactionState, config.onPanResponderReject, e, gestureState);
},
onResponderRelease: function (e) {
if (config.onPanResponderRelease) {
config.onPanResponderRelease(e, gestureState);
}
clearInteractionHandle(interactionState, config.onPanResponderRelease, e, gestureState);
PanResponder._initializeGestureState(gestureState);
},
onResponderStart: function (e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
if (config.onPanResponderStart) {
config.onPanResponderStart(e, gestureState);
@ -355,7 +357,7 @@ var PanResponder = {
},
onResponderMove: function (e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
// Guard against the dispatch of two touch moves when there are two
// simultaneously changed touches.
if (gestureState._accountsForMovesUpTo === touchHistory.mostRecentTimeStamp) {
@ -370,17 +372,13 @@ var PanResponder = {
},
onResponderEnd: function (e) {
var touchHistory = e.touchHistory;
const touchHistory = e.touchHistory;
gestureState.numberActiveTouches = touchHistory.numberActiveTouches;
if (config.onPanResponderEnd) {
config.onPanResponderEnd(e, gestureState);
}
clearInteractionHandle(interactionState, config.onPanResponderEnd, e, gestureState);
},
onResponderTerminate: function (e) {
if (config.onPanResponderTerminate) {
config.onPanResponderTerminate(e, gestureState);
}
clearInteractionHandle(interactionState, config.onPanResponderTerminate, e, gestureState);
PanResponder._initializeGestureState(gestureState);
},
@ -394,4 +392,19 @@ var PanResponder = {
}
};
module.exports = InteractionManager.createResponderFactory(PanResponder);
function clearInteractionHandle(
interactionState: {handle: ?number},
callback: Function,
event: Object,
gestureState: Object
) {
if (interactionState.handle) {
InteractionManager.clearInteractionHandle(interactionState.handle);
interactionState.handle = null;
}
if (callback) {
callback(event, gestureState);
}
}
module.exports = PanResponder;

View File

@ -7,27 +7,34 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
var docgen = require('react-docgen');
var docgenHelpers = require('./docgenHelpers');
var fs = require('fs');
var path = require('path');
var slugify = require('../core/slugify');
var jsDocs = require('../jsdocs/jsdocs.js');
'use strict';
var ANDROID_SUFFIX = 'android';
var CROSS_SUFFIX = 'cross';
var IOS_SUFFIX = 'ios';
const docgen = require('react-docgen');
const docgenHelpers = require('./docgenHelpers');
const fs = require('fs');
const jsDocs = require('../jsdocs/jsdocs.js');
const path = require('path');
const slugify = require('../core/slugify');
const ANDROID_SUFFIX = 'android';
const CROSS_SUFFIX = 'cross';
const IOS_SUFFIX = 'ios';
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function getNameFromPath(filepath) {
var ext = null;
while (ext = path.extname(filepath)) {
function removeExtName(filepath) {
let ext = path.extname(filepath);
while (ext) {
filepath = path.basename(filepath, ext);
ext = path.extname(filepath);
}
return filepath;
}
function getNameFromPath(filepath) {
filepath = removeExtName(filepath);
if (filepath === 'LayoutPropTypes') {
return 'Flexbox';
} else if (filepath === 'TransformPropTypes') {
@ -41,11 +48,7 @@ function getNameFromPath(filepath) {
}
function getPlatformFromPath(filepath) {
var ext = null;
while (ext = path.extname(filepath)) {
filepath = path.basename(filepath, ext);
}
filepath = removeExtName(filepath);
if (endsWith(filepath, 'Android')) {
return ANDROID_SUFFIX;
} else if (endsWith(filepath, 'IOS')) {
@ -55,21 +58,21 @@ function getPlatformFromPath(filepath) {
}
function getExample(componentName, componentPlatform) {
var path = '../Examples/UIExplorer/' + componentName + 'Example.js';
if (!fs.existsSync(path)) {
path = '../Examples/UIExplorer/' + componentName + 'Example.'+ componentPlatform +'.js';
if (!fs.existsSync(path)) {
let exPath = '../Examples/UIExplorer/' + componentName + 'Example.js';
if (!fs.existsSync(exPath)) {
exPath = '../Examples/UIExplorer/' + componentName + 'Example.' + componentPlatform + '.js';
if (!fs.existsSync(exPath)) {
return;
}
}
return {
path: path.replace(/^\.\.\//, ''),
content: fs.readFileSync(path).toString(),
path: exPath.replace(/^\.\.\//, ''),
content: fs.readFileSync(exPath).toString(),
};
}
// Add methods that should not appear in the components documentation.
var methodsBlacklist = [
const methodsBlacklist = [
// Native methods mixin.
'getInnerViewNode',
'setNativeProps',
@ -95,10 +98,10 @@ function filterMethods(method) {
// Determines whether a component should have a link to a runnable example
function isRunnable(componentName, componentPlatform) {
var path = '../Examples/UIExplorer/' + componentName + 'Example.js';
if (!fs.existsSync(path)) {
path = '../Examples/UIExplorer/' + componentName + 'Example.' + componentPlatform + '.js';
if (!fs.existsSync(path)) {
let exPath = '../Examples/UIExplorer/' + componentName + 'Example.js';
if (!fs.existsSync(exPath)) {
exPath = '../Examples/UIExplorer/' + componentName + 'Example.' + componentPlatform + '.js';
if (!fs.existsSync(exPath)) {
return false;
}
}
@ -107,7 +110,7 @@ function isRunnable(componentName, componentPlatform) {
// Hide a component from the sidebar by making it return false from
// this function
var HIDDEN_COMPONENTS = [
const HIDDEN_COMPONENTS = [
'Transforms',
'ListViewDataSource',
];
@ -116,24 +119,24 @@ function shouldDisplayInSidebar(componentName) {
return HIDDEN_COMPONENTS.indexOf(componentName) === -1;
}
function getNextComponent(i) {
if (all[i + 1]) {
var nextComponentName = getNameFromPath(all[i + 1]);
function getNextComponent(idx) {
if (all[idx + 1]) {
const nextComponentName = getNameFromPath(all[idx + 1]);
if (shouldDisplayInSidebar(nextComponentName)) {
return slugify(nextComponentName);
} else {
return getNextComponent(i + 1);
return getNextComponent(idx + 1);
}
} else {
return 'network';
}
}
function componentsToMarkdown(type, json, filepath, i, styles) {
var componentName = getNameFromPath(filepath);
var componentPlatform = getPlatformFromPath(filepath);
var docFilePath = '../docs/' + componentName + '.md';
function componentsToMarkdown(type, json, filepath, idx, styles) {
const componentName = getNameFromPath(filepath);
const componentPlatform = getPlatformFromPath(filepath);
const docFilePath = '../docs/' + componentName + '.md';
if (fs.existsSync(docFilePath)) {
json.fullDescription = fs.readFileSync(docFilePath).toString();
@ -152,10 +155,10 @@ function componentsToMarkdown(type, json, filepath, i, styles) {
}
// Put Flexbox into the Polyfills category
var category = (type === 'style' ? 'Polyfills' : type + 's');
var next = getNextComponent(i);
const category = (type === 'style' ? 'Polyfills' : type + 's');
const next = getNextComponent(idx);
var res = [
const res = [
'---',
'id: ' + slugify(componentName),
'title: ' + componentName,
@ -173,10 +176,10 @@ function componentsToMarkdown(type, json, filepath, i, styles) {
return res;
}
var n;
let componentCount;
function renderComponent(filepath) {
var json = docgen.parse(
const json = docgen.parse(
fs.readFileSync(filepath),
docgenHelpers.findExportedOrFirst,
docgen.defaultHandlers.concat([
@ -185,40 +188,40 @@ function renderComponent(filepath) {
])
);
return componentsToMarkdown('component', json, filepath, n++, styleDocs);
return componentsToMarkdown('component', json, filepath, componentCount++, styleDocs);
}
function renderAPI(type) {
return function(filepath) {
var json;
let json;
try {
json = jsDocs(fs.readFileSync(filepath).toString());
} catch(e) {
} catch (e) {
console.error('Cannot parse file', filepath, e);
json = {};
}
return componentsToMarkdown(type, json, filepath, n++);
return componentsToMarkdown(type, json, filepath, componentCount++);
};
}
function renderStyle(filepath) {
var json = docgen.parse(
const json = docgen.parse(
fs.readFileSync(filepath),
docgenHelpers.findExportedObject,
[docgen.handlers.propTypeHandler]
);
// Remove deprecated transform props from docs
if (filepath === "../Libraries/StyleSheet/TransformPropTypes.js") {
if (filepath === '../Libraries/StyleSheet/TransformPropTypes.js') {
['rotation', 'scaleX', 'scaleY', 'translateX', 'translateY'].forEach(function(key) {
delete json['props'][key];
delete json.props[key];
});
}
return componentsToMarkdown('style', json, filepath, n++);
return componentsToMarkdown('style', json, filepath, componentCount++);
}
var components = [
const components = [
'../Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js',
'../Libraries/Components/DatePicker/DatePickerIOS.ios.js',
'../Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js',
@ -228,7 +231,7 @@ var components = [
'../Libraries/Modal/Modal.js',
'../Libraries/CustomComponents/Navigator/Navigator.js',
'../Libraries/Components/Navigation/NavigatorIOS.ios.js',
'../Libraries/Picker/PickerIOS.ios.js',
'../Libraries/Components/Picker/PickerIOS.ios.js',
'../Libraries/Components/Picker/Picker.js',
'../Libraries/Components/ProgressBarAndroid/ProgressBarAndroid.android.js',
'../Libraries/Components/ProgressViewIOS/ProgressViewIOS.ios.js',
@ -253,7 +256,7 @@ var components = [
'../Libraries/Components/WebView/WebView.ios.js',
];
var apis = [
const apis = [
'../Libraries/ActionSheetIOS/ActionSheetIOS.js',
'../Libraries/Utilities/Alert.js',
'../Libraries/Utilities/AlertIOS.js',
@ -286,28 +289,28 @@ var apis = [
'../Libraries/Vibration/Vibration.js',
];
var stylesWithPermalink = [
const stylesWithPermalink = [
'../Libraries/StyleSheet/LayoutPropTypes.js',
'../Libraries/StyleSheet/TransformPropTypes.js',
'../Libraries/Components/View/ShadowPropTypesIOS.js',
];
var stylesForEmbed = [
const stylesForEmbed = [
'../Libraries/Components/View/ViewStylePropTypes.js',
'../Libraries/Text/TextStylePropTypes.js',
'../Libraries/Image/ImageStylePropTypes.js',
];
var polyfills = [
const polyfills = [
'../Libraries/Geolocation/Geolocation.js',
];
var all = components
const all = components
.concat(apis)
.concat(stylesWithPermalink)
.concat(polyfills);
var styleDocs = stylesForEmbed.reduce(function(docs, filepath) {
const styleDocs = stylesForEmbed.reduce(function(docs, filepath) {
docs[path.basename(filepath).replace(path.extname(filepath), '')] =
docgen.parse(
fs.readFileSync(filepath),
@ -323,7 +326,7 @@ var styleDocs = stylesForEmbed.reduce(function(docs, filepath) {
}, {});
module.exports = function() {
n = 0;
componentCount = 0;
return [].concat(
components.map(renderComponent),
apis.map(renderAPI('api')),