What I try:
I add in my code
`
newRef?.current?.measure( (fx, fy, width, height, px, py) => {
setY(fy)
})
`
With this way I get y-position of Item but after first windowHeight the measure start from begin. How can achieve that the measure continue in all ScrollView?
Related
On web, you can get any element by its position:
document.elementFromPoint(x, y)
Is there something similar in React Native? Can it be done with a native bridge (Java/Objective C)?
I would like to get an element on screen by position X and Y (Ignoring empty places and following transformation or styling calculations like padding, margin, borderRadius) then set it a native prop or dispatch events.
OBS: I'm not referring to onLayout property or any other declared in the component's construction/render, my idea is from any X and Y position get an element that corresponds to these coordinates then get a reference to it. A use case for example: Create a virtual cursor that dispatch click events on correct components, following margin/padding and ignoring pointerEvents none.
Image Example
You can use onLayout prop of React Native components.
You should check this link
Yes you can get your current elemnts position like in. 2 ways,
onLayout
suppose on click of that element you want to know that location
assign a ref to that
const newRef = useRef();
onButtonPress = () => {
newRef?.current?.measureInWindow( (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)
})
}
const onLayout=(event)=> {
const {x, y, height, width} = event.nativeEvent.layout;
}
return(
<TouchableOpacity ref={useRef} onLayout={onLayout} onPress={onButtonPress} >
</TouchableOpacity>
)
I want to move a circle from top to bottom inside of a View. For top I simply figured out y = 0.
How can I measure the y co-ordinate for the end of a view.
I tried Dimensions, but the height and width are different from coordinates.
My app uses Animated.ValueXY({ x: xCordinate, y: yCordinate })
The xCordinate and yCordinate get passed from a function that changes coordinates.
Animated.timing(this.moveAnimation, {
duration: 1000,
toValue: {x: xCordinate, y: yCordinate},
}).start();
I managed random value for my phone but it is not same for all devices
React native provides a onLayout method that gets triggered every time your view renders. Here's a sample code.
onLayout = (e) => {
this.setState({
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height,
x: e.nativeEvent.layout.x,
y: e.nativeEvent.layout.y
})
}
render (
<View onLayout={this.onLayout}>
<Text>Hamza Waleed</Text>
</View>
)
}
I'm trying to make an SVG element draggable inside but PanResponder doesn't account for the SVG viewBox causing the dragged element to not follow the touch around the screen.
I've tried utilizing the X and Y values of the touch event to affect the X and Y value of the SVG element, but get a worse effect than when I use the PanResponder. When I switched to using just the X and Y values of the touch event I tried to convert the touch event X and Y to the SVG coordinates by using an equation I found on svgwg.org.
viewBox: "0 0 960.1 1856.51"
Code used when I was converting X Y values to SVG cordinates
svgXYConvert = (eX, eY) =>{
let eW = this.props.width;
let eH = this.props.height;
let [vbX, vbY, vbW, vbH] = this.props.viewBox.split(" ");
// Calculate the scale of elements and vb
let scaleX = eW/vbW;
let scaleY = eH/vbH;
// translation points
let translateX = eX - (vbX * scaleX);
let translateY = eY - (vbY * scaleY);
return [translateX, translateY];
}
PanResponder component
import React from 'react';
import {PanResponder, Animated} from 'react-native';
import {Image, G, Rect} from 'react-native-svg';
const AnimatedG = Animated.createAnimatedComponent(G);
class DragImg extends React.Component {
constructor(props) {
super(props)
this.state = {
pan: new Animated.ValueXY({x: this.props.x, y: this.props.y}),
x: this.props.x,
y: this.props.y,
}
}
componentWillMount() {
this._panResponder = PanResponder.create({
onPanResponderTerminationRequest: () => false,
onStartShouldSetPanResponder: (e, gesture) => true,
onPanResponderGrant: (e, gesture) => {
this.state.pan.setOffset(this.state.pan.__getValue());
},
onPanResponderMove: Animated.event([
null, { dx: this.state.pan.x, dy: this.state.pan.y}
])
},
onPanResponderRelease: (e) => {
this.state.pan.flattenOffset();
}
})
}
render(){
let imgStyle = {transform: this.state.pan.getTranslateTransform()};
return <AnimatedG style={imgStyle} {...this._panResponder.panHandlers} x={this.state.x} y={this.state.y}>
<Image href={this.props.href} height={this.props.height} width={this.props.width} />
<Rect fill="transparent" height={this.props.height} width={this.props.width}/>
</AnimatedG>
}
}
export default DragImg;
When this is rendered inside of an SVG that is 100% height of the device, I am forcing landscape on an iPad, I allow them to zoom the SVG that is being used to see the details clearer. If they zoom in where the SVG takes up the entire screen the element moves at the right speed with the PanResponder and follows the touch perfectly almost, but if they are zoomed out or at the default screen we show them where the entire SVG is visible the dragged element moves slowly and doesn't track well.
I'm not sure what I should be doing differently to get the SVG element to track well regardless of if they are zoomed in or out on the SVG.
I made some similar code. I added an Animated.View with position: absolute so I move it and in the same time the SVG Circleis moved inside of the SVG area. To do it, I scaled the SVG size to have a perfect size with the maxWidth and minWidth. Also, the SVG must have this property: preserveAspectRatio="xMinYMin"its because it need to start in (0,0) as start point.
You can see it working here: https://snack.expo.io/#albertcito/svg-pan-responder
I need to get a list of components (x, y) at specified coordinates, preferably in order from children to grandparents. Would be awesome if absolutely positioned components were taken into account.
Think: which components (and in what order) would have the opportunity to react to a click/touch event at (x, y).
I am currently trying to build a mechanism that would keep track of every component that exists, and what are it's relations to other components (which one is parent, which one is children, so I can sort them). It's ugly, lots of edgecases, components that I wish were trackable need to know about it and cooperate, and I think I'm just reimplementing something that's already there.
EDIT: This is (probably) not about measuring. It's about asking "what components are currently rendered under pixel (123,456)"
measure is react native default method for any component to get update on it.
Below is the example which help you to get x and y coordinate:
class screenname extends React.Component {
render() {
return <View ref={view => { this.screenname = view; }} />
}
componentDidMount() {
// Print component dimensions to console
this.screenname.measure( (fx, fy, width, height, px, py) => {
console.log('Component width is: ' + width)
console.log('Component height is: ' + height)
console.log('X : ' + fx)
console.log('Y : ' + fy)
console.log('X offset to page: ' + px)
console.log('Y offset to page: ' + py)
})
}
}
To find position of children with respect to parent what you have to do is pass parent reference to measureLayout function like below:-
import {findNodeHandle} from 'react-native';
calculateDimensions(){
const handler = findNodeHandle(this.rootRef);
this.childRef.measureLayout(handler, ( X_Position, Y_Position, Width, Height ) =>
{
this.setState({
X_Dimension : X_Position,
Y_Dimension: Y_Position,
Child_View_Height: Height,
Child_View_Width: Width
});
});
}
//Parent with nested child
<View ref={(ref) => this.rootRef = ref}>
<View ref={(ref) => this.childRef = ref}>
</View>
</View>
Hope this will help
I'm trying to render an curved vertical list like this iOS component: https://github.com/makotokw/CocoaWZYCircularTableView
That component (written in Obj-c) iterates the visible cells when laying them out, and sets the frame (i.e. indent) using asin.
I know in React Native I can set the leftMargin style in the renderItem callback, but I can't figure out how to get the on-screen index of the item - all I have is the index into the source data. And also, at that point, I don't think I have access to the absolute position.
Any ideas?
The function you are looking for is
onViewableItemsChanged.
You can use it with viewabilityConfig which provides us with
minimumViewTime,viewAreaCoveragePercentThreshold,waitForInteraction
which can be set accordingly
const VIEWABILITY_CONFIG = {
minimumViewTime: 3000,
viewAreaCoveragePercentThreshold: 100,
waitForInteraction: true,
};
_onViewableItemsChanged = (info: {
changed: Array<{
key: string,
isViewable: boolean,
item: any,
index: ?number,
section?: any,
}>
}
){
//here you can have the index which is visible to you
}
<FlatList
renderItem={this.renderItem}
data={this.state.data}
onViewableItemsChanged={this._onViewableItemsChanged}
viewabilityConfig={VIEWABILITY_CONFIG}
/>
Thanks for both answers.
What I have ended up doing is deriving the visible items using the scroll offset of the list. This is simple because the list items all have the same height.
I do this in the onScroll handler, and at that point I calculate the horizontal offset for each item (and I use leftMargin / rightMargin to render this). It's not perfect, but it does give me an elliptical list.
_handleScroll = (event) => {
const topItemIndex = Math.floor(event.nativeEvent.contentOffset.y / LIST_ITEM_HEIGHT);
const topItemSpare = LIST_ITEM_HEIGHT-(event.nativeEvent.contentOffset.y % LIST_ITEM_HEIGHT);
const positionFromEllipseTop = (forIndex-topItemIndex)*LIST_ITEM_HEIGHT+topItemSpare;
const positionFromOrigin = Math.floor(Math.abs(yRadius - positionFromEllipseTop));
const angle = Math.asin(positionFromOrigin / yRadius);
if (orientation === 'Left') {
marginLeft = 0;
marginRight = ((xRadius * Math.cos(angle)))-LIST_ITEM_HEIGHT;
alignSelf = 'flex-end';
}
else if (orientation === 'Right') {
marginLeft = (xRadius * Math.cos(angle))-LIST_ITEM_HEIGHT;
marginRight = 0;
alignSelf = 'flex-start';
}
}
React-native's FlatList component has a prop called onLayout. You can get the position of the component on screen with this prop.
onLayout
Invoked on mount and layout changes with:
{nativeEvent: { layout: {x, y, width, height}}}
This event is fired immediately once the layout has been calculated,
but the new layout may not yet be reflected on the screen at the time
the event is received, especially if a layout animation is in
progress.