0

Using react-visibility-sensor I've created a higher component to animate the sections of my project on scroll.

I'm having problems trying to make it work only on the first scroll. At the moment the sections appear and disappear.

Any help would be really appreciated. Cheers!

import VisibilitySensor from 'react-visibility-sensor';

export const SlideUp = ({ children }) => {
  const [isVisible, setVisibility] = useState(false);

  const onChange = visiblity => {
    setVisibility(visiblity);
  };

  return (
    <VisibilitySensor
      partialVisibility
      offset={{ top: 200 }}
      onChange={onChange}
    >
      <div
        style={{
          transition: `opacity ${0.5}s ease, transform ${0.5}s ease`,
          opacity: isVisible ? 1 : 0,
          transform: isVisible ? `translateY(${0}px)` : `translateY(${20}px)`,
        }}
      >
        {children}
      </div>
    </VisibilitySensor>
  );
};```

- use example:

<SlideUp>
  <Section />
</SlideUp>
danihazler
  • 413
  • 1
  • 6
  • 13
  • I'm not totaly sure, what you would like to do and how I can create an example for you, but you can look in my [anwser](https://stackoverflow.com/a/62043131/9620269) for the post [ReactJS: React CountUp visible only once in visibility sensor](https://stackoverflow.com/questions/56928771/reactjs-react-countup-visible-only-once-in-visibility-sensor). I've tried to explain, how you can handle the visibility with the visibility sensor. – Rene May 27 '20 at 12:30

5 Answers5

1

My solution was to mix React Pose with React Visibility.

The visibility triggers the animation in React Pose which happens only once. The animation relies on Pose and not on visibility.

import VisibilitySensor from 'react-visibility-sensor';
import posed from 'react-pose';

const PoseDiv = posed.div({
  enter: {
    y: 0,
    opacity: 1,
    transition: {
      duration: 300,
      ease: 'easeIn',
    },
  },
  exit: { y: 20, opacity: 0 },
});

export const SlideUp = ({ children }) => {
  const [isVisible, setVisibility] = useState(false);
  const [entered, setEntered] = useState(false);

  const onChange = visiblity => {
    setVisibility(visiblity);
  };

  useEffect(() => {
    if (isVisible) {
      setEntered(true);
    }
  }, [isVisible]);

  return (
    <>
      <VisibilitySensor
        partialVisibility
        offset={{ top: 100 }}
        onChange={onChange}
      >
        <PoseDiv pose={entered ? 'enter' : 'exit'}>{children}</PoseDiv>
      </VisibilitySensor>
    </>
  );
}; 

I hope it helps somebody else. Cheers!

PS: this solution needs to be updated due to the fact that React Pose is now deprecated.

https://www.framer.com/api/motion/migrate-from-pose/

danihazler
  • 413
  • 1
  • 6
  • 13
0
import VisibilitySensor from 'react-visibility-sensor';

export const SlideUp = ({ children }) => {
  const [isVisible, setVisibility] = useState(false);

  const onChange = visiblity => {
    setVisibility(visiblity);
  };

  return (
    <VisibilitySensor
      partialVisibility
      offset={{ top: 200 }}
      onChange={onChange}
    >
      <div
        className={`example ${isVisible ? 'visible' : ''}`}
      >
        {children}
      </div>
    </VisibilitySensor>
  );
};

CSS

.example{
  ...
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.example.visible{
  opacity: 1;
  transform: translateY(0px);
}

Try to use className

kyun
  • 9,710
  • 9
  • 31
  • 66
0

I think you are toggling the visibility state, to keep the ui visible once displayed do not change the state to invisible

just change

const onChange = visiblity => {
   setVisibility(visiblity);
  };

to

const onChange = visiblity => {
if(visiblity){
 setVisibility(visiblity);
}
};
44kksharma
  • 2,740
  • 27
  • 32
0

This is the solution that I have been using. Just use hasBeenVisible render prop instead of isVisible.

import React, { useState } from "react";
import VisibilitySensor from "react-visibility-sensor";

/**
 * VisibilitySensor does not implement some kind of funcionality to track first time
 * visibility. This component extends VisibilitySensor compoment to provide this
 * feature. Just use `hasBeenVisible` render prop instead of `isVisible`.
 * 
 * https://github.com/joshwnj/react-visibility-sensor/issues/117#issuecomment-686365798
 */
const AppearSensor = ({
  onChange,
  children,
  ...rest
}) => {
  const [hasBeenVisible, setHasBeenVisible] = useState(false);

  return (
    <VisibilitySensor {...rest} onChange={(isVisible) => {
      if (isVisible) setHasBeenVisible(true)
      if (onChange) onChange(isVisible)
    }}>
      {
        ({
          isVisible,
          ...restRenderProps
        }) => {
          return children({ isVisible, ...restRenderProps, hasBeenVisible })
        }
      }
    </VisibilitySensor>
  );
};

AppearSensor.propTypes = VisibilitySensor.propTypes
AppearSensor.defaultProps = VisibilitySensor.defaultProps

export default AppearSensor;
André Abreu
  • 737
  • 7
  • 13
0

If you want the div to fade in one time then remain on the screen.

Change this

  const onChange = visiblity => {
    setVisibility(visiblity);
  };

To this

  const onChange = visiblity => {
    if(visiblity)
      setVisibility(true);
  };
Test1 Test2
  • 327
  • 2
  • 9