Scrolling Animation in GSAP: Image Scaling and Position change

Scrolling animation in GSAP : GSAP is an industry standard JavaScript animation library from GreenSock that lets you craft high-performance animations that work in every major browser.

Please Like and subscribe for more GSAP animation

Youtube Video Link

Preview of animation

Lets, Create a ReactJS project and install gsap :
  • You can create a new ReactJS project using Create React App or any other preferred method. If you haven’t installed Create React App globally, you can do so with the following command: npm install -g create-react-app
  • Then create a new React project:  npx create-react-app my-gsap-project
  • go to project folder :  cd my-gsap-project
  • Next, you need to install GSAP package. You can do this via npm or yarn: npm install gsap or yarn add gsap

 

Here are some basic steps to follow when creating GSAP animation in Reactjs:
  1. Register the scrollTrigger plugin to trigger elements on scroll.

  2. Create React Hooks and write GSAP code inside them.

  3. Utilize gsap.context() to collect all GSAP animations and ScrollTriggers created within the supplied function, allowing for easy reverting or killing of ALL of them at once.

  4. Develop timelines and scrollTriggers based on your specific requirements.

				
					useEffect(() => {

		gsap.registerEffect({
			name: "zoom",
			effect: (targets, config) => {
				console.log("tagets", targets);
				console.log("config", config);
				const vars = { transformOrigin: "0px 0px", ...config },
					{ scale, origin } = config,
					//  clamp(minimum, maximum, valueToClamp)
					// pass 0 ,100 , 20 - it's return 0

					clamp = gsap.utils.clamp(-100 * (scale - 1), 0);
				console.log("config", scale, origin);

				console.log("clamp", clamp(-100 * (scale - 1), 0));
				delete vars.origin;
				vars.xPercent = clamp((0.5 - origin[0] * scale) * 100);
				console.log("clamp2", clamp((0.5 - origin[0] * scale) * 100));

				vars.yPercent = clamp((0.5 - origin[1] * scale) * 100);
				vars.overwrite = "auto";
				return gsap.to(targets, vars);
			},
			extendTimeline: true,
			defaults: { origin: [0.5, 0.5], scale: 2 }
		});


		const zoomData = [
			{ scale: 1, origin: [0.5, 0.5] },
			{ scale: 1.5, origin: [0.5, 0.5] },
			{ scale: 2, origin: [0.8, 0.5] },
			{ scale: 1.5, origin: [0.2, 0.5] }
		];

		gsap.utils.toArray("section").forEach((section, index) => {
			const zoom = zoomData[index];
			ScrollTrigger.create({
				trigger: section,
				start: "top 45%",
				end: "+=125%",
				onToggle(self) {
					if (self.isActive) {
						gsap.effects.zoom(".photo", {
							scale: zoom.scale,
							origin: zoom.origin,
							duration: 1,
							ease: "power1.inOut"
						});
					}
				}
			});
		});
	}, [])
				
			

Here, we loop through each section and trigger a zoom effect for each one. When triggering each section, we pass targets and configurations to apply the zoom effects. We create a zoomData object where we set the scale and origin of each section. This way, when triggering each section, the scaling and origin positions are applied to the respective sections.

Check HTML / JSX code below

				
					<Box sx={{
			px: 10,
			"& .right-side": {
				width: "40vw",
				position: "fixed",
				right: 80,
				top: "50%",
				transform: "translateY(-50%)",
				height: "40vw",
				overflow: "hidden",
			},
			"& section": {
				position: "relative",
				height: "100vh",
				width: "calc(60vw - 100px)",
				display: "flex",
				flexDirection: "column",
				justifyContent: "center",
				"& .EachSectionInfo": {
					maxWidth: 700,
				},
				"& h1": {
					fontSize: 40,
					letterSpacing: 1,
					textTransform: "uppercase",
				},
				"& p": {
					fontWeight: 500,
					marginTop: "2rem",
					letterSpacing: 0.5,
					fontSize: 24,
					lineHeight: 1.6,
				}
			},
			"& .photo": {
				width: "100%",
				height: "100%",
				backgroundImage: `url(${bgImg})`,
				backgroundSize: "contain",
				backgroundRepeat: "no-repeat",
				backgroundPosition: "center",
			}
		}}>
			<Box className="right-side">
				<Box className="photo"></Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>Immersive music</h1>
					<p>
						Experie­nce immersive music with de­ep bass using these he­adphones. With powerful 40 mm sound drivers, the­y provide exceptional bass and pre­cise high frequencie­s, allowing you to hear every de­tail of your favorite songs. The audio expe­rience is truly dramatic and captivating.
					</p>
				</Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>Travel-Friendly Design</h1>
					<p>These he­adphones are designe­d to be portable and travel-frie­ndly. With their foldable design, the­y can easily be stored and carrie­d with you wherever you go, whe­ther it's a trip to the gym, a vacation, or just lounging at home.</p>
				</Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>HIGH QUALITY SOUND</h1>
					<p>
						Enjoy a seamless transition between wire­d and wireless modes with the­se headphones, enhancing your listening experie­nce. You can effortlessly switch be­tween the two modes, connecting to your devices in whiche­ver way
						suits you best. Bene­fit from the flexibility of wirele­ss communication combined with the reliability of corded audio.
					</p>
				</Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>EXCEPTIONAL COMFORT</h1>
					<p>These headphones are designed with your comfort in mind, featuring velvety and super-soft cushions that cradle your ears. Enjoy long listening sessions without discomfort or weariness, and listen to your favourite music,
						podcasts, and phone conversations in total comfort.</p>
				</Box>
			</Box>

		</Box >
				
			

Check below for Full source code

				
					import { Box } from "@mui/material"
import { useEffect } from "react";
import bgImg from "../assets/h55.png";
import gsap from "gsap";
import ScrollTrigger from "gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

const ProductInfoWithZoom = () => {
	useEffect(() => {

		gsap.registerEffect({
			name: "zoom",
			effect: (targets, config) => {
				console.log("tagets", targets);
				console.log("config", config);
				const vars = { transformOrigin: "0px 0px", ...config },
					{ scale, origin } = config,
					//  clamp(minimum, maximum, valueToClamp)
					// pass 0 ,100 , 20 - it's return 0

					clamp = gsap.utils.clamp(-100 * (scale - 1), 0);
				console.log("config", scale, origin);

				console.log("clamp", clamp(-100 * (scale - 1), 0));
				delete vars.origin;
				vars.xPercent = clamp((0.5 - origin[0] * scale) * 100);
				console.log("clamp2", clamp((0.5 - origin[0] * scale) * 100));

				vars.yPercent = clamp((0.5 - origin[1] * scale) * 100);
				vars.overwrite = "auto";
				return gsap.to(targets, vars);
			},
			extendTimeline: true,
			defaults: { origin: [0.5, 0.5], scale: 2 }
		});


		const zoomData = [
			{ scale: 1, origin: [0.5, 0.5] },
			{ scale: 1.5, origin: [0.5, 0.5] },
			{ scale: 2, origin: [0.8, 0.5] },
			{ scale: 1.5, origin: [0.2, 0.5] }
		];

		gsap.utils.toArray("section").forEach((section, index) => {
			const zoom = zoomData[index];
			ScrollTrigger.create({
				trigger: section,
				start: "top 45%",
				end: "+=125%",
				onToggle(self) {
					if (self.isActive) {
						gsap.effects.zoom(".photo", {
							scale: zoom.scale,
							origin: zoom.origin,
							duration: 1,
							ease: "power1.inOut"
						});
					}
				}
			});
		});
	}, [])
	return (
		<Box sx={{
			px: 10,
			"& .right-side": {
				width: "40vw",
				position: "fixed",
				right: 80,
				top: "50%",
				transform: "translateY(-50%)",
				height: "40vw",
				overflow: "hidden",
			},
			"& section": {
				position: "relative",
				height: "100vh",
				width: "calc(60vw - 100px)",
				display: "flex",
				flexDirection: "column",
				justifyContent: "center",
				"& .EachSectionInfo": {
					maxWidth: 700,
				},
				"& h1": {
					fontSize: 40,
					letterSpacing: 1,
					textTransform: "uppercase",
				},
				"& p": {
					fontWeight: 500,
					marginTop: "2rem",
					letterSpacing: 0.5,
					fontSize: 24,
					lineHeight: 1.6,
				}
			},
			"& .photo": {
				width: "100%",
				height: "100%",
				backgroundImage: `url(${bgImg})`,
				backgroundSize: "contain",
				backgroundRepeat: "no-repeat",
				backgroundPosition: "center",
			}
		}}>
			<Box className="right-side">
				<Box className="photo"></Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>Immersive music</h1>
					<p>
						Experie­nce immersive music with de­ep bass using these he­adphones. With powerful 40 mm sound drivers, the­y provide exceptional bass and pre­cise high frequencie­s, allowing you to hear every de­tail of your favorite songs. The audio expe­rience is truly dramatic and captivating.
					</p>
				</Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>Travel-Friendly Design</h1>
					<p>These he­adphones are designe­d to be portable and travel-frie­ndly. With their foldable design, the­y can easily be stored and carrie­d with you wherever you go, whe­ther it's a trip to the gym, a vacation, or just lounging at home.</p>
				</Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>HIGH QUALITY SOUND</h1>
					<p>
						Enjoy a seamless transition between wire­d and wireless modes with the­se headphones, enhancing your listening experie­nce. You can effortlessly switch be­tween the two modes, connecting to your devices in whiche­ver way
						suits you best. Bene­fit from the flexibility of wirele­ss communication combined with the reliability of corded audio.
					</p>
				</Box>
			</Box>
			<Box component="section">
				<Box className="EachSectionInfo">
					<h1>EXCEPTIONAL COMFORT</h1>
					<p>These headphones are designed with your comfort in mind, featuring velvety and super-soft cushions that cradle your ears. Enjoy long listening sessions without discomfort or weariness, and listen to your favourite music,
						podcasts, and phone conversations in total comfort.</p>
				</Box>
			</Box>

		</Box >
	)
}

export default ProductInfoWithZoom;
				
			
Scroll to Top