From 21c3931b697c29ad5509226866960f987d918be0 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Tue, 1 Jan 2019 12:21:29 -0800 Subject: [PATCH] [fix] use getBoundingClientRect to measure layout Fix #1037 Fix #1151 --- .../src/exports/UIManager/index.js | 20 +++++++----------- .../modules/getBoundingClientRect/index.js | 21 +++++++++++++++++++ .../src/modules/normalizeNativeEvent/index.js | 19 ++++++----------- 3 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 packages/react-native-web/src/modules/getBoundingClientRect/index.js diff --git a/packages/react-native-web/src/exports/UIManager/index.js b/packages/react-native-web/src/exports/UIManager/index.js index 20b1fca56..7bfb605bd 100644 --- a/packages/react-native-web/src/exports/UIManager/index.js +++ b/packages/react-native-web/src/exports/UIManager/index.js @@ -7,28 +7,24 @@ * @noflow */ +import getBoundingClientRect from '../../modules/getBoundingClientRect'; import setValueForStyles from '../../vendor/react-dom/setValueForStyles'; const getRect = node => { - const height = node.offsetHeight; + // Unlike the DOM's getBoundingClientRect, React Native layout measurements + // for "height" and "width" ignore scale transforms. + // https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements + const { x, y, top, left } = getBoundingClientRect(node); const width = node.offsetWidth; - let left = node.offsetLeft; - let top = node.offsetTop; - node = node.offsetParent; - - while (node && node.nodeType === 1 /* Node.ELEMENT_NODE */) { - left += node.offsetLeft - node.scrollLeft; - top += node.offsetTop - node.scrollTop; - node = node.offsetParent; - } - return { height, left, top, width }; + const height = node.offsetHeight; + return { x, y, width, height, top, left }; }; const measureLayout = (node, relativeToNativeNode, callback) => { const relativeNode = relativeToNativeNode || (node && node.parentNode); if (node && relativeNode) { setTimeout(() => { - const relativeRect = getRect(relativeNode); + const relativeRect = getBoundingClientRect(relativeNode); const { height, left, top, width } = getRect(node); const x = left - relativeRect.left; const y = top - relativeRect.top; diff --git a/packages/react-native-web/src/modules/getBoundingClientRect/index.js b/packages/react-native-web/src/modules/getBoundingClientRect/index.js new file mode 100644 index 000000000..d81aa7610 --- /dev/null +++ b/packages/react-native-web/src/modules/getBoundingClientRect/index.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2015-present, Nicolas Gallagher. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + */ + +/* global HTMLElement */ + +const getBoundingClientRect = (node: HTMLElement) => { + if (node) { + const isElement = node.nodeType === 1; /* Node.ELEMENT_NODE */ + if (isElement && typeof node.getBoundingClientRect === 'function') { + return node.getBoundingClientRect(); + } + } +}; + +export default getBoundingClientRect; diff --git a/packages/react-native-web/src/modules/normalizeNativeEvent/index.js b/packages/react-native-web/src/modules/normalizeNativeEvent/index.js index 126dd0992..f6368d296 100644 --- a/packages/react-native-web/src/modules/normalizeNativeEvent/index.js +++ b/packages/react-native-web/src/modules/normalizeNativeEvent/index.js @@ -7,18 +7,11 @@ * @flow */ +import getBoundingClientRect from '../getBoundingClientRect'; + const emptyArray = []; const emptyFunction = () => {}; -const getRect = node => { - if (node) { - const isElement = node.nodeType === 1 /* Node.ELEMENT_NODE */; - if (isElement && typeof node.getBoundingClientRect === 'function') { - return node.getBoundingClientRect(); - } - } -}; - // Mobile Safari re-uses touch objects, so we copy the properties we want and normalize the identifier const normalizeTouches = touches => { if (!touches) { @@ -35,13 +28,13 @@ const normalizeTouches = touches => { clientY: touch.clientY, force: touch.force, get locationX() { - rect = rect || getRect(touch.target); + rect = rect || getBoundingClientRect(touch.target); if (rect) { return touch.pageX - rect.left; } }, get locationY() { - rect = rect || getRect(touch.target); + rect = rect || getBoundingClientRect(touch.target); if (rect) { return touch.pageY - rect.top; } @@ -121,13 +114,13 @@ function normalizeMouseEvent(nativeEvent) { force: nativeEvent.force, identifier: 0, get locationX() { - rect = rect || getRect(nativeEvent.target); + rect = rect || getBoundingClientRect(nativeEvent.target); if (rect) { return nativeEvent.pageX - rect.left; } }, get locationY() { - rect = rect || getRect(nativeEvent.target); + rect = rect || getBoundingClientRect(nativeEvent.target); if (rect) { return nativeEvent.pageY - rect.top; }