反应本机: 获取元素的位置

我的风格 Image组件与柔性盒在屏幕的中心,工作得非常好。现在我希望第二个 Image组件直接显示在第一个组件的顶部。第二个图像是使用绝对定位。目前我只是猜测像素,以便它适合,但当然这是不准确的,太多的可维护性的努力。

我正在寻找与 jQuery 的 .offset()相当的 React 本机版本。有没有这样的事情,如果没有什么是最好的方式来实现这一点?

165112 次浏览

I needed to find the position of an element inside a ListView and used this snippet that works kind of like .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);
});

This assumes I had a ref='myElement' on my component.

React Native provides a .measure(...) method which takes a callback and calls it with the offsets and width/height of a component:

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)
})

Example...

The following calculates the layout of a custom component after it is rendered:

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)
})
}
}

Bug notes

  • Note that sometimes the component does not finish rendering before componentDidMount() is called. If you are getting zeros as a result from measure(...), then wrapping it in a setTimeout should solve the problem, i.e.:

    setTimeout( myComponent.measure(...), 0 )
    

This seems to have changed in the latest version of React Native when using refs to calculate.

Declare refs this way.

  <View
ref={(image) => {
this._image = image
}}>

And find the value this way.

  _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)
})
}

You can use onLayout to get the width, height, and relative-to-parent position of a component at the earliest moment that they're available:

<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);
}}
>

Compared to using .measure() as shown in the accepted answer, this has the advantage that you'll never have to fiddle around deferring your .measure() calls with setTimeout to make sure that the measurements are available, but the disadvantage that it doesn't give you offsets relative to the entire page, only ones relative to the element's parent.

I had a similar problem and solved it by combining the answers above

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; }} >
...

Now I can see the position of my feedPost element in the logs:

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

If you use function components and don't want to use a forwardRef to measure your component's absolute layout, you can get a reference to it from the LayoutChangeEvent in the onLayout callback.

This way, you can get the absolute position of the element:

<MyFunctionComp
onLayout={(event) => {
event.target.measure(
(x, y, width, height, pageX, pageY) => {
doSomethingWithAbsolutePosition({
x: x + pageX,
y: y + pageY,
});
},
);
}}
/>

Tested with React Native 0.63.3.

There is a measureInWindow property on the ref argument object that can be used like so:

const [offset, setOffset] = React.useState();


<View ref={(view) =>
if(!view) return;
view.measureInWindow((x, y) => {
setOffset({ x, y });
})
}>
</View>