Reputation: 867
I'm trying to create a progress bar that shows how much of a certain element the user still has left to view. Here are some details:
.postProgressBar
appears by default under .postHeroImage
.postProgressBar
to slowly fill up based on how much of the .spacer
element there is left to scroll to..postProgressBar
hits the bottom of my header
, I want it to become fixed
to the bottom of the header
(and to unfix when .postHeroImage
is in view again).See my current approach:
$(function() {
gsap.registerPlugin(ScrollTrigger);
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 1) {
$(".header").addClass("fixed");
} else {
$(".header").removeClass("fixed");
}
});
var action = gsap.set('.postProgressBar', { position:'fixed', paused:true});
gsap.to('progress', {
value: 100,
ease: 'none',
scrollTrigger: {
trigger: "#startProgressBar",
scrub: 0.3,
markers:true,
onEnter: () => action.play(),
onLeave: () => action.reverse(),
onLeaveBack: () => action.reverse(),
onEnterBack: () => action.reverse(),
}
});
});
body {
background-color: lightblue;
--white: #FFFFFF;
--grey: #002A54;
--purple: #5D209F;
}
.header {
position: absolute;
top: 0;
width: 100%;
padding: 20px 15px;
z-index: 9999;
background-color: var(--white);
}
.header.fixed {
position: fixed;
background-color: var(--white);
border-bottom: 1px solid var(--grey);
}
.postHeroImage {
padding: 134px 0 0 0;
margin-bottom: 105px;
position: relative;
}
.postHeroImage__bg {
background-size: cover;
background-repeat: no-repeat;
width: 100%;
min-height: 400px;
}
progress {
position: absolute;
bottom: -15px;
left: 0;
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 15px;
border: none;
background: transparent;
z-index: 9999;
}
progress::-webkit-progress-bar {
background: transparent;
}
progress::-webkit-progress-value {
background: var(--purple);
background-attachment: fixed;
}
progress::-moz-progress-bar {
background: var(--purple);
background-attachment: fixed;
}
.spacer {
height: 1000vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/ScrollTrigger.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<body>
<header class="header">Header</header>
<section class="postHeroImage" id="startProgressBar">
<progress class="postProgressBar" max="100" value="0"></progress>
<div class="container">
<div class="row">
<div class="col-12">
<div class="postHeroImage__bg" style="background-image: url( 'https://picsum.photos/200/300' );" loading="lazy"></div>
</div>
</div>
</div>
</section>
<div class="spacer">lorum ipsum</div>
</body>
Current issues:
.postProgressBar
doesn't become fixed
(can't see fixed
inline style in inspect mode).postProgressBar
is showing progress that isn't accurate based on the amount of .spacer
there is left to scroll.Upvotes: 3
Views: 2186
Reputation: 18398
start
and end
properties of the scrollTrigger
. This will tell the gsap when to start animating and when to end. For end we need to add 64px, the height of the header, to the scrolling cos our container is 64px below from top of the viewport.
$(function() {
//read the css variable
let bodyStyles = window.getComputedStyle(document.body);
let headerHeight = bodyStyles.getPropertyValue('--header-height');
gsap.registerPlugin(ScrollTrigger);
gsap.to('progress', {
value: 100,
ease: 'none',
scrollTrigger: {
trigger: "#startProgressBar",
scrub: 0.3,
start: 'start 0px',
end: 'bottom' + headerHeight,
markers: true,
}
});
});
body {
background-color: lightblue;
--white: wheat;
--grey: #002A54;
--purple: #5D209F;
/* 40px padding top and bottom + 24px line height*/
--header-height: 64px;
}
.header {
/* position: absolute;*/
position: sticky;
top: 0;
width: 100%;
padding: 20px 15px;
z-index: 9999;
background-color: var(--white);
}
.postHeroImage {
padding: 134px 0 0 0;
margin-bottom: 105px;
position: relative;
}
.postHeroImage__bg {
background-size: cover;
background-repeat: no-repeat;
width: 100%;
min-height: 400px;
}
progress {
position: sticky;
top: var(--header-height);
left: 0;
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 15px;
border: none;
background: transparent;
/*z-index: 9999;*/
}
progress::-webkit-progress-bar {
background: transparent;
}
progress::-webkit-progress-value {
background: var(--purple);
background-attachment: fixed;
}
progress::-moz-progress-bar {
background: var(--purple);
background-attachment: fixed;
}
.spacer {
height: 1000vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/ScrollTrigger.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<header class="header">Header</header>
<section class="postHeroImage" id="startProgressBar">
<progress class="postProgressBar" max="100" value="0"></progress>
<div class="container">
<div class="row">
<div class="col-12">
<div class="postHeroImage__bg" style="background-image: url( 'https://picsum.photos/id/705/300/200' );" loading="lazy"></div>
</div>
</div>
</div>
</section>
<div class="spacer">lorum ipsum</div>
trigger
to '.container'
.
Solution without sticky Here I've used position fixed:
$(function() {
//read the css variable
let bodyStyles = window.getComputedStyle(document.body);
let headerHeight = bodyStyles.getPropertyValue('--header-height');
gsap.registerPlugin(ScrollTrigger);
var action = gsap.set('.postProgressBar', {
position: 'fixed',
paused: true
});
ScrollTrigger.create({
trigger: '.postProgressBar',
start: 'top 64px',
onEnter: () => action.play(),
onLeaveBack: () => action.reverse(),
});
gsap.to('progress', {
value: 100,
ease: 'none',
scrollTrigger: {
trigger: "#mainContent",
scrub: 0.3,
end: 'bottom 110%',
markers: false,
}
});
});
* {
margin: 0;
padding: 0;
}
body {
background-color: lightblue;
--white: wheat;
--grey: #002A54;
--purple: #5D209F;
/* 40px padding top and bottom + 24px line height*/
--header-height: 64px;
position: relative;
height:100%
}
.header {
position: fixed;
top: 0;
width: 100%;
padding: 20px 15px;
z-index: 9999;
background-color: var(--white);
}
.postHeroImage {
padding: 134px 0 0 0;
margin-bottom: 105px;
position: relative;
margin-top: var(--header-height);
}
.postHeroImage__bg {
background-size: cover;
background-repeat: no-repeat;
width: 100%;
min-height: 400px;
}
progress {
position: static;
top: var(--header-height);
left: 0px;
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 15px;
border: none;
background: transparent;
/*z-index: 9999;*/
}
progress::-webkit-progress-bar {
background: transparent;
}
progress::-webkit-progress-value {
background: var(--purple);
background-attachment: fixed;
}
progress::-moz-progress-bar {
background: var(--purple);
background-attachment: fixed;
}
.spacer {
height: 1000vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.0/ScrollTrigger.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<header class="header">Header</header>
<section class="postHeroImage" id="startProgressBar">
<div class="container">
<div class="row">
<div class="col-12">
<div class="postHeroImage__bg" style="background-image: url( 'https://picsum.photos/id/705/300/200' );" loading="lazy"></div>
</div>
</div>
</div>
<progress class="postProgressBar" max="100" value="0"></progress>
</section>
<div id="mainContent" class="spacer">lorum ipsum</div>
Upvotes: 2
Reputation: 5419
Instead of the library that you use, you could simply use the HTML tag <progress>
(your library is using the same).
Here is a simple way of how you can do it:
const scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
window.addEventListener('scroll', (e) => document.querySelector('progress').value = window.pageYOffset / (scrollHeight - window.innerHeight) * 100)
html, body {
padding: 0;
margin: 0;
height: 2500px;
}
progress {
position: fixed;
border-radius: 0;
width: 100%;
height: 22px;
}
progress::-webkit-progress-value {
background-color: #5D209F;
}
<progress value="0" max="100"></progress>
Every time the user scrolls the page, the progress gets a value, which is current height position / (document's height - active screen height) * 100.
Upvotes: 1