Giter Club home page Giter Club logo

react-scrollmagic's Introduction

react-scrollmagic

React components for ScrollMagic

NPM

⚠️ This library is not developed any further right now. Please consider to use GSAP ScrollTrigger instead. I have a helper component in my other library react-gsap: https://bitworking.github.io/react-gsap/src-components-scroll-trigger

Introduction

react-scrollmagic lets you use the ScrollMagic library in React in a fully declarative way. It abstracts away the direct use of the ScrollMagic classes ScrollMagic.Controller and ScrollMagic.Scene.

From version 2 on the GSAP library in no more included. But react-scrollmagic plays nicely together with react-gsap.

Install

npm install --save react-scrollmagic

Usage

import React from 'react';
import { Controller, Scene } from 'react-scrollmagic';

const App = () => (
  <div>
    <Controller>
      <Scene duration={600} pin>
        <div>Sticky Example</div>
      </Scene>
    </Controller>
  </div>
);

Examples live demo:

https://bitworking.github.io/react-scrollmagic/

Examples source:

https://github.com/bitworking/react-scrollmagic/tree/master/example/src/components/ScrollMagicExamples

Documentation

These React components use http://scrollmagic.io/ internally. So for an in-depth documentation please visits following sites:

http://scrollmagic.io/docs/ScrollMagic.Controller.html
http://scrollmagic.io/docs/ScrollMagic.Scene.html

There are two components available:

Controller

Props:

name type optional default more info
container string or object yes window
vertical boolean yes true
globalSceneOptions object yes {} link
loglevel number yes 2
refreshInterval number yes 100

Scene

The Scene component only consumes a single child. If you want to animate multiple children then you have to wrap them in a HTML element.

Scene sets the ref for the child component automatically. This only works for HTML tags, Styled Components or React.forwardRef components. If you use stateless or stateful components then you need to set the triggerElement or pin prop or wrap them in a HTML tag. See Components.js for an example.

The Scene component also works with a function as child. The function takes an animation progress (0-1) as first parameter and the event object as second parameter. See ClassToggle.js for an example.

From version 2 on it also works with a react-gsap Tween or Timeline component as direct child. See SectionWipes2.js for an example.

Props:

name type optional default more info
duration number or string yes 0 Can be changed on-the-fly
offset number or string yes 0 Can be changed on-the-fly
triggerElement string, object or null yes child element
triggerHook number or string yes "onCenter" link (Can be changed on-the-fly)
reverse boolean yes true Can be changed on-the-fly
loglevel number yes 2
indicators boolean yes false only boolean in contrast to plugin options: link
classToggle string or string[2] yes undefined link
pin boolean or string yes undefined link
pinSettings PinSettings yes undefined See Types and link
enabled boolean yes true Can be changed on-the-fly
progressEvents boolean yes true Ability to silence progress events reducing redraws

Types

PinSettings

name type optional default
pushFollowers boolean yes true
spacerClass string yes "scrollmagic-pin-spacer"

This project was bootstrapped with:

https://github.com/transitive-bullshit/create-react-library

License

MIT © bitworking

react-scrollmagic's People

Contributors

bitworking avatar leibowitz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-scrollmagic's Issues

BUG: Setting `indicators={true}` on scene causing error

Error message:

Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'firstChild')

Call Stack
Object._indicators.updateBoundsPositions
node_modules/react-scrollmagic/dist/index.es.js (3139:0)
eval
node_modules/react-scrollmagic/dist/index.es.js (3285:0)

Here is the code:

<div>
        <Controller>
          <Scene duration={"100%"} indicators={true}>
            <div className="h-screen bg-yellow-500">Sticky Example 2</div>
          </Scene>
          <Scene duration={"100%"} indicators={true}>
            <div className="h-screen bg-green-500">Sticky Example 1</div>
          </Scene>
        </Controller>
      </div>

dependencies:

  "dependencies": {
    "next": "12.1.6",
    "react": "18.1.0",
    "react-dom": "18.1.0",
    "react-scrollmagic": "^2.3.0"
  },
  "devDependencies": {
    "autoprefixer": "^10.4.7",
    "eslint": "8.16.0",
    "eslint-config-next": "12.1.6",
    "postcss": "^8.4.14",
    "tailwindcss": "^3.0.24"
  }

node version: v16.15.0
MacOS version: 12.4 (21F79) Intel

Screenshot of the error:
image

How to add class only once?

How can I instead of toggling a class, add a class only once ?

<Controller>
      <Scene duration={1000} classToggle="fade-in" triggerElement=".about__title">
          <div className="about__container">
            <Card title="Variedade"/>
            <Card title="Segurança"/>
            <Card title="Praticidade"/>
          </div>
      </Scene>
</Controller>

Best practice to split scenes into components

When I want to use React I also usually want to separate as much code to different components.
So if I want to split the different Scenes into different Components it does not work anymore because the Controller checks if the children have the type of Scene, but of course they have the type of the new component now. Based on this context, what is the best practice to separate scenes?

Any Example for onEnter trigger hook and pinning?

Hi team,

We have an animation where we want to start the animation when the Scene comes into the viewport (onEnter triggerHook). And once the whole scene has covered the viewport fully (i.e top is 0, we want to pin it with position fixed). Currently if we use onEnter triggerHook and pin the Scene, the child div of the Scene gets a top of the viewport height as soon as the animation starts, thus we are unable to see the animation.

If you guys have any examples related to this scenario, please attach the demo in this issue.

Thanks in advance,
Reetam

Pin two scenes to one element

Is it possible to pin two or more scenes to one element? An equivalent of this in the original API:
https://scrollmagic.io/examples/expert/cascading_pins.html

    new ScrollMagic.Scene({ triggerElement: "#trigger", duration: 150 })
      .setPin("#pin")
      .setClassToggle("#pin", "green")
      .on("enter leave", updateBox)
      .addTo(controller);

    new ScrollMagic.Scene({ triggerElement: "#trigger", duration: 150, offset: 300 })
      .setPin("#pin")
      .setClassToggle("#pin", "green")
      .on("enter leave", updateBox)
      .addTo(controller);

How to change component state on Scene event?

Hi

is it possible to assign an event listener to a Scene?

Access to events is described here
#2
and here #34
by wrapping Scene's child in a function. This does "give access" to events, but I need to change component state on event, and for that I need to assign a listener, not call a handler directly; because since the wrapper function is inside render(), I can't call state change from there (a React no-no).

How can I accomplish component state change on, say, enter or leave event?

Cascading Pins?

Any examples for cascading pins? I've been nesting Scenes, but it doesn't seem to work.

<Controller globalSceneOptions={{triggerHook : 'onLeave'}}>
<Scene
   duration={80}
   triggerElement='#example'
   pin="#example"
>
                <Scene
		  duration={80}
		  triggerElement="#example"
		  pin="#example"
		  offset={160}
		>
                  <div id="example"></div>
                </Scene>
</Scene>
</Controller>

Autoplay tween on trigger

Hi!

I have a question.
I am trying to trigger an react-gsap tween animation as autoplay like I have in this project:
https://hydrustent.com/

The problem is that I can only make it animate during a duration of a scene, and the ideal should be to trigger it on enter.

I've tried this with no luck:

<Controller>
  <Scene
    indicators
    triggerHook="onEnter"
    offset="100"
  >
    <Tween
      from={{ y: 100 }}
      to={{ y: 0 }}
      duration={1 /* for one sec */}
      ease="Power3.easeInOut"
    >
      <section {...props}>
        {/* content here */}
      </section>
    </Tween>
  </Scene>
</Controller>

With vanilla gsap and scrollmagic this should do the trick.
Am I missing something!?

Thanks.

Struggling while trying to achieve a certain scenario

Hi guys,
thanks for this awesome package!

I already made some nice animations, but now, working on a new project, I don't know how to approach it and I don't even know if ScrollMagic is capable of doing what I want.

This is what I want to achieve: : https://dashretail.eu/

When I scroll down, it should

  • trigger and play a defined animation
  • disable any further scrolling while an animation is playing
  • animated the reverse way when I scroll up

Hoping for any kind of support.

Thanks in advance!

React alternatives to this library

Since the project seems to be abandoned, do exists currently supported react alternatives? I'd like to have the "pin" option but need callbacks to the entering or leaving items

How to get currently visible component

Is there a way to get a reference to the current visible component? In my specific case, when scrolling, user can click an always visible button (it has a fixed position on the page) and user should be able to access the currently visible scene content

On component unmount, the controller is not destroyed.

The bug

When creating a new React application with Vite's React template, it comes with StrictMode enabled.

StrictMode, mounts the component, then unmounts it and then mounts it again. It does that to check for errors.

That created a problem, where I had one controller in my JSX structure but because of the mount-unmount-mount situation I ended it up with two. That caused a number of problems down the line.

How can I disable this Scene on mobile?

I'm kinda new to scrollmagic and react in general. I'm stuck on this problem. Any ideas how I can solve this?

I know that I can use a prop for disabling it, but how can I change that boolean on the fly based on the screen width?

The site is being made in Gatsby JS.

Help is greatly appreciated!

      <Controller>
      <Scene
        duration={500}
        pin={true}
        enabled={true}
        >
         {(progress) => (
      <Tween 
      from={{
        css: {
          transform: 'translate3d(0px, 0px, 0px) scale(0, 1)'
        },
        ease: 'Strong.easeOut',
      }}
      to={{
        css: {
          transform: 'translate3d(0px, 0px, 0px) scale(1, 1)'
      },
      ease: 'Strong.easeOut'
    }}     
      totalProgress={progress}
        paused
    >
        <div className="background-progress"></div>
        </Tween>
        )}
      </Scene>
      </Controller>

Example for nested controller and scenes

Hi again! I cant yet complete my homepage prototype: i'm struggling on understanding how the Controller and Scene work, especially in the case for multiple Controllers.

I'm trying something like, having this example/src/components/ScrollMagicExamples/SectionWipes.js as the main scrolling scenes, but then on any of those sub-panes, having a nested controller with its own animation during scrolling: in my case i want to have the panes as the sections for my homepage, and then one of those is "Who We Are" section, in which i want to make fade in sequence the 4 team members for my company, while the user is scrolling. After finishing it, then it goes to the next section (Pane) with other info.

@bitworking can you post some working example for reference please?

Animations does not work on react component

I'm trying to animate my <Logo/> react component but nothing happens to the component. I have found out that if I replace the <Logo/> with <div> Test <div/> everything works as expected. The problem seems to be with my react component.

Any help would be appreciated.

  <Controller>
      <Scene
          triggerElement={triggerElement1}
          triggerHook={triggerHook1}
          duration={animationDuration1}
      >
          {(progress: number) => (
              <Tween
                  to={{
                      css: {
                          left: '-10vw',
                          width: '40vw',
                          height: '2vh',
                          strokeWidth: '1em',
                      },
                  }}
                  totalProgress={progress}
                  paused
              >
                  <Logo className="logo" />
              </Tween>
          )}
      </Scene>
  </Controller>

The <Logo/> component

import React from 'react';

export default class Logo extends React.Component {
    constructor(props) {
        super(props);
        this.ref = React.createRef();
    }

    render() {
        return (
            <svg
                className={this.props.className}
                ref={this.ref}
                width="100"
                height="100"
            >
                <circle
                    cx="50"
                    cy="50"
                    r="40"
                    stroke="green"
                    stroke-width="4"
                    fill="yellow"
                />
                Sorry, your browser does not support inline SVG.
            </svg>
        );
    }
}

Add ability to silence progress events

In our application we do not need progress events, only enter and leave events.

It would be advantageous to performance if listening to progress events was optional. This could be done at the Scene component level with very minor and non default behaviour changing adjustments.

Adding a callback to a scene.

I am trying to have a callback fire when the Scene component is added or leaves. Is there a current way to do this using the component?

Multiple timelines inside a <Scene>

Hi!

Great library, I'm loving it so far.

Is there a way to get multiple timelines inside a scene? In the docs I've noticed it says "From version 2 on it also works with a react-gsap Tween or Timeline component as direct child." An das Scene only accepts one child - is there any way to get two timelines that I want to run concurrently triggering from the same scroll events?

If I wrap the two timelines in a

, the animation just gets triggered on page load, not bound to scrollmagic. Any ideas?

Scrolling to a scene

Does the library at present support being able to scroll to a particular scene by code. I can use a ref to the controller to call scrollTo but that jumps straight to the scene, I need the ability to animate/scroll/tween to it.

How to disable?

I am trying to disable all scroll effects for mobile usage, based on the window width.

Even though I set the enabled property on the to false the animations are still running.

Is this a bug or intended behavior?

Another thing I tried was accessing the Controller directly via refs and then call this.controllerRef.state.controller.enabled(false) but this also did not have an effect.

So what is the correct way on disabling scrollMagic?

How can I use the Scene Control Methods for scene manipulation

Hello,
I would need to change or set the duration of a Scene after the DOM has loaded, because I will use the height of a div, which has variable content.
I tried putting a ref on the scene, but I won't be able to manipulate the props directly.

The scroll magic api has something like scene.setClassToggle(), how would I use that with the Scene Component?

best wishes and thanks,
Hans

Compatibility with scrolling components

We have a landing page project which looks like a great fit for scrollmagic. The design has a navigation bar where the items change state as their respective element comes into view. Unfortunately though, trying to add this into the mix with scrollmagic is breaking the animation triggers. Is there a recommended approach for this using a prebuilt component such as https://github.com/fisshy/react-scroll

I know that the core scrollmagic library does support callbacks which would let me manually control using an on("enter") or on("leave") but I cannot see how to set that up through this abstraction.

Thanks

Use react context to access controller instance

I've noticed that Scene components need to be a direct children of a Controller component to work properly. I think it would make sense to use the react context API to give any Scene component access to the nearest controller. This way you could also simply wrap everything inside a Controller and use Scenes at any point inside the app.

I've tried to build a custom version of the Controller that makes use of context but currently there does not seem to be a way to create an instance of the ScrollMagic.Controller class that is used in this library.
Instead I used a workaround with a custom component that pretends to be a Scene and passes the controller prop on to the context:

type ScrollMagicContextType = {
    controller: any
}

const ScrollMagicContext = React.createContext<ScrollMagicContextType>(null);

export const ScrollMagicProvider: React.FC = ({ children }) => {

    return (
        <ReactScrollMagic.Controller>
            <ControllerGrabber>
                {controller => (
                    <ScrollMagicContext.Provider value={{ controller }}>
                        {children}
                    </ScrollMagicContext.Provider>
                )}
            </ControllerGrabber>
        </ReactScrollMagic.Controller>
    );
}

const ControllerGrabber = (props: { controller?: any, children: (ctr: any) => React.ReactNode }) => {
    return (
        <>
            {props.children(props.controller)}
        </>
    );
}

// Setting the display name to "Scene" causes the Controller to pass the controller instance as a prop
ControllerGrabber.displayName = "Scene";

Ideally the Scene component would simply consume the context. For now I use a simple hook and pass in the controller manually (you could also wirte a wrapper component for this):

export const useScrollController = () => {
    const scrollContext = useContext(ScrollMagicContext);
    return scrollContext.controller;
}

const SomeComponent = () => {
  const controller = useScrollController();

  return (
    <ReactScrollMagic.Scene
      {...{ controller }}
    >
        ...
    </ReactScrollMagic.Scene>
  );
}

Maybe this would be something to consider for a future release? Or is there a reason why context is not used? Also, like already mentioned in #22, I think Controller should pass the controller instance to every child and not just to Scene to avoid the displayName hack.

Error about triggerElement not defined, even when is not needed

I'm using the library, great by the way!

Using like this, just works, but i get an annoying error on the console.

  <Controller>
    <Scene duration={2000} offset={150}>
      {progress => {
        return (
          <Tween
            from={{ opacity: 1 }}
            to={{ opacity: 0, scale: 0.1 }}
            paused
            totalProgress={progress}>
            <div>
              <Icon name="down arrow" size="huge" />
            </div>
          </Tween>
        );
      }}
    </Scene>
  </Controller>

captura de pantalla 2018-12-20 a la s 13 10 53

To avoid the error, i've added triggerElement={null} on the <Scene>, but again, it is not needed cause i'm using the offset prop

Duration does not update on State change unless the page is refreshed

I have a event listening for when the browser resizes to change the state of the app.

        window.addEventListener("resize", _.debounce(() => {
			if(window.innerWidth > 1920) {
				let scale_height = window.innerWidth * 0.565;
				this.setState({scale_height: scale_height});
			} else {
				this.setState({scale_height: "100vh"});
			}
        },500));

This updates the height for elements in the page and in turn also updates the duration.

The duration updates on the new state but doesn't refreshes unless you actually refresh the page.

From what I understand, I may need to update the duration from the event listener.

Related issue: janpaepke/ScrollMagic#105

Is it possible to update the duration on a new state change?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.