Move element by certain amount while scrolling - javascript

I want to move the white box to the right by 50% while scrolling until it reaches the red section. The distance to the red section is 1000px in the example.
The code below moves the box to the right as I scroll down, and I'm just using a random number 10 to slow down the movement but I can't get my head around to make it move evenly for every scroll event until the box reaches the red section and move 50% to the right.
var xPos = 0;
function getXPos(target, windowPos) {
var amount = windowPos - target;
xPos = amount / 10;
return xPos;
}
$(window).scroll(function() {
var windowPos = $(window).scrollTop();
var sectionOne = $('section.one').offset().top;
var sectionTwo = $('section.two').offset().top;
var box = $('.box');
if (windowPos > sectionOne && windowPos < sectionTwo) {
box.css({
"transform": 'translateX(' + getXPos(sectionOne, windowPos) + '%)'
});
}
});
body {
margin: 0;
}
.box {
background: white;
position: sticky;
top: 0;
width: 300px;
height: 300px;
}
section.one {
height: 1000px;
background: blue;
}
section.two {
height: 1000px;
background: red;
}
<section class="one">
<div class="box"></div>
</section>
<section class="two"></section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
There is also another issue with scroll that if I scroll too fast, the box won't move as much.
Here is the fiddle for demonstration.
https://jsfiddle.net/sungsoonz/0Lspo2d9/

So I used the logic of making a progress bar for whole page but for your section with class "one". So when you scroll the section 100% of it's height the "left" css property on the div with class "box" becomes at value of "100%". But as I understood we need to stop moving when we reach section with class "two" with div with class "box". So {left: 100%} will become when we have scrolled whole section with class "box" minus the visible height of div with class "box". Then it is easily calculated to move only for 50% of width of section with class "one" (-width of div with class "box" width / 2 to center it). Hope I described my solution clearly (xd). Hope it helps
The code:
one = document.querySelector(".one")
two = document.querySelector(".two")
box = document.querySelector(".box")
$(window).on('scroll', function (){
if (window.scrollY >= (one.scrollHeight - box.offsetHeight)) {
$('.box').css('left', `calc(50% - ${(box.offsetWidth / 2)}px`);
return
}
$scrolledFrom = $(document).scrollTop();
$documentHeight = $(document).height() - ($(".two").height() + box.offsetHeight);
$leftOffset = ($scrolledFrom / $documentHeight) * 100;
$('.box').css('left', `calc(${($leftOffset / 2)}% - ${(box.offsetWidth / 2)}px`);
console.log ()
});
body {
margin: 0;
}
.box {
background: white;
position: sticky;
top: 0;
left: 0;
width: 300px;
height: 300px;
}
section.one {
height: 1000px;
background: blue;
}
section.two {
height: 1000px;
background: red;
}
<section class="one">
<div class="box"></div>
</section>
<section class="two"></section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Related

Logo cloning on navbar scroll

I have the following script:
$(function() {
var header = $(".header-nav");
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 50) {
header.addClass("scrolled");
}
if (scroll > 50 && scroll < 60) {
$(".header_logo img").clone().appendTo(".header-logo");
}
if (scroll <= 50) {
header.removeClass("scrolled");
}
});
});
It's supposed to make the navbar fixed on scroll and clone the website logo to the navbar on a .header-logo empty div
But it doesn't work as expected. The logo is mass duplicated or don't appear until a top scrolling.
Is there a way to make it work as: When I scroll, the logo is cloned one time on the navbar then disappear if you go back to top page?
Thanks
Clone img outside the condition, then append or remove based on your if condition. You need to set a class to detect cloned img for removing.
$(function() {
var header = $(".header-nav");
$el = $(".header-logo img").clone().addClass('cloned');
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 50) {
header.addClass("scrolled");
} else {
header.removeClass("scrolled");
}
if (scroll > 50) {
$el.appendTo(".header-logo");
} else {
$('.cloned').remove();
}
});
});
body {
height: 1000px;
/* fake height! */
}
header.header-nav.scrolled {
position: fixed;
}
.header-nav {
background: white;
width: 100%;
min-height: 150px;
border: 1px solid gray;
}
.scrolled {
background: red;
}
.header-logo img {
height: 150px;
display: block;
margin: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header class="header-nav">
<div class="header-logo">
<img src="https://i.graphicmama.com/blog/wp-content/uploads/2019/10/02145410/logo-design-trends-2020-colorful-gradient-logos-example-1.gif" />
</div>
</header>

Using jQuery and CSS3's TranslateX() to reveal an element while scrolling parent element into view

I would like to change the position of a circle when it's parent section is scrolled into view.
While scrolling down after the parent is in view, it should move to the right and when scrolling up, it should move back to where it was originally. (-200px to the left) It should only be moved while the user is actively scrolling.
If the user scrolls all the way down to the very bottom of the circle's parent section, or if they have already scrolled down to the bottom and reload the page, the circle should appear in it's fully-revealed position.
My current code partially works, but I'm having trouble with getting the entire element to appear based on how much of the parent element is visible and also getting it to display in it's final position when reloading the page after having scrolled to the very bottom.
JSFiddle: https://jsfiddle.net/thebluehorse/gu2rvnsw/
var $window = $(window),
$sectionFour = $('.section-four'),
$circle = $sectionFour.find('.circle'),
lastScrollTop = 0,
position = -200;
function revealCircle() {
var isVisible,
st = $window.scrollTop();
isVisible = isInView($sectionFour);
if (isVisible) {
// console.log('section four is in view, so lets do stuff!');
if (st > lastScrollTop) {
if (position === 0) {
return false
}
$circle.css('transform', 'translateX(' + position + 'px')
position++;
} else {
if (position === -200) {
return false
}
$circle.css('transform', 'translateX(' + position + 'px')
position--;
}
}
}
function isInView(node) {
var rect;
if (typeof jQuery === 'function' && node instanceof jQuery) {
node = node[0];
}
rect = node.getBoundingClientRect();
return (
(rect.height > 0 || rect.width > 0) &&
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.left <= (window.innerWidth || document.documentElement.clientWidth)
);
}
$window.on('scroll', revealCircle);
.circle {
width: 400px;
height: 400px;
background: #fff;
-webkit-border-radius: 200px;
-moz-border-radius: 200px;
border-radius: 200px;
transform: translateX(-200px); }
.section {
min-height: 400px; }
.section-one {
background-color: red; }
.section-two {
background-color: orange; }
.section-three {
background-color: yellow; }
.section-four {
background-color: green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="section section-one"></section>
<section class="section section-two"></section>
<section class="section section-three"></section>
<section class="section section-four">
<div class="circle"></div>
</section>
Your code can be simplified a bit. The only value you need to keep track of as the page is scrolled is scrollTop(). Because the geometry of $sectionFour never changes, you can cache its getBoundingClientRect() right away.
Once you know that $sectionFour is in view, you want to figure out how many pixels of its total height are in view, convert that to a percentage, and then apply that percentage to the initial position of -200. Essentially, when only a few pixels are showing, that's a small percentage, such as 10% and -200 becomes -180. When the element is fully in view, the percentage should be near 100%, and -200 becomes 0. This means you're not keeping track of the last position or which direction the scroll was, you're just computing what the value should be based on the current viewport (scrollTop).
var $window = $(window),
$sectionFour = $('.section-four'),
$circle = $sectionFour.find('.circle');
rect = $sectionFour[0].getBoundingClientRect();
function revealCircle() {
var scrollTop = $window.scrollTop();
var windowHeight = $window[0].innerHeight;
if (scrollTop + windowHeight > rect.top) {
var percentVisible = (scrollTop - (rect.top - windowHeight)) / rect.height;
var position = 200 - (percentVisible * 200);
$circle.css('transform', 'translateX(-' + position + 'px');
}
}
$window.on('scroll', revealCircle);
body { margin:0;}
.circle {
width: 400px;
height: 400px;
background: #fff;
-webkit-border-radius: 200px;
-moz-border-radius: 200px;
border-radius: 200px;
transform: translateX(-200px); }
.section {
min-height: 400px; }
.section-one {
background-color: red; }
.section-two {
background-color: orange; }
.section-three {
background-color: yellow; }
.section-four {
background-color: green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section class="section section-one"></section>
<section class="section section-two"></section>
<section class="section section-three"></section>
<section class="section section-four">
<div class="circle"></div>
</section>
It could be more simpler to use css variables for pure JS solution :
const sectFour = document.querySelector('#section-four')
, divCircle = sectFour.querySelector('.circle')
function percentVisible(elm)
{
let rect = elm.getBoundingClientRect()
, viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight)
, visu = viewHeight-rect.top
return (visu<0) ? -1 : Math.min( 100, (visu/rect.height*100))
// return !(rect.bottom < 0 || rect.top - viewHeight >= 0) => checkVisible
}
window.onscroll=_=>
{
let circlePos = percentVisible(sectFour) *2
if (circlePos>=0)
{
divCircle.style.setProperty('--circle-pos', `-${200-circlePos}px`)
// IE 11 : // divCircle.style=('transform:translateX(-'+(200-circlePos)+'px')
}
}
* { margin: 0 }
.circle {
--circle-pos :-200px;
width : 400px;
height : 400px;
background-color : #fff;
-webkit-border-radius: 200px;
-moz-border-radius: 200px;
border-radius: 200px;
transform : translateX(var(--circle-pos));
/* IE 11 ........... : translateX(-200px); */
}
section { min-height: 400px; }
section:nth-of-type(1) { background-color: red; }
section:nth-of-type(2) { background-color: orange; }
section:nth-of-type(3) { background-color: yellow; }
section:nth-of-type(4) { background-color: green; }
<section></section>
<section></section>
<section></section>
<section id="section-four"> <div class="circle"></div> </section>
You should have a look at Intersection Observer (IO), this was designed to solve problems like yours. Listening to scroll event and calculating the position can result in bad performance.
First, you have to define the options for the IO:
let options = {
root: document.querySelectorAll('.section-four'),
rootMargin: '0px',
threshold: 1.0
}
let observer = new IntersectionObserver(callback, options);
After defining the options you have to tell the observer which elements to observe, I guess in your case this would be .section-four:
let targets = document.querySelectorAll('.section-four');
targets.forEach(target => {
observer.observe(target) }
)
Final step is to define the callback function that should be executed once .section-four is getting into view:
let callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element
// here you can do something like $(entry.target).find('circle') to get your circle
});
};
Have a look at this demo, depending on how much the element is visible the background-color changes. I think this comes close to your problem, you just don't change the bg-color you animate the circle inside the element.
There is also another demo on the site that displays how much of an element is visible on the screen, maybe this suits you better.
You can also use this polyfill from w3c to support older browsers.

Fix Section when reaches top. Unfix when previous section is visible

I'm trying to stick a section to the top when it hits the top of the browser on scroll down, but I'd like to unstick it when the user scrolls back up and the previous section is back in view.
I'm detecting distance from top to section I'd like to stick, but once its at the top how do we detect user scrolling back up and previous section comes back into view.
My Codepen: https://codepen.io/omarel/pen/LeEjax
Snippet
$(window).on('scroll', function() {
var scrollTop = $(window).scrollTop();
sectionone = $('section.one').offset().top;
sectiontwo = $('section.two').offset().top;
sectiontwodistance = (sectiontwo - scrollTop);
sectiononedistance = (sectionone - scrollTop);
console.log(sectiononedistance);
if (sectiontwodistance < 1) {
$('section.two').addClass('fix');
}
});
html,
body {
width: 100%;
height: 100%;
}
section {
height: 100%;
border: 5px solid red;
position: absolute;
width: 100%;
}
section.one {
z-index: 1;
top: 0%;
}
section.two {
border: 5px solid green;
z-index: 2;
top: 100%;
}
section.three {
z-index: 3;
top: 200%;
}
section.fix {
position: fixed;
top: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="one">
1
</section>
<section class="two">
2
</section>
<section class="three">
3
</section>
I would update your jQuery to the snippet below. It checks the position of section one against the height of the window and if less than, or equal to it removes the .fix class.
$(window).on('scroll', function() {
var scrollTop = $(window).scrollTop();
sectionone = $('section.one').offset().top;
sectiontwo = $('section.two').offset().top;
sectiontwodistance = (sectiontwo - scrollTop);
sectiononedistance = (sectionone - scrollTop);
console.log(sectiononedistance);
if (sectiontwodistance < 1) {
$('section.two').addClass('fix');
}
if (Math.abs(sectiononedistance) <= $(window).height()) {
$('section.two').removeClass('fix');
}
});

How do I make an image move when i scroll down?

Here is an example of what i want to achieve:
https://www.flambette.com/en/
I have tried to change the css properties of images but that effect does not satisfy my needs.
I have tried the following code:
mydocument.on('scroll',function() {
if (mydocument.scrollTop() > 10 && mydocument.scrollTop() < 200 ) {
$('#first').css('top', '320px');
$('#first').css('left', '215px');
$('#first').css('transition', '0.5s');
}
else {
$('#first').css('top', '300px');
$('#first').css('left', '200px');
$('#first').css('transition', '0.5s');
}
});
This is supposed to move an image when you scroll between 10 and 200 px.
var container = document.getElementById('container');
var windowHeight = window.innerHeight;
var windowWidth = window.innerWidth;
var scrollArea = 1000 - windowHeight;
var square1 = document.getElementsByClassName('square')[0];
var square2 = document.getElementsByClassName('square')[1];
// update position of square 1 and square 2 when scroll event fires.
window.addEventListener('scroll', function() {
var scrollTop = window.pageYOffset || window.scrollTop;
var scrollPercent = scrollTop/scrollArea || 0;
square1.style.left = scrollPercent*window.innerWidth + 'px';
square2.style.left = 800 - scrollPercent*window.innerWidth*0.6 + 'px';
});
body {
overflow-x: hidden;
}
.container {
width: 100%;
height: 1000px;
}
.square {
position: absolute;
}
.square-1 {
width: 100px;
height: 100px;
background: red;
top: 600px;
}
.square-2 {
width: 120px;
height: 120px;
background: black;
left: 800px;
top: 800px;
}
<div class="container" id="container">
<div class="square square-1"></div>
<div class="square square-2"></div>
</div>
Hope to help you.
Here you can see more examples about movable elements and scroll events.

Stick div at top not working properly : javascript

I going to create a scroll and stick div which has to stick on the top of the page but while scrolling down the div next to stickdiv automatically stick to the div before to sticky div
var left = document.getElementsByClassName("stickdiv");
for (var i = 0; i < left.length; i++) {
var stop = (left[0].offsetTop);
window.onscroll = function(e) {
var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
// left.offsetTop;
if (scrollTop >= stop) {
// get array item by index
left[0].classList.add('stick'); //adding a class name
} else {
// get array item by index
left[0].classList.remove('stick');
}
}
}
.stickdiv {
height: 50vh!important;
width: 100vh!important;
background-color: green!important;
}
.stick {
position: fixed;
top: 0;
margin: 0 0
}
#right {
float: right;
width: 100px;
height: 1000px;
background: red;
}
.des {
height: 300px;
width: 100%;
background-color: #000;
}
<div class="des"></div>
<div class="stickdiv"></div>
<div id="right"></div>
Example : green color div is the sticky div but after scrollingdown , red is also going to stick , I've tried position absolute in css but not working how to fix it
Here is the code to make green sticky when scrolling.
$ = document.querySelectorAll.bind(document);
// how far is the green div from the top of the page?
var initStickyTop = $(".stickdiv")[0].getBoundingClientRect().top + pageYOffset;
// clone the green div
var clone = $(".stickdiv")[0].cloneNode(true);
// hide it first
clone.style.display = "none";
// add it to dom
document.body.appendChild(clone);
addEventListener("scroll",stick=function() {
// if user scroll past the sticky div
if (initStickyTop < pageYOffset) {
// hide the green div but the div still take up the same space as before so scroll position is not changed
$(".stickdiv")[0].style.opacity = "0";
// make the clone sticky
clone.classList.add('stick');
// show the clone
clone.style.opacity="1";
clone.style.display = "block";
} else {
// make the clone not sticky anymore
clone.classList.remove("stick");
// hide it
clone.style.display = "none";
// show the green div
$(".stickdiv")[0].style.opacity="1";
};
});
// when resize, recalculate the position of the green div
addEventListener("resize", function() {
initStickyTop = $(".stickdiv")[0].getBoundingClientRect().top + pageYOffset;
stick();
});
.stickdiv {
height: 50vh!important;
width: 100vh!important;
background-color: green!important;
}
.stick {
position: fixed;
top: 0;
margin: 0 0
}
#right {
float: right;
width: 100px;
height: 1000px;
background: red;
}
.des {
height: 300px;
width: 100%;
background-color: #000;
}
<div class="des"></div>
<div class="stickdiv"></div>
<div id="right"></div>
JS FIDDLE
you might want to remove the stickdiv class and add it accordingly
if (scrollTop >= stop) {
// get array item by index
left[0].classList.add('stick'); //adding a class name
left[0].classList.remove('stickdiv');
} else {
// get array item by index
left[0].classList.remove('stick');
left[0].classList.add('stickdiv');
}

Categories