Moving a view outside of a scroll view in React Native: Tips and tricks

Currently, I am working on a unique feature where there is a horizontal ScrollView containing pink boxes. Each box is constructed using Animated.View along with a GestureDetector to handle the dragging functionality. My goal is to enable the user to drag a pink box outside of the scroll view area (represented by the blue box) so that it becomes "detached". However, the challenge arises when the dragged box remains connected to the scroll view even when it's moved beyond its boundaries.

To illustrate this issue, I have created a simplified example which demonstrates the problem through a GIF:

The GIF above showcases the behavior where the pink box continues to be scrolled along with the scroll view despite being moved outside of the designated area. Moreover, once the box is positioned in the white space outside of the scroll view, it loses the ability to be further dragged or panned, which is not the intended behavior.

I have prepared an Expo Snack for you to test and experience the problem firsthand. For the best results, I recommend testing it on an iOS device within the Expo Go app.

Below is the code snippet responsible for creating the scenario described above. The key point to note is the usage of overflow: visible property in the scroll view, as this may play a role in causing the observed behavior:

App.js

export default function App() {
  const boxes = useState([{id: 0}, {id: 1}]);

  return (
    <SafeAreaView>
      <GestureHandlerRootView style={styles.container}>
        <View style={styles.scrollViewContainer}>
          <ScrollView style={styles.scrollViewStyles} horizontal={true}>
            {boxes.map(({id}) => <Box key={id} />)}
          </ScrollView>
        </View>
        <StatusBar style="auto" />
      </GestureHandlerRootView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
  },
  scrollViewContainer: {
    width: '100%',
    backgroundColor: "lightblue",
    height: 150,
  },
  scrollViewStyles: {
    overflow: "visible"
  }
});

Additionally, here is the Box component mentioned above that is rendered within the App:

const SIZE = 100;
export default function Box(props) {
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const context = useSharedValue({x: 0, y: 0});

  const panGesture = Gesture.Pan()
    .onBegin(() => {
      context.value = {x: translateX.value, y: translateY.value};
    })
    .onUpdate((event) => {
      translateX.value = event.translationX + context.value.x;
      translateY.value = event.translationY + context.value.y;
    });

  const panStyle = useAnimatedStyle(() => ({
    transform: [
      {translateX: withSpring(translateX.value)},
      {translateY: withSpring(translateY.value)},
    ]
  }));

  return (<GestureDetector gesture={panGesture}>
    <Animated.View style={[styles.box, panStyle]} />
  </GestureDetector>);
}

const styles = StyleSheet.create({
  box: {
    width: SIZE,
    height: SIZE,
    marginHorizontal: 10,
    borderRadius: 20,
    backgroundColor: "pink",
    shadowColor: "#000",
    shadowOffset: {width: 0, height: 0},
    shadowOpacity: 0.5,
  },
});

Answer №1

To achieve this functionality, one approach I discovered was to remove the item from the scrollview when it is dragged outside and then re-render it in a different location.

You can determine if the object has been moved outside the scrollview by monitoring the onEnd event of the pan gesture.

 .onEnd((event) => {
      if (translateY.value > 90) {
        runOnJS(props.moveItem)(
          event.translationX ,
          event.translationY + context.value.y,
          props.id
        );
      }

Subsequently, you can exclude that item from the array within the scrollview and include it in a separate array outside the scrollview.

  const moveItem = (x, y, id) => {
    const boxesF = boxes.filter((e) => e.id != id);
    setBoxes(boxesF);
    movedItems.push();
    setMovedItems([...movedItems,{ x: x, y: y, id:id }]);
    console.log('called'+id);
    console.log(x);
    console.log(y);
  };

Then, you can render these items individually:

    <View style={styles.scrollViewContainer}>
      <ScrollView style={styles.scrollViewStyles} horizontal={true}>
        {boxes?.map(({ id }) => (
          <Box key={id} moveItem={moveItem} id={id} />
        ))}
      </ScrollView>
    </View>
    <View>
      {movedItems?.map((e) => {
        return <Box translateX={e.x} translateY={e.y} key={e.id}/>;
      })}
    </View>

I followed this method and created a sample snack project. While I successfully separated the items from the scrollview, the positioning may not be perfect, requiring additional calculations based on translation values for accurate placement.

Snack Url

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

The "as" property in NextJS Link does not properly reload the page when opened

I recently started using NextJS and I have a question about its router. I want to link to a page, but I would like the URL to be different. <Link href="/About/About" as="/about-page"> <a> Who We Are <im ...

Leveraging image values for selecting an image from a collection and showcasing it (Javascript)

I have a collection of images that I want to enlarge upon clicking. Here is what I currently have: <div class="gallery"> <div> <img src="content/imggallery/img1.jpg" class="oldimg" value="0"> </div> ...

Updating the text of an element will occur only when the specified condition has been met

I am trying to dynamically change the text within the <span data-testid="recapItemPrice">ZADARMO</span> element from 'ZADARMO' to 'DOHODOU' only when the text inside the parent element 'INDIVIDUÁLNA CENA PREP ...

Ensuring the legitimacy of Rails form submissions

I am encountering an issue with validating a form before submitting it, as the form is being submitted without triggering the jQuery part. <%= form_for(:session,class:"form-signin form-horizontal",:id=> "form",:role=> "form") do |f| %> & ...

Need help with checking for the presence of a value in a multidimensional array using Node.js/Javascript?

Imagine you have an array structured like this: $game = Array ( ['round'] => Array ( ['match'] => Array ( ['player_2'] => Array ( ...

What are the steps to generate two unique instances from a single class?

Is there a way to output two instances of the class Cat : Skitty, 9 years and Pixel, 6 years, in the console? (() => { class Cat { constructor(name, age) { this.name = name; this.age = age; } } docume ...

Step-by-step guide on validating a user in Joomla using AJAX and jQuery

Is there a way to authenticate a user in Joomla through an AJAX call? I want to implement an error effect if the login is incorrect and redirect the user upon successful authentication. I am specifically interested in using JQuery's .ajax API for thi ...

Converting month names to uppercase with moment.js

After changing the moment's locale by setting the property below: moment.locale(chosenLocale); It appears that everything is functioning correctly. The month names and weekday names are displayed according to the selected locale, and week numbers ar ...

Transferring MySQL data from PHP to Javascript using JSON format

I am attempting to utilize a code snippet that fetches data from a mySQL database, assigns that data to a variable, aggregates all the resulting values into a PHP array, and then converts it to JSON format. Subsequently, I transfer the JSON data to JavaScr ...

Substitute dynamic Angular expressions with fixed values within a string

Inside my angular controller, I am defining a stringTemplate containing expressions like "{{data.a}} - {{data.b}}". Along with that, I have an object named data with values {a: "example1", b: "example2"}. My goal is to find a way to replace the dynamic ex ...

Leveraging data stored in a parent component within a child component in Vue.js

Working with Laravel Spark, I have implemented a component within another component. <parent-component :user="user"> <child-component :user="user" :questions="questions"></child-component> <parent-component> Inside the parent ...

Abbreviating AngularJS with JavaScript

While Angular offers the ng shortcut for directives in markup, I am curious about how to implement this in JavaScript code. For example, instead of writing angular.isArray, is it possible to use ng.isArray similar to jQuery ($) or Underscore (_)? ...

Develop a unique Kotlin/JS WebComponent featuring custom content

I am trying to create a custom tag using Kotlin that includes default content. While the example I found works well, I am having trouble adding default content (such as an input element) inside the custom tag. After attempting various approaches, I have o ...

A step-by-step guide to moving Vue .prototype to its own file

I have a couple of functions that I need to add to Vue's .prototype. However, I don't want to clutter up the main.js file. I attempted to move the prototypes like this: //main.js import "./vue-extensions/prototypes"; ...

Error: The system reference 'Sys' is not defined in ASP.NET

I am trying to implement ajax functionality on the ASP.NET platform. To achieve this, I am using ScriptManager in conjunction with jQuery, and adding the script after the "document is ready" event. $(document).ready(function () { // sync {{scopeN ...

Unknown CSS element discovered: bootstrap, gradient, carousel

I recently created a random quote app using javascript, jQuery, and bootstrap on Codepen. Everything worked perfectly there. However, when I organized the files, pushed them to git, and tried to view the app from Safari, I encountered some warnings and t ...

Tips for positioning a div next to an input box when it is in focus

I am working on a scenario where I have used CSS to extend the search box when focused. The idea is that when the search box is focused, it should decrease in size and a cancel button should appear next to it. This is the CSS code I am using: .clrble .fr ...

To properly handle this file type in React, ensure you have the correct babel loader installed

https://i.sstatic.net/iNFs3.pngAn error is triggered when trying to compile with webpack. The message indicates that an appropriate loader may be needed to handle this file type. The libraries I am using are: https://i.sstatic.net/a8fXR.png Below are my ...

Encountering issues with accessing properties of undefined while chaining methods

When comparing lists using an extension method that calls a comparer, I encountered an error. Here is the code snippet: type HasDiff<T> = (object: T, row: any) => boolean; export const isListEqualToRows = <T>(objects: T[], rows: any[], has ...

Combining rendering and redirecting in ExpressJS for efficient web page management

I'm currently using ExpressJS to develop the backend of my website, which involves calling an API to generate a response. One specific requirement I have is to indicate that a payment was successful and then automatically redirect to another page afte ...