I am trying to implement a slip-scroll
parallax
type effect using CSS
and JavaScript
. When the screen scrolls down, the logo in the top right will change it's background to contrast with what is below. There is also a nav button on the left that does the same.
I have this so far...
// Detect request animation frame
var scroll = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame
// IE Fallback, you can even fallback to onscroll
||
function(callback) {
window.setTimeout(callback, 1000 / 60)
};
var lastPosition = -1;
// my Variables
var lastSection = false;
var replaceItemTop = -1;
var replaceItemBottom = -1;
var replaceItemHeight = -1;
// The Scroll Function
function loop() {
var top = window.pageYOffset;
// my variables
// my sections to calculate stuff
var sections = document.querySelectorAll('.section');
var replaceContainer = document.querySelectorAll('.js-replace');
var replaceItem = document.querySelectorAll('.js-replace__item');
if (replaceItem.length > 0) {
// get top position of item from container, because image might not have loaded
replaceItemTop = parseInt(replaceContainer[0].getBoundingClientRect().top);
replaceItemHeight = replaceItem[0].offsetHeight;
replaceItemBottom = replaceItemTop + replaceItemHeight;
}
var sectionTop = -1;
var sectionBottom = -1;
var currentSection = -1;
// Fire when needed
if (lastPosition == window.pageYOffset) {
scroll(loop);
return false;
} else {
lastPosition = window.pageYOffset;
// Your Function
Array.prototype.forEach.call(sections, function(el, i) {
sectionTop = parseInt(el.getBoundingClientRect().top);
sectionBottom = parseInt(el.getBoundingClientRect().bottom);
// active section
if ((sectionTop <= replaceItemBottom) && (sectionBottom > replaceItemTop)) {
// check if current section has bg
currentSection = el.classList.contains('section--bg');
// switch class depending on background image
if (currentSection) {
replaceContainer[0].classList.remove('js-replace--reverse');
} else {
replaceContainer[0].classList.add('js-replace--reverse')
}
}
// end active section
// if active Section hits replace area
if ((replaceItemTop < sectionTop) && (sectionTop <= replaceItemBottom)) {
// animate only, if section background changed
if (currentSection != lastSection) {
document.documentElement.style.setProperty('--replace-offset', 100 / replaceItemHeight * parseInt(sectionTop - replaceItemTop) + '%');
}
}
// end active section in replace area
// if section above replace area
if (replaceItemTop >= sectionTop) {
// set offset to 0 if you scroll too fast
document.documentElement.style.setProperty('--replace-offset', 0 + '%');
// set last section to current section
lastSection = currentSection;
}
});
}
// Recall the loop
scroll(loop)
}
// Call the loop for the first time
loop();
window.onresize = function(event) {
loop();
};
/* variables */
:root {
/* this value is going to be changed by javascript */
--replace-offset: 50%;
--replace-offset-2: calc((100% - var(--replace-offset)) * -1)
}
a {
text-decoration: none;
}
/* set image position */
img {
vertical-align: bottom;
}
.footer {
background-color: black;
color: white;
padding-top: 50px;
padding-bottom: 50px;
text-align: center;
}
/* without fixed header this makes no sense */
.header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
}
.header_nav {
position: fixed;
top: 50%;
transform: translateY(-50%);
left: 0;
z-index: 9;
}
.logo {
background-color: white;
display: inline-block;
border: solid;
padding: 10px;
border-radius: 10px;
font-size: 2em;
}
.logo a {
color: black;
}
.logo--invert {
background-color: black;
color: white;
border-color: white;
}
.logo--invert a {
color: white;
}
.sidelogo {
background-color: white;
display: inline-block;
border: solid;
padding: 10px;
border-radius: 10px;
font-size: 2em;
}
.sidelogo a {
color: black;
}
.sidelogo--invert {
background-color: black;
color: white;
border-color: white;
}
.sidelogo--invert a {
color: white;
}
.section {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
section--1 {
height: 100vh;
width: 100%;
}
.hero {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url("https://www.w3schools.com/howto/photographer.jpg");
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.section--2 {
background: white;
}
.section--3 {
background: black;
color: white;
}
/**
This is the interesting part
**/
/* align content above each other without absolute */
.js-replace {
display: grid;
}
.js-replace__item {
grid-row: -1 / 1;
grid-column: -1 / 1;
overflow: hidden;
will-change: transform;
}
/* item to replace with */
.js-replace__item {
transform: translateY(calc(var(--replace-offset) * 1));
}
.js-replace__content {
/* fixes problem with calculating correct height in js */
border: 1px solid transparent;
will-change: transform;
transform: translateY(calc(var(--replace-offset) * -1));
}
/* previous replace item*/
.js-replace__item--active {
transform: translateY(calc(var(--replace-offset-2) * 1));
}
.js-replace__item--active .js-replace__content {
transform: translateY(calc(var(--replace-offset-2) * -1));
}
/* REVERSE ANIMATION */
.js-replace--reverse .js-replace__item {
transform: translateY(calc(var(--replace-offset-2) * 1));
}
.js-replace--reverse .js-replace__content {
transform: translateY(calc(var(--replace-offset-2) * -1));
}
/* previous replace item*/
.js-replace--reverse .js-replace__item--active {
transform: translateY(calc(var(--replace-offset) * 1));
}
.js-replace--reverse .js-replace__item--active .js-replace__content {
transform: translateY(calc(var(--replace-offset) * -1));
}
<body class="body">
<div class="header">
<!-- replace content -->
<div class="header__logo js-replace">
<!-- item to replace -->
<div class="js-replace__item js-replace__item--active">
<div class="js-replace__content">
<div class="logo"><a href="#">Logo</a></div>
</div>
</div>
<!-- end item to replace -->
<!-- item to replace with -->
<div class="js-replace__item">
<div class="js-replace__content">
<div class="logo logo--invert"><a href="#">Logo</a></div>
</div>
</div>
<!-- end item to replace with -->
</div>
<!-- end replace content -->
</div>
<div class="header_nav">
<!-- replace content -->
<div class="header__side js-replace">
<!-- item to replace -->
<div class="js-replace__item js-replace__item--active">
<div class="js-replace__content">
<div class="sidelogo"><a href="#">Nav</a></div>
</div>
</div>
<!-- end item to replace -->
<!-- item to replace with -->
<div class="js-replace__item">
<div class="js-replace__content">
<div class="sidelogo sidelogo--invert"><a href="#">Nav</a></div>
</div>
</div>
<!-- end item to replace with -->
</div>
<!-- end replace content -->
</div>
<main class="main">
<section class="section--1 section">
<div class="hero">
</div>
</section>
<section class="section--2 section section--bg">
<h1>Section 2</h1>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis
tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan
porttitor, facilisis luctus, metus</p>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
Mauris placerat eleifend leo.</p>
</section>
<section class="section--3 section">
<h1>Section 3</h1>
<p>
<strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci,
sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.
</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus
turpis elit sit amet quam. Vivamus pretium ornare est.</p>
</blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
</section>
<section class="section--4 section section--bg">
<h1>Section 4</h1>
<p>
Some great section 4 content
</p>
<ul>
<li>This is a list item</li>
<li>This is a list item</li>
<li>This is a list item</li>
<li>This is a list item</li>
<li>This is a list item</li>
</ul>
</section>
<section class="section--5 section section--bg">
<h1 class="section__title">
Section 5
</h1>
<p>
This is some random content for section 5
</p>
</section>
</main>
<footer class="footer section">
<form action="#" method="post">
<div>
<label for="name">Text Input:</label>
<input type="text" name="name" id="name" value="" tabindex="1" />
</div>
<div>
<h4>Radio Button Choice</h4>
<label for="radio-choice-1">Choice 1</label>
<input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" />
<label for="radio-choice-2">Choice 2</label>
<input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" />
</div>
<div>
<label for="select-choice">Select Dropdown Choice:</label>
<select name="select-choice" id="select-choice">
<option value="Choice 1">Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
</select>
</div>
<div>
<label for="textarea">Textarea:</label>
<textarea cols="40" rows="8" name="textarea" id="textarea"></textarea>
</div>
<div>
<label for="checkbox">Checkbox:</label>
<input type="checkbox" name="checkbox" id="checkbox" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
</footer>
</body>
The Logo on the right is behaving as expected, the background changes as you scroll down the page.
However, the nav on the left is not working correctly, I think it needs some offset adding as currently it changes when the nav changes
Anyone any ideas where I am going wrong?
If you're working on a web development project, setting a nice background color can give the website a more enticing look. To set a background color for a div or related element in CSS, you use the background-color property. While setting this background color, your creativity is your limit as to how far you want to go.
Right now it's just the default white. You could either style it red via CSS like this: #left-panel {background-color: red} (preferred), or you could set it initially in the JavaScript by duplicating the following line at the top of your document.ready function: $ ("#left-panel").css ('background-color', 'red');.
How TO - Change Background on Scroll 1 Step 1) Add HTML:#N#Example#N#<div class="bg-image img1"></div>#N#<div class="bg-image img2"></div>#N#<div class="bg-image... More ...
The second div has a much better contrast ratio between the background color and the color of the text . Thus, it is more accessible and clearer for people to read. In this article, we saw how you can change the background-color of a div.
Considering your code you will need to duplicate your logic in order to make it working for both elements. You will need to adjust the top
variable for the second element so it's no more the top of the screen but the top of screen + the offset of the element.
Here is another idea that rely more on CSS with less JS code to obtain almost the same result:
window.onscroll = function() {
var scroll = window.scrollY || window.scrollTop || document.getElementsByTagName("html")[0].scrollTop;
document.documentElement.style.setProperty('--scroll-var', scroll+"px");
}
:root {
--scroll-var: 0px;
}
a {
text-decoration: none;
}
/* set image position */
img {
vertical-align: bottom;
}
.footer {
background-color: #fff;
color: #000;
padding-top: 50px;
padding-bottom: 50px;
text-align: center;
}
/* without fixed header this makes no sense */
.header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
}
.header_nav {
position: fixed;
top: 50%;
transform: translateY(-50%);
left: 0;
z-index: 9;
}
.logo,
.sidelogo{
background:
repeating-linear-gradient(to bottom,
#fff 0,#fff 100vh,
#000 100vh,#000 200vh) top/100% 600vh no-repeat padding-box,
repeating-linear-gradient(to bottom,
#000 0,#000 100vh,
#fff 100vh,#fff 200vh) top/100% 600vh no-repeat border-box;
display: inline-block;
border: solid transparent;
border-radius: 10px;
font-size: 2em;
}
.logo a,
.sidelogo a{
display:block;
padding: 10px;
background:
repeating-linear-gradient(to bottom,
#000 0,#000 100vh,
#fff 100vh,#fff 200vh) top/100% 600vh no-repeat;
background-clip: text;
color: transparent;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.logo,
.logo a{
background-position:0 calc(-1 * var(--scroll-var));
}
.sidelogo,
.sidelogo a{
background-position:0 calc(-1 * var(--scroll-var) - 50vh + 30px);
}
.section {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
section--1 {
height: 100vh;
width: 100%;
}
.hero {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url("https://www.w3schools.com/howto/photographer.jpg");
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.section--2,
.footer{
background: white;
}
.section--3,
.section--5{
background: black;
color: white;
}
<body class="body">
<div class="header">
<div class="logo"><a href="#">Logo</a></div>
</div>
<div class="header_nav">
<div class="sidelogo"><a href="#">Nav</a></div>
</div>
<main class="main">
<section class="section--1 section">
<div class="hero">
</div>
</section>
<section class="section--2 section section--bg">
<h1>Section 2</h1>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis
</p>
</section>
<section class="section--3 section">
<h1>Section 3</h1>
<p>
<strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci,
sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.
</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
</section>
<section class="section--4 section section--bg">
<h1>Section 4</h1>
<p>
Some great section 4 content
</p>
<ul>
<li>This is a list item</li>
<li>This is a list item</li>
<li>This is a list item</li>
<li>This is a list item</li>
<li>This is a list item</li>
</ul>
</section>
<section class="section--5 section section--bg">
<h1 class="section__title">
Section 5
</h1>
<p>
This is some random content for section 5
</p>
</section>
</main>
<footer class="footer section">
<form action="#" method="post">
<div>
<label for="name">Text Input:</label>
<input type="text" name="name" id="name" value="" tabindex="1" />
</div>
<div>
<h4>Radio Button Choice</h4>
<label for="radio-choice-1">Choice 1</label>
<input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" />
<label for="radio-choice-2">Choice 2</label>
<input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" />
</div>
<div>
<label for="select-choice">Select Dropdown Choice:</label>
<select name="select-choice" id="select-choice">
<option value="Choice 1">Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
</select>
</div>
<div>
<label for="textarea">Textarea:</label>
<textarea cols="40" rows="8" name="textarea" id="textarea"></textarea>
</div>
<div>
<label for="checkbox">Checkbox:</label>
<input type="checkbox" name="checkbox" id="checkbox" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
</footer>
</body>
The trick it to rely on a gradient coloration for your elements. The coloration should be the opposite of the site coloration. Our sections are 100vh
height alternating white/black thus we use a gradient with a black/white coloration that we change each 100vh
and that cover all the screen (600vh
in this case since we have 6 sections).
Then we use a simple CSS variable that we update on scroll to define the background position. It's like we make the background sliding inside the element so that it remain fixed relatively to the whole document and we will obtain the needed result.
The drawbacks of this methods:
100vh
to be able to correctly define the gradient. If the section are dynamic we may consider more code to adjust the gradient using more CSS variables.background-clip:text
which isn't well supported in older browsers.Concerning the gradient, I used repeating-linear-gradient
because I adjusted the section to have a perfect alternation between white and black but in case we don't have this we can consider a linear-gradient
where we define the color of each section like we want.
Here is a more generic example (I removed the gradient for the border coloration to make it easy)
window.onscroll = function() {
var scroll = window.scrollY || window.scrollTop || document.getElementsByTagName("html")[0].scrollTop;
document.documentElement.style.setProperty('--scroll-var', scroll + "px");
}
:root {
--scroll-var: 0px;
}
body {
margin:0;
}
.header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
}
.header_nav {
position: fixed;
top: 50%;
transform: translateY(-50%);
left: 0;
z-index: 9;
}
.logo,
.sidelogo {
background:
linear-gradient(to bottom,
#fff 0, #fff 100vh,
blue 100vh, blue 300vh,
red 300vh, red 400vh,
#fff 400vh, #fff 500vh,
#000 500vh, #000 600vh)
top/100% 600vh;
display: inline-block;
border: solid transparent;
border-radius: 10px;
font-size: 2em;
}
.logo a,
.sidelogo a {
display: block;
padding: 10px;
background:
linear-gradient(to bottom,
#000 0, #000 100vh,
red 100vh, red 300vh,
blue 300vh, blue 400vh,
#000 400vh, #000 500vh,
#fff 500vh, #fff 600vh)
top/100% 600vh no-repeat;
background-clip: text;
color: transparent;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.logo,
.logo a {
background-position: 0 calc(-1 * var(--scroll-var));
}
.sidelogo,
.sidelogo a {
background-position: 0 calc(-1 * var(--scroll-var) - 50vh + 30px);
}
.section {
min-height: 100vh;
}
.hero {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-image: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url("https://www.w3schools.com/howto/photographer.jpg");
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
<div class="header">
<div class="logo"><a href="#">Logo</a></div>
</div>
<div class="header_nav">
<div class="sidelogo"><a href="#">Nav</a></div>
</div>
<section class="section">
<div class="hero">
</div>
</section>
<section class="section" style="background:red;">
</section>
<section class="section" style="background:red;">
</section>
<section class="section" style="background:blue;">
</section>
<section class="section" style="background:#000;">
</section>
<section class="section" style="background:#fff;">
</section>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With