use gsap scrollTrigger in react - javascript

I am trying to imply some animations in an image while scrolling , the image will grow while scrolling down and return to normal when scrolling up . The reference : codepen.
This is my react code :
<ArticleWrapper ref={ref}>
<img id='grow' className='image' src={Img} alt='Image' />
</ArticleWrapper>
I have tried to implement this but it didnt work when I scroll :
const ref = useRef(null);
useEffect(() => {
const element = ref.current;
scrollTrigger: {
trigger: "#grow",
scrub: 1.5,
start: "top center",
end: "+=400",
ease: "power1.out"
},
{
duration: 1,
scale: 1
}
);
}, []);
Any idea how to do that ?

Related

Passing div style as a function argument Javascript/ React

I'm building an application using React, framer-motion and react-intersection-observer.
Inside animation.js I have a function which is imported inside App.js and used as a component in App.js.
I want to apply an aspect ratio to some divs using style as a parameter, but it doesn't work.
<FadeAnimation name={'project-image--image'} style={'--ratio':16/9} />
Failed to set an indexed property on 'CSSStyleDeclaration': Indexed property setter is not supported.
I have other divs with this property and they are displayed correctly
<div className='project-image--image' style={{'--ratio':1/2}}/>
Animation.js
export const container = {
hidden: { opacity: 0, y: 5 },
visible: { opacity: 1, y: 0 }
}
function FadeAnimation({ children, name, delay, duration, style }) {
const controls = useAnimation();
const [ref, inView] = useInView();
useEffect(() => {
if (inView) {
controls.start("visible");
}
}, [controls, inView]);
return (
<motion.div className={`${name}`} style={{`${style}`}}
ref={ref}
animate={controls}
initial="hidden"
transition={{ duration: duration, delay: delay }}
variants={{
visible: { opacity: 1, y: 0 },
hidden: { opacity: 0, y: 5 }
}}
>
{children}
</motion.div>
);
}
Have you tried:
<FadeAnimation name={'project-image--image'} style={{'--ratio':16/9}} />
(Adding another curly brace)
And then, in the FadeAnimationComponent using it as
<motion.div className={`${name}`} style={style} {/*...*/}/>

The scroll trigger end of gsap does not work properly

Hi I use gsap and bootstrap
Each version is gsap 3.9.1 and bootstrap 5.1.3
My problem is that the end of the scroll trigger does not work properly.
const navbarani = gsap.from('.navbar', {
yPercent: -100,
paused: true,
duration: 0.5
}).progress(1)
ScrollTrigger.create({
start: 'top top',
end: 'bottom',
onUpdate: (self) => {
self.direction === -1 ? navbarani.play() : navbarani.reverse()
}
})
If I change end: 'bottom' to end: 999999 it works properly, but
I can't use end: 999999 because I want the scroll to work only when it's at the top.
What I don't understand is that if i erase the bootstrap from the cdn, end: 'bottom' works well.
That's why I think bootstrap and gsap scroll trigger's end cause conflict.
This is the code that I experimented with.
<template>
<div class="navbar">I'm navbar</div>
<div class="blank" />
</template>
<script>
import { onMounted } from 'vue'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
export default {
setup () {
onMounted(() => {
const navbarani = gsap.from('.navbar', {
yPercent: -100,
paused: true,
duration: 0.5
}).progress(1)
ScrollTrigger.create({
start: 'top top',
end: 'bottom',
onUpdate: (self) => {
self.direction === -1 ? navbarani.play() : navbarani.reverse()
}
})
})
}
}
</script>
<style>
.navbar {
position: fixed;
}
.blank {
height: 150vh;
}
</style>
I wonder if this collision only occurs to me or if the version is wrong.
If you don't have enough explanation, please let me know. I'll add an explanation.
Thank you for your help.

How to turn on and off the scroll trigger in Material UI useScrollTrigger module?

I'm trying to create a survey page using Material UI in React. I'd like my survey to turn on when the user scrolls over a question and turns off when the user scrolls out of a question. A behavior similar to this page.
https://www.surveymonkey.com/r/Typical-Customer-Demographics-Template
After doing some research, I decided to use useScrollTrigger module in MUI. I was able to turn on the questions when I scroll down to them, but still can't figure out a way to turn them back off when I scroll down, out of them.
This is how I created my scrollToColor function:
import { useScrollTrigger } from "#material-ui/core";
export default function ScrollToColor(props) {
const {
threshold,
threshold2,
textColorBefore,
textColorAfter,
fadeIn,
fadeOut,
children,
...other
} = {
threshold: 50,
threshold2: 100,
textColorBefore: "gray",
textColorAfter: "black",
fadeIn: "0.1s ease-in",
fadeOut: "0.1s ease-out",
...props,
};
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: threshold,
target: props.window ? window() : undefined,
});
const trigger2 = useScrollTrigger({
disableHysteresis: true,
threshold: threshold2,
target: props.window ? window() : undefined,
});
return React.cloneElement(children, {
style: {
color: trigger ? textColorAfter : textColorBefore,
transition: trigger ? fadeIn : fadeOut,
color: trigger2 ? textColorBefore : textColorAfter,
transition: trigger2 ? fadeOut : fadeIn,
},
...other,
});
}
I created 2 triggers so when it scrolls to the question, the text color turns black and when scroll out, it turns back to gray. This is a sample code on how I added this to the parent component.
import {
AppBar,
Toolbar,
Typography,
ThemeProvider,
CssBaseline,
createMuiTheme
} from "#material-ui/core";
import ScrollToColor from "./ScrollToColor";
const NavbarscrollToColor = props => {
const theme = createMuiTheme();
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<ScrollToColor>
<AppBar position="static">
<Toolbar>
<Typography variant="h5" noWrap>
{props.title}
</Typography>
</Toolbar>
</AppBar>
</ScrollToColor>
</ThemeProvider>
);
};
export default NavbarscrollToColor;
But this unfortunately doesn't work. Any tips on how to make this work?
Also, is there any easier and cleaner way in React I could have archived this other than using the MUI useScrollTrigger module?
Thanks!
After spending more time on this issue, here is the solution that I came up with that works:
import { useScrollTrigger } from "#material-ui/core";
export default function ScrollToColor(props) {
const {
threshold,
threshold2,
textColorBefore,
textColorAfter,
fadeIn,
fadeOut,
children,
...other
} = {
threshold: 0,
threshold2: 100,
textColorBefore: "gray",
textColorAfter: "black",
fadeIn: "0.3s ease-in",
fadeOut: "0.3s ease-out",
...props,
};
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: threshold,
target: props.window ? window() : undefined,
});
const trigger2 = useScrollTrigger({
disableHysteresis: true,
threshold: threshold2,
target: props.window ? window() : undefined,
});
return React.cloneElement(children, {
style: {
color: trigger && !trigger2 ? textColorAfter : textColorBefore,
transition: trigger && !trigger2 ? fadeIn : fadeOut,
},
...other,
});
}

How to move plane icon with framer motion?

I have a react paper plane icon that I would like to make a cool effect by making it travel around the HTML document reaching the final position which is the menu button at the top.
This is how my react component called NavPlane looks like:
import React, { Component, useEffect } from "react";
import { useCycle } from "framer-motion";
import { FaPaperPlane } from "react-icons/fa";
import { motion } from "framer-motion";
const PlaneVariants = {
animationOne: {
x: 370,
y: 250,
transition: { ease: [0.17, 0.67, 0.83, 0.67] },
},
animationTwo: {
x: 140,
y: 150,
transition: { duration: 1.0 },
},
animationThree: {
x: 170,
y: 200,
transition: { duration: 1.0 },
},
};
export default function NavPlane() {
const [animation, cycleAnimation] = useCycle(
"animationOne",
"animationTwo",
"animationThree"
);
useEffect(() => {
setTimeout(() => {
cycleAnimation();
}, 1000);
}, []);
return (
<motion.div
className="text-2xl text-gray-600"
variants={PlaneVariants}
animate={animation}
>
<FaPaperPlane />
</motion.div>
);
}
cycleAnimation() was supposed to cycle through 3 animations but only cycling through the first two. The 3rd is only applied when making some change to the code and updating it.
The final goal is to have a movement that goes from right corner of the page to middle, does a knot movement and then travels to the top of the page.
Any ideas how to make it cycle through as many animation variants as I want?
cycleAnimation only advances one step. Initially it will start "animationOne". Once you call cycleAnimation() after 1 second, "animationTwo" will start. You need another timer if you want to play "animationThree"
useEffect(() => {
setTimeout(cycleAnimation, 1000); // start "animationTwo" after 1 second
setTimeout(cycleAnimation, 2000); // start "animationThree" after 2 seconds
}, []);

Return back to first slide when carousel reaches last

I am using https://www.npmjs.com/package/react-multi-carousel in react js project.
The carousel is working as expected but I am in the need to make the carousel to start from first when it reaches the last slide.
Complete working example: https://codesandbox.io/s/react-multi-carousel-playground-2c6ye
Code:
<Carousel
ssr
deviceType={deviceType}
itemClass="image-item"
responsive={responsive}
>
I have added like this,
<Carousel
infinite={true}
autoPlay={true}
autoPlaySpeed={3000}
ssr
deviceType={deviceType}
itemClass="image-item"
responsive={responsive}
>
But it automatically creates infinite number of slides but that is not my requirement.. Once it reaches the end then it should get back to first slide after 1 second duration because user needs to move backward n number of times to reach the first slide.
Kindly help me to start from beginning slide once the carousel once it reaches last slide(With some delay like 1000ms so that user can see the last slide for 1s and can view the first after that..
You can achieve this by writing your own autoloop and by using custom buttons. Honnestly, maybe you should just pick another library that does what you want. But you educationnal purpose, I did an example of what you should have done. Please note that you need to add the CSS for the new button group.
import React, { useEffect, useRef } from "react";
import { render } from "react-dom";
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
const responsive = {
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 1,
paritialVisibilityGutter: 60
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 1,
paritialVisibilityGutter: 50
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1,
paritialVisibilityGutter: 30
}
};
const images = [
"https://images.unsplash.com/photo-1549989476-69a92fa57c36?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60",
"https://images.unsplash.com/photo-1549396535-c11d5c55b9df?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60",
"https://images.unsplash.com/photo-1550133730-695473e544be?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=800&q=60"
];
/* ADD THIS LINE */
// Your custom Button group. CSS need to be added
const ButtonGroup = ({ next, previous, goToSlide, ...rest }) => {
const {
carouselState: { currentSlide }
} = rest;
const lastImageIndex = images.length - 1;
return (
<div className="carousel-button-group" style={{ position: "absolute" }}>
<button
onClick={() =>
currentSlide === 0 ? goToSlide(lastImageIndex) : previous()
}
>
Prev
</button>
<button
onClick={() =>
currentSlide === lastImageIndex ? goToSlide(0) : next()
}
>
Next
</button>
</div>
);
};
/* TO THIS LINE */
const Simple = ({ deviceType }) => {
/* ADD THIS LINE */
const carousel = useRef(null);
const lastImageIndex = images.length - 1;
useEffect(() => {
const autoloop = setInterval(() => {
if (carousel.state.currentSlide === lastImageIndex) {
carousel.goToSlide(0);
} else {
carousel.next();
}
}, 3000); // Your custom auto loop delay in ms
return () => clearInterval(autoloop);
}, []);
/* TO THIS LINE */
return (
<Carousel
ssr
deviceType={deviceType}
itemClass="image-item"
responsive={responsive}
/* ADD THIS LINE */
ref={el => (carousel = el)}
arrows={false}
customButtonGroup={<ButtonGroup />}
/* TO THIS LINE */
>
{images.slice(0, 5).map((image, index) => {
return (
<div key={index} style={{ position: "relative" }}>
<img
draggable={false}
alt="text"
style={{ width: "100%", height: "100%" }}
src={image}
/>
<p
style={{
position: "absolute",
left: "50%",
bottom: 0,
color: "white",
transform: " translateX(-50%)"
}}
>
Legend:{index}.
</p>
</div>
);
})}
</Carousel>
);
};
render(<Simple />, document.getElementById("root"));
Hope it helps. Happy coding :)
I believe the best option now is to use this prop:
infiniteLoop: true
Reference: https://github.com/leandrowd/react-responsive-carousel/issues/232
The simplest solution to this problem is to add infiniteloop props as true.
<Carousel infiniteLoop={true} autoPlay={true} interval={1000}>
<div>
<img src={slider1} />
<p className='legend'>Legend 1</p>
</div>
<div>
<img src={slider2} />
<p className='legend'>Legend 2</p>
</div>
</Carousel>

Categories

Resources