I'm really struggling with the maths behind my multilayer parallax. If I have the parallax effect at the top of the page then it works fine, however, due it potentially being anywhere on my page I need to offset the parallax and adjust the height so you don't see the background.
However, I need to work out how much I need to adjust the size of the layers, based on the depth the layer has and where it's positioned on the page. This parallax panel could be anywhere on the page, however.
Here is my code and a working example:
/**
* #author Martyn Lee Ball
* #desc Creates multi-layer Parallax effect
* #version 1.0
* #return {Array} Returns instances of classes as Array
*/
;class Parallax {
constructor(node) {
const self = this;
// Settings and defaults
self.settings = {
container: node,
height: node.clientHeight,
name: node.dataset.parallax,
layers: [],
defaultDepth: 0.5,
offset: function(element) {
let top = 0, left = 0;
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while(element);
return {
top: top,
left: left
};
}.call(self, node)
}
;
// Populate layers setting with objects
(function() {
let layers = (self.settings.container).querySelectorAll('[data-layer]');
let count = 0;
for (const layer in layers) {
if (layers.hasOwnProperty(layer)) {
self.settings.layers.push({
// Get the depth, set to default depth if not set
depth: (function() {
if (layers[layer].dataset.depth !== undefined)
return parseFloat(layers[layer].dataset.depth);
return self.settings.defaultDepth;
}.call(self)),
element: layers[layer],
name: layers[layer].dataset.layer,
});
let height = function(layer) {
const self = this;
let height = self.settings.height;
return (height + (height - (height * layer.depth)));
}.call(self, self.settings.layers[count]);
layers[layer].style.height = height + "px";
}
count++;
}
}.call(this));
this.setupScrollEvent.call(this);
}
setupScrollEvent() {
const self = this;
window.addEventListener('scroll', function(e) {
self.last_known_scroll_position = window.scrollY || document.documentElement.scrollTop;
if (!self.ticking) {
window.requestAnimationFrame(function() {
self.scrolledEvent.call(self, self.last_known_scroll_position);
self.ticking = false;
});
self.ticking = true;
}
});
self.last_known_scroll_position = window.scrollY || document.documentElement.scrollTop;
self.scrolledEvent.call(self, self.last_known_scroll_position);
}
scrolledEvent(scrollY) {
const self = this;
// TODO: Check panel is in view
for (const layer in self.settings.layers) {
if ((self.settings.layers).hasOwnProperty(layer)) {
let movement = -((scrollY * self.settings.layers[layer].depth) - scrollY);
let translate3d = 'translate3d(0, ' + movement + 'px, 0)';
self.settings.layers[layer].element.style['-webkit-transform'] = translate3d;
self.settings.layers[layer].element.style['-moz-transform'] = translate3d;
self.settings.layers[layer].element.style['-ms-transform'] = translate3d;
self.settings.layers[layer].element.style['-o-transform'] = translate3d;
self.settings.layers[layer].element.style.transform = translate3d;
}
}
}
static initialize() {
this.instances = [];
// Ensure DOM has loaded
window.addEventListener('load', function() {
// Does page contain any parallax panels?
let panels = document.querySelectorAll('[data-parallax]');
if (panels.length > 0) {
// Parallax panels found, create instances of class and return them for reference
for (const panel in panels) {
if (panels.hasOwnProperty(panel)) {
this.instances.push(new this(panels[panel]));
}
}
}
}.bind(this));
return this.instances;
}
}
window.plugins = { "parallax-instances": Parallax.initialize() };
html,
body {
margin: 0;
padding: 0;
}
.layer {
height: 800px;
}
section[data-parallax] {
height: 500px;
position: relative;
overflow: hidden;
border: 1px solid black;
border-width: 1px 0 1px 0;
}
section[data-parallax] > div {
position: absolute;
z-index: -1;
background-position: bottom center;
background-size: auto;
background-repeat: no-repeat;
width: 100%;
height: 100%;
}
section[data-parallax] > div[data-layer='layer-bg'] {
background-image: url('https://i.imgur.com/F7pqpWZ.jpg');
}
section[data-parallax] > div[data-layer='layer-1'] {
background-image: url('https://i.imgur.com/uxpVhe1.png');
background-position: left bottom;
}
section[data-parallax] > div[data-layer='layer-2'] {
background-image: url('https://i.imgur.com/JeGChIm.png');
}
section[data-parallax] > div[data-layer='layer-3'] {
background-image: url('https://i.imgur.com/V7l8cxD.png');
background-position: right bottom;
}
section[data-parallax] > div[data-layer='layer-4'] {
background-image: url('https://i.imgur.com/joB5tI4.png');
}
section[data-parallax] > div[data-layer='layer-overlay'] {
background-image: url('https://i.imgur.com/h1ybMNZ.png');
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Parallax Development</title>
<script src="parallax-dev.js" type="text/javascript"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<section style="padding-top: 300px;"></section>
<section data-parallax="panel-1">
<div data-layer="layer-bg" data-depth="0.10"></div>
<div data-layer="layer-1" data-depth="0.20"></div>
<div data-layer="layer-2" data-depth="0.50"></div>
<div data-layer="layer-3" data-depth="0.80"></div>
<div data-layer="layer-4" data-depth="1.0"></div>
</section>
<section style="padding-bottom: 50vh;"></section>
</body>
</html>
Related
But When i select the other picture it did not show in magnifier box, instead it show the default picture in magnifier. how i can fix that?. I want to change the image after selecting from below and magnifier should work on that image.
and magnifier position is very downside, can we also make the appropriate position
HTML
Css
.product-image {
height: 300px;
cursor: zoom-in;
}
.magnifier-container {
display: inline-block;
position: relative;
}
.magnifier {
display: none;
position: absolute;
top: 0;
left: 100%;
overflow: hidden;
height: 300px;
width: 300px;
border: 1px solid #ddd;
border-radius: 10px;
background-color: white;
}
.magnifier__img {
width: 1000px;
transform-origin: 150px 150px;
}
js
// most efficient way to add HTML, faster than innerHTML
const parseHTML = (htmlStr) => {
const range = document.createRange();
range.selectNode(document.body); // required in Safari
return range.createContextualFragment(htmlStr);
};
// pass this function any image element to add magnifying functionality
const makeImgMagnifiable = (img) => {
const magnifierFragment = parseHTML(`
<div class="magnifier-container">
<div class="magnifier">
<img class="magnifier__img" src="${img.src}"/>
</div>
</div>
`);
// This preserves the original element reference instead of cloning it.
img.parentElement.insertBefore(magnifierFragment, img);
const magnifierContainerEl = document.querySelector(".magnifier-container");
img.remove();
magnifierContainerEl.appendChild(img);
// query the DOM for the newly added elements
const magnifierEl = magnifierContainerEl.querySelector(".magnifier");
const magnifierImg = magnifierEl.querySelector(".magnifier__img");
// set up the transform object to be mutated as mouse events occur
const transform = {
translate: [0, 0],
scale: 1,
};
// shortcut function to set the transform css property
const setTransformStyle = (el, { translate, scale }) => {
const [xPercent, yRawPercent] = translate;
const yPercent = yRawPercent < 0 ? 0 : yRawPercent;
// make manual pixel adjustments to better center
// the magnified area over the cursor.
const [xOffset, yOffset] = [
`calc(-${xPercent}% + 250px)`,
`calc(-${yPercent}% + 70px)`,
];
el.style = `
transform: scale(${scale}) translate(${xOffset}, ${yOffset});
`;
};
// show magnified thumbnail on hover
img.addEventListener("mousemove", (event) => {
const [mouseX, mouseY] = [event.pageX + 40, event.pageY - 20];
const { top, left, bottom, right } = img.getBoundingClientRect();
transform.translate = [
((mouseX - left) / right) * 100,
((mouseY - top) / bottom) * 100,
];
magnifierEl.style = `
display: block;
top: ${mouseY}px;
left: ${mouseX}px;
`;
setTransformStyle(magnifierImg, transform);
});
// zoom in/out with mouse wheel
img.addEventListener("wheel", (event) => {
event.preventDefault();
const scrollingUp = event.deltaY < 0;
const { scale } = transform;
transform.scale =
scrollingUp && scale < 3
? scale + 0.1
: !scrollingUp && scale > 1
? scale - 0.1
: scale;
setTransformStyle(magnifierImg, transform);
});
// reset after mouse leaves
img.addEventListener("mouseleave", () => {
magnifierEl.style = "";
magnifierImg.style = "";
});
};
const img = document.querySelector(".product-image");
makeImgMagnifiable(img);
I'm trying to make an Infinite marquee that speeds up on scroll, https://altsdigital.com/ you can see the effect on this website, the text says "Not your usual SEO agency" and when you scroll it speeds up.
Here's what I've tried but it does not work. It does not loop properly without overlapping (keep your eye on the left side of the page, you'll notice the text briefly overlaps and then translates left to create a gap) and I am unsure on how to fix it:
Here's the code (TEXT ONLY VISIBLE ON "FULL PAGE" view):
const lerp = (current, target, factor) => {
let holder = current * (1 - factor) + target * factor;
holder = parseFloat(holder).toFixed(3);
return holder;
};
class LoopingText {
constructor(DOMElements) {
this.DOMElements = DOMElements;
this.lerpingData = {
counterOne: { current: 0, target: 0 },
counterTwo: { current: 100, target: 100 },
};
this.interpolationFactor = 0.1;
this.speed = 0.2;
this.render();
this.onScroll();
}
onScroll() {
window.addEventListener("scroll", () => {
this.lerpingData["counterOne"].target += this.speed * 5;
this.lerpingData["counterTwo"].target += this.speed * 5;
});
}
lerp() {
for (const counter in this.lerpingData) {
this.lerpingData[counter].current = lerp(
this.lerpingData[counter].current,
this.lerpingData[counter].target,
this.interpolationFactor
);
}
this.lerpingData["counterOne"].target += this.speed;
this.lerpingData["counterTwo"].target += this.speed;
if (this.lerpingData["counterOne"].target < 100) {
this.DOMElements[0].style.transform = `translate(${this.lerpingData["counterOne"].current}%, 0%)`;
} else {
this.lerpingData["counterOne"].current = -100;
this.lerpingData["counterOne"].target = -100;
}
if (this.lerpingData["counterTwo"].target < 100) {
this.DOMElements[1].style.transform = `translate(${this.lerpingData["counterTwo"].current}%, 0%)`;
} else {
this.lerpingData["counterTwo"].current = -100;
this.lerpingData["counterTwo"].target = -100;
}
}
render() {
this.lerp();
window.requestAnimationFrame(() => this.render());
}
}
let textArray = document.getElementsByClassName("item");
new LoopingText(textArray);
#import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Poppins";
}
.hero-section {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
position: relative;
width: 100%;
}
.loop-container {
position: relative;
width: 100%;
display: flex;
/* padding-right: 24px; */
}
.item {
position: absolute;
font-size: 15rem;
white-space: nowrap;
margin: 0;
}
span {
transition: all 0.2s;
cursor: default;
}
.hover:hover {
color: gray;
transition: all 0.2s;
}
<body>
<section class="hero-section">
<div class="loop-container">
<div class="item">Infinite Horizontal Looping Text</div>
<div class="item">Infinite Horizontal Looping Text</div>
</div>
</section>
<section class="hero-section">
</section>
</body>
Your items are overlapping because you're not allowing any lerping diffing when the items should switch positions.
The current value should never equal the target value. If the values match, than the current value needs to catch up the target — giving that erratic movement and wrong calculations, additionally aggravated for the two sibling elements which should be perfectly in sync to give that immediate snap-back, perceived as a fluid continuous motion.
Solution
Instead of animating two (or more) children independently,animate only the parent .loop-container.
The container should be as wide as one child element exactly.
"Push" one child element to the far left using position: absolute; left: -100%
To allow the target value to be always greater than the current value:when the target value is greater than 100 — set current to the negative difference of the two values, and target to 0
Demo time:
const lerp = (current, target, factor) => current * (1 - factor) + target * factor;
class LoopingText {
constructor(el) {
this.el = el;
this.lerp = {current: 0, target: 0};
this.interpolationFactor = 0.1;
this.speed = 0.2;
this.direction = -1; // -1 (to-left), 1 (to-right)
// Init
this.el.style.cssText = `position: relative; display: inline-flex; white-space: nowrap;`;
this.el.children[1].style.cssText = `position: absolute; left: ${100 * -this.direction}%;`;
this.events();
this.render();
}
events() {
window.addEventListener("scroll", () => this.lerp.target += this.speed * 5);
}
animate() {
this.lerp.target += this.speed;
this.lerp.current = lerp(this.lerp.current, this.lerp.target, this.interpolationFactor);
if (this.lerp.target > 100) {
this.lerp.current -= this.lerp.target;
this.lerp.target = 0;
}
const x = this.lerp.current * this.direction;
this.el.style.transform = `translateX(${x}%)`;
}
render() {
this.animate();
window.requestAnimationFrame(() => this.render());
}
}
document.querySelectorAll(".loop-container").forEach(el => new LoopingText(el));
/* QuickReset */ * { margin: 0; box-sizing: border-box; }
body { min-height: 400vh; /* force some scrollbars */ }
.hero-section {
position: relative;
top: 50vh;
overflow: hidden;
font: 900 9vw/1 sans-serif;
min-height: 100vh;
}
<section class="hero-section">
<div class="loop-container">
<div class="item">Infinite Horizontal Looping Text </div>
<div class="item">Infinite Horizontal Looping Text </div>
</div>
</section>
PS:
When animating, (unless you want an element static / immovable) you should never put an elements transformations inside an if/else logic. The element should always receive the updated transformations. Put inside the conditional logic only the values that you actually want to modify (as I did in the example above).
I'm trying to make an Infinite marquee that speeds up on scroll, https://altsdigital.com/ you can see the effect on this website, the text says "Not your usual SEO agency" and when you scroll it speeds up.
Here's what I've tried but it does not work. It does not loop properly without overlapping (keep your eye on the left side of the page, you'll notice the text briefly overlaps and then translates left to create a gap) and I am unsure on how to fix it:
Here's the code (TEXT ONLY VISIBLE ON "FULL PAGE" view):
const lerp = (current, target, factor) => {
let holder = current * (1 - factor) + target * factor;
holder = parseFloat(holder).toFixed(3);
return holder;
};
class LoopingText {
constructor(DOMElements) {
this.DOMElements = DOMElements;
this.lerpingData = {
counterOne: { current: 0, target: 0 },
counterTwo: { current: 100, target: 100 },
};
this.interpolationFactor = 0.1;
this.speed = 0.2;
this.render();
this.onScroll();
}
onScroll() {
window.addEventListener("scroll", () => {
this.lerpingData["counterOne"].target += this.speed * 5;
this.lerpingData["counterTwo"].target += this.speed * 5;
});
}
lerp() {
for (const counter in this.lerpingData) {
this.lerpingData[counter].current = lerp(
this.lerpingData[counter].current,
this.lerpingData[counter].target,
this.interpolationFactor
);
}
this.lerpingData["counterOne"].target += this.speed;
this.lerpingData["counterTwo"].target += this.speed;
if (this.lerpingData["counterOne"].target < 100) {
this.DOMElements[0].style.transform = `translate(${this.lerpingData["counterOne"].current}%, 0%)`;
} else {
this.lerpingData["counterOne"].current = -100;
this.lerpingData["counterOne"].target = -100;
}
if (this.lerpingData["counterTwo"].target < 100) {
this.DOMElements[1].style.transform = `translate(${this.lerpingData["counterTwo"].current}%, 0%)`;
} else {
this.lerpingData["counterTwo"].current = -100;
this.lerpingData["counterTwo"].target = -100;
}
}
render() {
this.lerp();
window.requestAnimationFrame(() => this.render());
}
}
let textArray = document.getElementsByClassName("item");
new LoopingText(textArray);
#import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Poppins";
}
.hero-section {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
position: relative;
width: 100%;
}
.loop-container {
position: relative;
width: 100%;
display: flex;
/* padding-right: 24px; */
}
.item {
position: absolute;
font-size: 15rem;
white-space: nowrap;
margin: 0;
}
span {
transition: all 0.2s;
cursor: default;
}
.hover:hover {
color: gray;
transition: all 0.2s;
}
<body>
<section class="hero-section">
<div class="loop-container">
<div class="item">Infinite Horizontal Looping Text</div>
<div class="item">Infinite Horizontal Looping Text</div>
</div>
</section>
<section class="hero-section">
</section>
</body>
Your items are overlapping because you're not allowing any lerping diffing when the items should switch positions.
The current value should never equal the target value. If the values match, than the current value needs to catch up the target — giving that erratic movement and wrong calculations, additionally aggravated for the two sibling elements which should be perfectly in sync to give that immediate snap-back, perceived as a fluid continuous motion.
Solution
Instead of animating two (or more) children independently,animate only the parent .loop-container.
The container should be as wide as one child element exactly.
"Push" one child element to the far left using position: absolute; left: -100%
To allow the target value to be always greater than the current value:when the target value is greater than 100 — set current to the negative difference of the two values, and target to 0
Demo time:
const lerp = (current, target, factor) => current * (1 - factor) + target * factor;
class LoopingText {
constructor(el) {
this.el = el;
this.lerp = {current: 0, target: 0};
this.interpolationFactor = 0.1;
this.speed = 0.2;
this.direction = -1; // -1 (to-left), 1 (to-right)
// Init
this.el.style.cssText = `position: relative; display: inline-flex; white-space: nowrap;`;
this.el.children[1].style.cssText = `position: absolute; left: ${100 * -this.direction}%;`;
this.events();
this.render();
}
events() {
window.addEventListener("scroll", () => this.lerp.target += this.speed * 5);
}
animate() {
this.lerp.target += this.speed;
this.lerp.current = lerp(this.lerp.current, this.lerp.target, this.interpolationFactor);
if (this.lerp.target > 100) {
this.lerp.current -= this.lerp.target;
this.lerp.target = 0;
}
const x = this.lerp.current * this.direction;
this.el.style.transform = `translateX(${x}%)`;
}
render() {
this.animate();
window.requestAnimationFrame(() => this.render());
}
}
document.querySelectorAll(".loop-container").forEach(el => new LoopingText(el));
/* QuickReset */ * { margin: 0; box-sizing: border-box; }
body { min-height: 400vh; /* force some scrollbars */ }
.hero-section {
position: relative;
top: 50vh;
overflow: hidden;
font: 900 9vw/1 sans-serif;
min-height: 100vh;
}
<section class="hero-section">
<div class="loop-container">
<div class="item">Infinite Horizontal Looping Text </div>
<div class="item">Infinite Horizontal Looping Text </div>
</div>
</section>
PS:
When animating, (unless you want an element static / immovable) you should never put an elements transformations inside an if/else logic. The element should always receive the updated transformations. Put inside the conditional logic only the values that you actually want to modify (as I did in the example above).
Hi I have 5 squares and I want to give randomly position left and top to them but giving randomly position to them can make a problem like this:
and this is my code:
let container = document.querySelector(".container");
let squares = document.querySelectorAll(".square");
let pageWidth = container.getBoundingClientRect().width;
let pageHeight = container.getBoundingClientRect().height;
let width;
squares.forEach((square) => {
width = Math.floor(Math.random() * 25) + 30;
square.style.width = width + "px";
square.style.height = width + "px";
square.style.position = "absolute";
square.style.top = Math.floor(Math.random() * (pageHeight - 200)) + "px";
square.style.left = Math.floor(Math.random() * (pageWidth - 200)) + "px";
});
.container {
position: relative;
width: 100%;
height: 100vh;
}
.square:nth-child(1) {
background-color: #142bc5;
}
.square:nth-child(2) {
background-color: #37d437;
}
.square:nth-child(3) {
background-color: #c52f14;
}
.square:nth-child(4) {
background-color: #c5149f;
}
.square:nth-child(5) {
background-color: #38c514;
}
<div class="container">
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
</div>
how can I prevent this issue?
You need to keep a record of where boxes have already been drawn on the page and how large they are. You then need to check for collisions / overlaps when you draw subsequent boxes.
Here's a code sample that uses this Stack Overflow answer to determine whether or not boxes overlap.
Code
const container = document.querySelector(".container");
const squares = document.querySelectorAll(".square");
const {width: pageWidth, height: pageHeight} = container.getBoundingClientRect();
const drawnSquares = [];
squares.forEach((square) => {
// Since this is a square, width and height are equal
const {width, top, left} = getRandomSquare();
square.style.width = `${width}px`;
square.style.height = `${width}px`;
square.style.top = `${top}px`;
square.style.left = `${left}px`;
drawnSquares.push({
width,
top,
left,
bottom: top + width,
right: left + width
});
});
function getRandomSquare() {
const width = Math.floor(Math.random() * 25) + 30;
const top = Math.floor(Math.random() * (pageHeight - width));
const left = Math.floor(Math.random() * (pageWidth - width));
const square = {width, top, left};
if (!overlapsWithAny(square)) {
// We've got a good square, return it
return square;
} else {
// Our square overlaps with one that's already on the page
// so we should generate a new square
return getRandomSquare();
}
}
function overlapsWithAny(square) {
if (drawnSquares.length === 0) {
return false;
} else {
return drawnSquares.some(ds => {
// Based on https://stackoverflow.com/a/12067046/2176546
return !(square.right < ds.left ||
square.left > ds.right ||
square.bottom < ds.top ||
square.top > ds.bottom);
})
}
}
body {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
}
.container {
position: relative;
width: 100vw;
height: 100vh;
}
.square {
position: absolute;
}
.square:nth-child(1) {
background-color: #142bc5;
}
.square:nth-child(2) {
background-color: #37d437;
}
.square:nth-child(3) {
background-color: #c52f14;
}
.square:nth-child(4) {
background-color: #c5149f;
}
.square:nth-child(5) {
background-color: #38c514;
}
<div class="container">
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
</div>
Note how I added body {margin: 0; padding: 0} to the stylesheet to make 100vh and 100vw work properly.
as the answer by Robbie JW mentioned you need to apply a function to detect collisions , there is a very helpful function also you can find in this answer
Im working on a site right now with a scroll loop effect (when you reach the bottom of the page it seamlessly jumps back to the top creating an endless loop). Though I am having an issue trying implement an effect to rotate the individual div's based on their offsetTop.
Here is a fiddle link with the rotate effect working without the scroll loop effect-> https://jsfiddle.net/jacob_truax/bgrkewny/3/
Here is a link to a fiddle with both effects -> https://jsfiddle.net/jacob_truax/b1x4dow7/18/
As you can see in the second fiddle, adding the scroll loop effect while implementing the rotation effect breaks the code. Can someone help me figure this out please?
Here is the js for the broken fiddle
const sections = document.querySelectorAll("section")
const divTag = document.querySelector("div.Loop")
const mainTag = document.querySelector("main")
var doc = window.document,
clones = divTag.querySelectorAll('.is-clone'),
disableScroll = false,
scrollHeight = 0,
scrollPos = 0,
clonesHeight = 0,
i = 0;
const addMovement = function() {
const topViewport = divTag.offsetTop
const midViewport = topViewport + (divTag.offsetHeight / 2)
sections.forEach(section => {
const topSection = section.offsetTop
const midSection = topSection + (section.offsetHeight / 2)
const distanceToSection = (midViewport - midSection)
console.log(distanceToSection)
const image = section.querySelector(".info")
image.style.transform = `rotate(${distanceToSection}deg)`
})
}
addMovement()
function getScrollPos () {
return (divTag.offsetTop || divTag.scrollTop) - (divTag.clientTop || 0);
}
function setScrollPos (pos) {
divTag.scrollTop = pos;
}
function getClonesHeight () {
clonesHeight = 0;
for (i = 0; i < clones.length; i += 1) {
clonesHeight = clonesHeight + clones[i].offsetHeight;
}
return clonesHeight;
}
function reCalc () {
scrollPos = getScrollPos();
scrollHeight = divTag.scrollHeight;
clonesHeight = getClonesHeight();
if (scrollPos <= 0) {
setScrollPos(1); // Scroll 1 pixel to allow upwards scrolling
}
}
function scrollUpdate () {
if (!disableScroll) {
scrollPos = getScrollPos();
if (clonesHeight + scrollPos >= scrollHeight) {
// Scroll to the top when you’ve reached the bottom
setScrollPos(1); // Scroll down 1 pixel to allow upwards scrolling
disableScroll = true;
} else if (scrollPos <= 0) {
// Scroll to the bottom when you reach the top
setScrollPos(scrollHeight - clonesHeight);
disableScroll = true;
}
}
if (disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering
window.setTimeout(function () {
disableScroll = false;
}, 40);
}
}
function init () {
reCalc();
divTag.addEventListener('scroll', function () {
window.requestAnimationFrame(scrollUpdate);
addMovement()
}, false);
window.addEventListener('resize', function () {
window.requestAnimationFrame(reCalc);
addMovement()
}, false);
}
if (document.readyState !== 'loading') {
init()
} else {
doc.addEventListener('DOMContentLoaded', init, false)
}
Here is the css
html,
body {
height: 100%;
/* overflow: hidden; */
}
body {
color: #000;
}
main {
height: 100%;
position: relative;
top: 0;
left: 0;
}
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
text-align: center;
z-index: 1;
}
.Loop {
position: absolute;
height: 100%;
overflow: auto;
}
section {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
}
::scrollbar {
display: none;
}
section div {
position: absolute;
z-index: 2;
text-align: center;
width: 50%;
background-color: #ff0000;
}
section img {
position: relative;
width: 50%;
background-color: #000;
}
The offsetTop property returns the top position (in pixels) relative
to the top of the offsetParent element.
Changing line #14 to use scrollTop instead works:
const topViewport = divTag.scrollTop;