React Native: получение позиции элемента
Я укладываю Image
компонент с flexbox должен находиться в центре экрана, который работает довольно хорошо. Теперь я хочу второй Image
компонент, который будет отображаться непосредственно в верхней части первого. Второе изображение использует абсолютное позиционирование. В настоящее время я просто угадываю пиксели, чтобы они соответствовали, но, конечно, это не является точным и слишком много усилий по обслуживанию.
Я в значительной степени ищу React Native эквивалент jQuery's .offset()
, Есть ли такая вещь, и если нет, то какой лучший способ достичь этого?
8 ответов
React Native обеспечивает .measure(...)
метод, который принимает обратный вызов и вызывает его со смещением и шириной / высотой компонента:
myComponent.measure( (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X offset to frame: ' + fx)
console.log('Y offset to frame: ' + fy)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
})
Пример...
Следующее вычисляет макет пользовательского компонента после его визуализации:
class MyComponent extends React.Component {
render() {
return <View ref={view => { this.myComponent = view; }} />
}
componentDidMount() {
// Print component dimensions to console
this.myComponent.measure( (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X offset to frame: ' + fx)
console.log('Y offset to frame: ' + fy)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
})
}
}
Заметки об ошибках
Обратите внимание, что иногда компонент не завершает рендеринг раньше
componentDidMount()
называется. Если вы получаете нули в результатеmeasure(...)
затем завернуть вsetTimeout
должен решить проблему, а именно:setTimeout( myComponent.measure(...), 0 )
Ты можешь использовать onLayout
чтобы получить ширину, высоту иотносительную к родительской позиции компонента в самый ранний момент, когда они доступны:
<View
onLayout={event => {
const layout = event.nativeEvent.layout;
console.log('height:', layout.height);
console.log('width:', layout.width);
console.log('x:', layout.x);
console.log('y:', layout.y);
}}
>
По сравнению с использованием.measure()
как показано в принятом ответе, это имеет то преимущество, что вам никогда не придется возиться с отсрочкой .measure()
звонки с setTimeout
чтобы убедиться, что измерения доступны, но недостатком является то, что они не дают смещений относительно всей страницы, а только смещений относительно родительского элемента.
У меня была похожая проблема, и я решил ее, объединив ответы выше.
class FeedPost extends React.Component {
constructor(props) {
...
this.handleLayoutChange = this.handleLayoutChange.bind(this);
}
handleLayoutChange() {
this.feedPost.measure( (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
})
}
render {
return(
<View onLayout={(event) => {this.handleLayoutChange(event) }}
ref={view => { this.feedPost = view; }} >
...
Теперь я могу видеть положение моего элемента feedPost в журналах:
08-24 11:15:36.838 3727 27838 I ReactNativeJS: Component width is: 156
08-24 11:15:36.838 3727 27838 I ReactNativeJS: Component height is: 206
08-24 11:15:36.838 3727 27838 I ReactNativeJS: X offset to page: 188
08-24 11:15:36.838 3727 27838 I ReactNativeJS: Y offset to page: 870
Мне нужно было найти положение элемента внутри ListView и использовать этот фрагмент, который работает вроде .offset
:
const UIManager = require('NativeModules').UIManager;
const handle = React.findNodeHandle(this.refs.myElement);
UIManager.measureLayoutRelativeToParent(
handle,
(e) => {console.error(e)},
(x, y, w, h) => {
console.log('offset', x, y, w, h);
});
Это предполагает, что у меня был ref='myElement'
на моем компоненте.
Если вы используете функциональные компоненты и не хотите использовать
forwardRef
чтобы измерить абсолютную компоновку вашего компонента, вы можете получить ссылку на него из
LayoutChangeEvent
в
onLayout
Перезвони.
Таким образом, вы можете получить абсолютную позицию элемента:
<MyFunctionComp
onLayout={(event) => {
event.target.measure(
(x, y, width, height, pageX, pageX) => {
doSomethingWithAbsolutePosition({
x: x + pageX,
y: y + pageY,
});
},
);
}}
/>
Протестировано с React Native 0.63.3.
В объекте аргумента ref есть свойство measureInWindow, которое можно использовать следующим образом:
const [offset, setOffset] = React.useState();
<View ref={(view) =>
if(!view) return;
view.measureInWindow((x, y) => {
setOffset({ x, y });
})
}>
</View>
Похоже, что это изменилось в последней версии React Native при использовании ссылок для расчета.
Объявите ссылки таким образом.
<View
ref={(image) => {
this._image = image
}}>
И найдите ценность таким образом.
_measure = () => {
this._image._component.measure((width, height, px, py, fx, fy) => {
const location = {
fx: fx,
fy: fy,
px: px,
py: py,
width: width,
height: height
}
console.log(location)
})
}
У меня сработало следующее.
- Получить позицию на макете.
// ...
const [position, setPosition] = useState({x: 0, y: 0});
// ...
return (
// ...
<View
onLayout={event => {
event.target.measure((x, y, width, height, pageX, pageY) => {
setPosition({x: x + pageX, y: y + pageY});
});
}}
/>
// ...
);
- Получить позицию по вызову.
const ref = useRef(null);
// ...
const [position, setPosition] = useState({x: 0, y: 0});
// ...
return (
// ...
<TouchableOpacity
ref={ref}
activeOpacity={0.75}
onPress={() => {
ref.current.measure((x, y, width, height, pageX, pageY) => {
setPosition({x: x + pageX, y: y + pageY});
});
}}
/>
// ...
);