Recat.js Vanilla JavaScript Fade in animation effect - javascript

I want to make a fade-in animation effect using javaScript scroll event. What should I do?
The current style is working on style components. Please advise
const handleAnimation = ( ) => {
const windowHeight = window.innerHeight;
const scrollY = window.scrollY;
const bannerTop = ref.current && ref.current.getBoundingClientRect().top;
const bannerAbsolute = scrollY + bannerTop;
return { windowHeight: windowHeight, scrollY: scrollY };
}
useEffect(() => {
window.addEventListener('scroll', handleAnimationScroll);
return () => {
window.removeEventListener('scroll', handleAnimationScroll);
};
}, []);
handleAnimationScroll();

Related

Make navbar link active on wheel event rather than scroll?

I want to make the links in my navbar have an active class when you scroll into the corresponding section.
The code below was working just fine until I implemented a smooth scroll/parallax library which removes the scroll event.
I tried making the code work using the wheel event, tried seeing if there was anything similar to scrollY for wheel events but I couldn't find anything.
Any ideas on how I could implement this feature? It can be different from the implementation I had before
EDIT: Here's a codepen. If you uncomment the locomotive portion, the code no longer works. How can I make it work?
const sections = document.querySelectorAll("section");
const navLinks = document.querySelectorAll("nav a");
window.addEventListener("scroll", function () {
let navbar = document.querySelector("nav");
let current = "";
sections.forEach(function (section) {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (scrollY >= sectionTop - sectionHeight / 3) {
current = `#${section.getAttribute("id")}`;
}
navLinks.forEach(function (each) {
// add/remove active class
each.classList.remove("nav-active");
if (each.getAttribute("href") == current) {
each.classList.add("nav-active");
}
});
});
});
Try using an IntersectionObserver instead:
const navLinks = document.querySelectorAll("nav a");
const updateNav = (entries, observer) => {
const matchingIds = entries.filter(e => e.isIntersecting).map(e => `#${e.target.id}`);
if (matchingIds.length !== 0) {
const current = matchingIds[0];
navLinks.forEach(function(link) {
link.classList.remove("nav-active");
if (link.getAttribute("href") == current) {
link.classList.add("nav-active");
}
});
}
};
const options = {
root: null,
rootMargin: "0px",
threshold: 0.66
}
const observer = new IntersectionObserver(updateNav, options);
document.querySelectorAll("*[data-scroll-section]").forEach(el => observer.observe(el));
Intersection Observer API
Demo

Having conditional rendering on an id of div tag

Currently I'm trying to move my image from center to left and left to right as the user scrolls down. For achieving this, I'm using useEffect to manipulate my DOM events. I want the layout in such a way that after the user has scrolled 600 pixels in height the image starts moving to the right. For this I tried conditionally rendering a div tag but I get an error in my useEffect since it doesnt recognize the other element. So how can I move my image when it reaches a certain height?
CodeSandbox: https://codesandbox.io/s/misty-sun-e6odq?file=/src/App.js
Code:
const [display, setDisplay] = useState(false);
useEffect(function onFirstMount() {
const changeBackground = () => {
let value = window.scrollY;
console.log(value);
let img = document.getElementById("moveLeft");
let img2 = document.getElementById("moveRight");
img.style.transform = `translateX(-${value * 0.5}px)`;
img2.style.transform = `translateX(${value * 0.5}px)`;
if (value > 600) {
setDisplay(true);
} else {
setDisplay(false);
}
};
window.addEventListener("scroll", changeBackground);
return () => window.removeEventListener("scroll", changeBackground);
}, []);
return (
<>
<div className="App">
<div class="inflow">
<div class="positioner">
<div class="fixed">
<div id={display?"moveRight":"moveLeft"}>
<img
alt="passport"
src="https://cdn.britannica.com/87/122087-050-1C269E8D/Cover-passport.jpg"
/>
</div>
</div>
</div>
</div>
</div>
<div className="App2">
</div>
<div className="App2"></div>
</>
As you have two calls to getElementById and you dynamically change the id based on state, you will always have an undefined element.
You could fix it like so
let img =
document.getElementById("moveLeft") ||
document.getElementById("moveRight");
const val = img.id === "moveLeft" ? -(value * 0.5) : value * 0.5;
img.style.transform = `translateX(${val}px)`;
And remove the img2 call, as there is always one of the above in the document. Example of this here: https://codesandbox.io/s/stoic-babycat-6bl5i?file=/src/App.js
You could also try to achieve it with only one image:
useEffect(
function onFirstMount() {
const changeBackground = () => {
if (!imgRef || !imgRef.current) return;
const value = window.scrollY;
if (value > 600) {
imgRef.current.style.display = `block`;
} else {
imgRef.current.style.display = `none`;
return;
}
const progress = (window.innerWidth / 100) * moveRatio;
if (
animData.current.isGoingRight &&
animData.current.currentPos >= window.innerWidth - 200
) {
animData.current.isGoingRight = false;
} else if (
!animData.current.isGoingRight &&
animData.current.currentPos <= startPosition
) {
animData.current.isGoingRight = true;
}
if (animData.current.isGoingRight)
animData.current.currentPos += progress;
else animData.current.currentPos -= progress;
imgRef.current.style.transform = `translateX(${animData.current.currentPos}px)`;
};
window.addEventListener("scroll", changeBackground);
return () => window.removeEventListener("scroll", changeBackground);
},
[imgRef]
);
This one moves in relation to the window inner width, you can see it here https://codesandbox.io/s/hidden-microservice-xb2ku?file=/src/App.js, that's just an example, I'm sure there are another approaches

Why can I not call my function inside the render ()?

I am very new using React and I have the following problem; I have defined a function out of the return (), and I am calling it afterwards so it shows only when the page is loaded.
class Landing extends Component {
render() {
scrollbar = () => {
const progress = document.getElementById("progressBar");
const totalHeight = document.body.scrollHeight - window.innerHeight;
window.onscroll = function () {
const progressHeight = (window.pageYOffset / totalHeight) * 100;
progress.style.height = progressHeight + "%";
};
};
return (
<div>
<div id="progressBar"> {this.scrollbar()}</div>
<div id="scrollPath"></div>
</div>
)
}
}
The error that I get is:
"scrollbar is undefined" // Search for the keywords to learn more about each error.
Any help is much appreciated.
Thank you!
You must define the method (scrollbar) above the render method.
class Landing extends Component {
scrollbar = () => {
const progress = document.getElementById("progressBar");
const totalHeight = document.body.scrollHeight - window.innerHeight;
window.onscroll = function () {
const progressHeight = (window.pageYOffset / totalHeight) * 100;
progress.style.height = progressHeight + "%";
};
};
render() {
return (
<>
<div id="progressBar">{this.scrollbar}</div>
<div id="scrollPath"></div>
</>
)
}
}
I hope this solve your issue. :)
First, you shouldn't be doing DOM manipulation like that when using react, so ideally you would rewrite the scrollbar function to be more inline with react.
The error you are getting is because this is referring to the class, meaning this.scrollbar is looking for a class property called scrollbar. You do not have a class property called scrollbar, but a function declared within the render method.
You have two options to solve this error.
Reference the function instead of a class property:
<div id="progressBar">{scrollbar()}</div>
Change the function to be a class property:
class Landing extends Component {
scrollbar = () => {
const progress = document.getElementById("progressBar");
const totalHeight = document.body.scrollHeight - window.innerHeight;
window.onscroll = function () {
const progressHeight = (window.pageYOffset / totalHeight) * 100;
progress.style.height = progressHeight + "%";
};
};
Once you do one of these, you still might see some unexpected behavior. By doing:
<div>{this.scrollbar()}</div>
You are rendering the result of the function call. The function has no return value though, so it will render undefined. Since it really doesn't need to be rendered, you can just call in inside the render function directly:
render() {
this.scrollbar();
return (
<div>
<div id="progressBar"></div>
<div id="scrollPath"></div>
</div>
)
}
1) Move your scrollbar function outside render.
2) remove () these braces while calling scrollbar inside render function.
scrollbar = () => {
const progress = document.getElementById("progressBar");
const totalHeight = document.body.scrollHeight - window.innerHeight;
window.onscroll = function () {
const progressHeight = (window.pageYOffset / totalHeight) * 100;
progress.style.height = progressHeight + "%";
};
};
render() {
return (
<div>
<div id="progressBar"> {this.scrollbar}</div>
<div id="scrollPath"></div>

Horizontal Scrolling on React Component Using Vertical Mouse Wheel

I have a component that resizes into a horizontal row of bootstrap cards when in a smaller desktop window. For users without a horizontal mouse wheel and not using a touchpad, I would like to allow users to scroll horizontally using their vertical mouse wheel movements when hovering over this particular component.
Here is the original StackOverflow issue I based my code off of:
https://stackoverflow.com/a/15343916/8387497
Horizontal Scroll helper component:
function horizontalScroll (event) {
const delta = Math.max(-1, Math.min(1, (event.nativeEvent.wheelDelta || -event.nativeEvent.detail)))
event.currentTarget.scrollLeft -= (delta * 10)
event.preventDefault
}
How I've implemented it on component requiring horizontal scrolling:
<Row className='announcements-home' onWheel={horizontalScroll} >
When I've placed this horizontalScroll helper function within the React onWheel event, it scrolls horizontally AND vertically. My desired outcome is just horizontal scrolling. Also, Firefox does not appear to respond at all to horizontal scrolling with these changes.
Okay, so the issue seems to be that you only refer to the function event.preventDefault rather than invoking it.
Adding some brackets at the end to invoke it should do the trick:
event.preventDefault().
I however found this issue while looking for some simple code to use, so I will also leave the hook I made for this if others in the same situation:
import { useRef, useEffect } from "react";
export function useHorizontalScroll() {
const elRef = useRef();
useEffect(() => {
const el = elRef.current;
if (el) {
const onWheel = e => {
if (e.deltaY == 0) return;
e.preventDefault();
el.scrollTo({
left: el.scrollLeft + e.deltaY,
behavior: "smooth"
});
};
el.addEventListener("wheel", onWheel);
return () => el.removeEventListener("wheel", onWheel);
}
}, []);
return elRef;
}
Usage:
import React from "react";
import { useSideScroll } from "./useSideScroll";
export const SideScrollTest = () => {
const scrollRef = useHorizontalScroll();
return (
<div ref={scrollRef} style={{ width: 300, overflow: "auto" }}>
<div style={{ whiteSpace: "nowrap" }}>
I will definitely overflow due to the small width of my parent container
</div>
</div>
);
};
Note:
The scroll behavior "smooth" seems to be giving some trouble when trying to do continuous scrolling. This behavior can be omitted to have proper continuous scrolling, but it will look jerky.
As far as I know, there is no easy solution for this. I have however created a rather involved solution in my own project, so thought some people may appreciate that also: https://gist.github.com/TarVK/4cc89772e606e57f268d479605d7aded
onWheel = (e) => {
e.preventDefault()
var container = document.getElementById('container')
var containerScrollPosition = document.getElementById('container').scrollLeft
container.scrollTo({
top: 0,
left: largeContainerScrollPosition + e.deltaY
behaviour: 'smooth' //if you want smooth scrolling
})
}
There is another small problem with TarVK's proposed hook. Once you scroll to the end and continue scrolling nothing happens, when we are used to containing elements starting to scroll as well. So I made a fix for that:
export function useHorizontalScroll () {
const elRef = useRef();
useEffect(() => {
const el = elRef.current;
if (el) {
const onWheel = (e) => {
if (e.deltaY === 0) return;
if (
!(el.scrollLeft === 0 && e.deltaY < 0) &&
!(el.scrollWidth - el.clientWidth - Math.round(el.scrollLeft) === 0 &&
e.deltaY > 0)
) {
e.preventDefault();
}
el.scrollTo({
left: el.scrollLeft + e.deltaY,
behavior: 'smooth'
});
};
el.addEventListener('wheel', onWheel);
return () => el.removeEventListener('wheel', onWheel);
}
}, []);
return elRef;
}
It's conditionally preventing default behavior only when there is space to scroll in that direction, so when there is no space to scroll, for example the whole page will start to scroll. The change is here:
if (
!(el.scrollLeft === 0 && e.deltaY < 0) &&
!(el.scrollWidth - el.clientWidth - Math.round(el.scrollLeft) === 0 &&
e.deltaY > 0)
) {
e.preventDefault();
}
I can not comment, because my reputation is not enough.
#arVK's answer works, but using 'scrollBy' instead of 'scrollTo' can get smooth wheel.
import { useRef, useEffect } from "react";
export function useHorizontalScroll() {
const elRef = useRef();
useEffect(() => {
const el = elRef.current;
if (el) {
const onWheel = e => {
if (e.deltaY == 0) return;
e.preventDefault();
el.scrollBy(e.deltaY, 0);
};
el.addEventListener("wheel", onWheel);
return () => el.removeEventListener("wheel", onWheel);
}
}, []);
return elRef;
}
You can use onWheel event directly:
import React, { useRef } from "react";
export const Content = () => {
const ref = useRef<HTMLDivElement>(null);
const onWheel = (e: UIEvent) => {
const elelemnt = ref.current;
if (elelemnt) {
if (e.deltaY == 0) return;
elelemnt.scrollTo({
left: elelemnt.scrollLeft + e.deltaY,
});
}
};
return (
<div ref={ref} onWheel={onWheel}>
TEST CONTENT
</div>
);
};

Why isn't my scrolling position working in react?

I'm trying to make a sticky nav with an active state while scrolling. So when you're scrolling over each section, the nav has an active state. Kind of like what is seen here:
https://codepen.io/rishabhp/pen/aNXVbQ
The problem I'm having is some of my numbers aren't correct. Here's the code:
handleScroll = () => {
let sections = document.querySelectorAll('.deal-details__container'),
nav = document.querySelectorAll('.overview-nav'),
navHeight = nav[0].clientHeight;
let totalScroll = document.body.getBoundingClientRect().top;
window.addEventListener('scroll', () => {
sections.forEach(section => {
let topOffset = section.getBoundingClientRect().top;
let top = topOffset - navHeight,
bottom = top + section.clientHeight;
if (totalScroll >= top && totalScroll <= bottom) {
this.setState({ activeSection: true });
} else {
this.setState({ activeSection: false });
}
});
});
console.log(totalScroll);
};
For instance my totalScroll inside of the forEach only amounts to -20 or something similar.
I feel like I'm missing something simple. Any thoughts?

Categories

Resources