Detecting view position changes in React Native on layout update

I am currently working on a project where I need to track the position of a view that is being moved with a pan responder. Although I am using the onLayout prop to access the width, height, x and y positions, it seems to only run during the first render. Any suggestions on how to handle this issue?

Below is the code snippet:

import React, { useState, useRef } from "react";
import {
  View,
  Animated,
  PanResponder,
  Dimensions,
  StyleSheet,
} from "react-native";

const WINDOW_HEIGHT = Dimensions.get("window").height;

export default function Cropper({ photo }) {
  const [height, setHeight] = useState(WINDOW_HEIGHT / 2); // For future use

  const pan = useRef(new Animated.ValueXY()).current;

  const panResponder = useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onPanResponderMove: Animated.event([null, { dy: pan.y }]),
      onPanResponderRelease: () => {
        pan.flattenOffset();
      },
    })
  ).current;

 const onLayout = (event) => {
    const {
      nativeEvent: { layout },
    } = event;
    
    // Code logic for recalculating top and bottom views' height goes here
  };

  const panStyle = {
    transform: pan.getTranslateTransform(),
  };

  return (
    <View style={styles.container}>
      <View style={styles.blurView} />
      <Animated.View
        onLayout={(event) => onLayout(event)}
        {...panResponder.panHandlers}
        style={[
          styles.cropper,
          panStyle,
          {
            height: height,
          },
        ]}
      />
      <View style={styles.blurView} />
      <View style={styles.bottomButtonsContainer}></View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  blurView: {
    flex: 1,
    width: "100%",
    backgroundColor: "rgba(0, 0, 0, .9)",
  },
  cropper: {
    width: "100%",
    backgroundColor: "red",
  },
  bottomButtonsContainer: {
    position: "absolute",
    bottom: 0,
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    width: "100%",
    height: 120,
  },
});

The goal is to determine the middle view position as the user interacts with it, and then dynamically adjust the heights of the top and bottom views accordingly.

Answer №1

When including onLayout as a prop,

export default function Cropper({ photo }) {
needs to be changed to
export default function Cropper({ photo, onLayout }) {

If not, it seems you'll need to define onLayout according to another response.

Answer №2

It seems that you may have overlooked adding const for your onLayout.

I tested out the code on snack expo and was able to successfully trigger console.logs while moving the red view.

Answer №3

Issue:

The OnLayout method fails to trigger on an Animated.View when a custom PanResponder is utilized for moving the Animated.View via style transformations.

Resolution(full code snippet provided below):

To resolve this issue, it is necessary to add an additional listener to the onPanResponderMove method on the PanResponder. This listener can then execute any required method with information from the movement event.

onPanResponderMove: Animated.event([null, { dy: pan.y }], {listener: ({nativeEvent}) => {onSquareMoved(nativeEvent)}}),

const onSquareMoved = (event) => {
  console.log('square moved changes')
}

In order to invoke a method every time the panResponder moves, a listener needs to be added that captures the event and triggers the onSquareXChanged method (a modified version of onLayout).

A complete snack has been included which calls a method each time the square is moved, accessible here:

This approach draws inspiration from an answer shared by @ajthyng. While examining the operation of the snack in your environment, I observed that onLayout only fires during the initial render and not while the square is being moved. A review of debugging outcomes and responses outlined in the following GitHub issue: https://github.com/facebook/react-native/issues/28775, suggests that onLayout does not activate when style transformation values change within an Animated.View.

If you have a more intricate component within the Animated.View, or if the animation demands are computationally intensive, performance issues may arise. For those interested in implementing advanced animation effects without sacrificing performance, I recommend exploring the Reanimated 2 library currently in Alpha stage. This library empowers users to define animations logic that will subsequently run on a separate thread by leveraging the worklet functionality to run animation logic independently while maintaining 60 fps.

William Candillon's insightful video delves into the library's new syntax with an example akin to the aforementioned question's snack: https://www.youtube.com/watch?time_continue=471&v=e5ALKoP1m-k&feature=emb_title

Should you have further inquiries regarding this matter, feel free to reach out.

Working Code Example:

import React, { useState, useRef } from "react";
import {
  View,
  Animated,
  PanResponder,
  Dimensions,
  StyleSheet,
} from "react-native";

const WINDOW_HEIGHT = Dimensions.get("window").height;

export default function Cropper({ photo }) {
  const [height, setHeight] = useState(WINDOW_HEIGHT / 2); // Use in future

  const pan = useRef(new Animated.ValueXY()).current;

  const panResponder = useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onPanResponderMove: Animated.event([null, { dy: pan.y }],
        {listener: ({nativeEvent}) => onSquareMoved(nativeEvent)}}),
      onPanResponderRelease: () => {
        pan.flattenOffset();
      },
    })
  ).current;

  const onSquareMoved = (event) => {
    console.log('square was moved')
  }

  return (
    <View style={styles.container}>
      <View style={styles.blurView} />
      <Animated.View
        onLayout={(event) => onLayout(event)}
        {...panResponder.panHandlers}
        style={[
          styles.cropper,
          panStyle,
          {
            height: height,
          },
        ]}
      />
      <View style={styles.blurView} />
      <View style={styles.bottomButtonsContainer}></View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  blurView: {
    flex: 1,
    width: "100%",
    backgroundColor: "rgba(0, 0, 0, .9)",
  },
  cropper: {
    width: "100%",
    backgroundColor: "red",
  },
  bottomButtonsContainer: {
    position: "absolute",
    bottom: 0,
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    width: "100%",
    height: 120,
  },
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating an Angular loading component using the route parameter

When a user clicks on a link in the home component, I want to load a different component based on the URL parameter. For example, if the user is at "/home" and sees a list of links: Link 1 Link 2 Clicking on Link 1 should load the details component with ...

JavaScript influences CSS in EasySlider 1.7, creating a harmonious relationship between the

Struggling to achieve a captivating overlay effect with a slider using EasySlider 1.7, incorporating both images and corresponding text. However, encountering difficulties in aligning the text correctly on the page. The black overlay visible on the main s ...

Divide table rows in html without a header into two equal parts, with 50% on the left side

I am trying to format 20 rows in a standard html table without any headers. The current setup looks like this: current situation <table> <tbody> <tr> <td>1</td> </tr> <tr> ...

What is the best way to select photos from a directory and showcase them on a webpage using HTML

I am currently working on developing an image slideshow feature for my website. I have created a basic code that displays a set of images in a slideshow format. However, whenever I add new images to the directory, I have to manually update the code to incl ...

Angular module delayed initialization

After defining my angular module in a routine following window.onload, I've noticed that angular seems to look for the module before the window is fully loaded. However, I want to make sure all assets are loaded before triggering the angular module. ...

Using Highstocks to set color spans for a line rather than specifying each individual line color

I am working on creating a Gantt Chart using Highstocks to compare multiple series. I would like the first span color to be Red, the second to be Blue, and the third to be Green. How can I achieve this color setup? Additionally, how can I configure the too ...

Changing the value of an element using JavaScript through Selenium WebDriver in .NET

I'm attempting to perform a popup page test using the chrome webdriver and selenium2 with .NET, but I am encountering some difficulties. After the window pops up, I need to modify the value of an element. Specifically, I need to change the default val ...

What is the reasoning behind having additional parameters right next to require('../models/owners.js')?

import('../utils/handlers.js')(database, Sequelize); The structure of using import(..something)(why?) consecutively is a bit confusing to me. Can you explain it further? ...

How can I modify a PHP file to be compatible with Ajax functionality?

I'm currently exploring the world of CodeIgniter and facing some challenges with AJAX. This is my first time working with it, so I have quite a few questions popping up. What I'm looking for: I want to be able to post data using AJAX and retrie ...

Tips for Aligning h5 Headings with Unordered Lists

My footer contains the following code: HTML: <div class="container"> <div class="row"> <div class="col-md-6"> <div class="pull-right"> <h5><u><i>Information</i></u> ...

An issue has been detected where the bullet list is uneven due to the CSS property/value columns being set

Is there a more effective way to split a bulleted list into two columns than the method I am currently using and ensure that the columns are aligned at the top? .hand-bullet-list ul{ list-style-type: none; margin-left: 0px; } .hand-bullet-list ...

Tips for implementing sorting and filtering with Ajax and Json in PHP and MySQL applications

Greetings! I'm a newcomer in the world of software development, currently working on a software site. I've successfully built the pages and layout using HTML, CSS, and JavaScript, which was the easy part. Now, my challenge lies in creating main c ...

Permanently remove the span tag and any associated text from the HTML document

Currently, I am working on a project that involves numerous page numbers marked using the span class. Below is an example of this markup: <p>Cillacepro di to tem endelias eaquunto maximint eostrum eos dolorit et laboria estiati buscia ditiatiis il ...

Using jQuery UI datepicker - how to set a parameter based on a specific condition

New to the world of JavaScript, I am trying my hand at implementing the jQuery UI datepicker widget. My goal is to add an additional line to the widget parameters if the variable selectedDate has a value. However, my current approach seems to be ineffecti ...

Error encountered in parsing JSON: abrupt end of data (JavaScript)

I have been working on a few functions that are responsible for parsing JSON data, both external and internal, and displaying it on a local webpage using the localStorage feature. While I have successfully displayed the external JSON data, I am running int ...

What dimensions are optimal for images on a Responsive web design?

In the process of making our website responsive, I have a question regarding image sizes. If I want images to fill the entire screen, excluding the navigation header, what specific dimensions should these images be in order to display correctly on all type ...

What specific characteristic of TypeScript's number data type or the Math.ceil() function is responsible for this calculation mistake?

Currently, I am working on a function in Typescript that is supposed to generate a unique number each time it runs. However, there seems to be a problem with the arithmetic as the results are not always correct. Upon further examination of the code below, ...

Reorganize content from one div to another when the screen size changes using Bootstrap

Creating a login screen with 2 columns has been my recent project. For large screens: The left column contains the login controls while the right column displays an image. On small screens: The top column shows the image, and the bottom column houses the ...

How do I change the 'CSS Theme' of my website?

With Halloween approaching, I am eager to transform my website's BODY css into a spooky Halloween theme. The challenge is that my .Net pages are Templates. Is there a way for me to ensure that the body class checks for an existing CSS theme override? ...

I am unable to engage with an element encapsulated within a #shadow-root that includes an <iframe> element

Original page source: Currently utilizing selenium with Java for web automation. In order to reach the shadow-root, I am utilizing JavaScriptExecutor (document.shadowRoot.querySelector) Successfully able to interact with various elements within the page ...