17

Is there any way to change background color dynamically on scroll?

For example, refer this site(https://www.samsung.com/sec/smartphones/galaxy-note9/)

When you first access that site, background color is blue.

While scroll down, it's color change to black smoothly.

Also see this site(codepen.io/Funsella/pen/yLfAG/)

Second site is same with first. But it's color changed at once.

But first site's color is not change at once.

It changed gradually related to scroll position.

body {
  height: 100vh;
}
.section1 {
  background-color: white;
  height: 100%;
}
.section2 {
  background: linear-gradient(#f05fa6, #ed1654);
  height: 100%;
}
<html>
<body>
  <section class="section1">
    SECTION1
  </section>
  <section class="section2">
    SECTION2
  </section>
</body>
</html>

Above code is what I'm worked on.

Current it's color is split by each section.

When I scroll down, I want to change color background-color: white -> background: linear-gradient(#f05fa6, #ed1654)

Is there any solution about this?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Hide
  • 3,199
  • 7
  • 41
  • 83

5 Answers5

20

You need to smoothly interpolate the colors by taking into account the page's scroll offset (window.scrollY, or window.pageYOffset on older browsers).

The Samsung site is transitioning a solid color instead of a gradient, which is a bit simpler.

Like this (see CodePen):

const [red, green, blue] = [69, 111, 225]
const section1 = document.querySelector('.section1')

window.addEventListener('scroll', () => {
  let y = 1 + (window.scrollY || window.pageYOffset) / 150
  y = y < 1 ? 1 : y // ensure y is always >= 1 (due to Safari's elastic scroll)
  const [r, g, b] = [red/y, green/y, blue/y].map(Math.round)
  section1.style.backgroundColor = `rgb(${r}, ${g}, ${b})`
})

You can apply the same logic to the gradient colors.

atomiks
  • 684
  • 6
  • 11
  • 1
    Works perfectly. In this line, `const y = 1 + (window.scrollY || window.pageYOffset) / 150`, Why add `1` and divide with `150`? – Hide Oct 04 '18 at 03:06
  • If you don't use `1 + ` then the color won't start at `rgb(69, 111, 225)`, it goes to pure white at the top of the page. If you do the math you will see why. The `150` is just a random-ish number I chose - a constant that can be changed based on how rapidly you want the color to transition. – atomiks Oct 04 '18 at 03:10
  • I got it. Anyway, it doesn't work on safari. So I added `section1.style.webkitBackgroundColor` and `section1.style.mozBackgroundColor`. But it doesn't work. What property should I have to use? – Hide Oct 04 '18 at 07:08
  • Which Safari? The code I posted is ES2015 which will work on browsers released from around late 2015 onwards. If you're using a gradient, then it's `style.background` instead of `style.backgroundColor`. – atomiks Oct 04 '18 at 08:31
  • I access the site that you linked, it doesn't work. Also I use OSX(iMac 2017). Chrome works fine. – Hide Oct 04 '18 at 08:33
  • I found the cause - Safari doesn't like decimals in `rgb()`. You need to round the values. I've updated the answer. – atomiks Oct 04 '18 at 08:41
  • I have one more question. Differently with my code, you only use one `
    `. Is there any way to implement with multiple `
    `?
    – Hide Oct 04 '18 at 08:50
  • Because I have so many `
    ` and not only change color blue -> black, but also change multiple color like this(`blue->black->red->white-> ...`)
    – Hide Oct 04 '18 at 08:51
  • That's quite a bit trickier, but remember that you can do anything with the `rgb()` CSS function and the numbers 0-255 inside it as arguments. If you want to transition to red, you'll want to make the first argument (r) closer to 255 than the others. – atomiks Oct 04 '18 at 08:54
  • Can I change color to white instead of current color(black)? I modify your source, but it doesn't work properly. What point I have to modify? – Hide Oct 08 '18 at 07:52
  • It's just math: dividing red (69) by `y` which is 1 when scroll = 0px, 2 when scroll = 150px, etc: `red/y`. So you'll probably want to multiply all colors instead: `red * y` for white. – atomiks Oct 09 '18 at 02:11
  • How would it be to background color change from blue to pink? and not black – FilipeOS Oct 16 '20 at 15:28
  • You might want to [debounce](https://stackoverflow.com/questions/12009367/javascript-event-handling-scroll-event-with-a-delay) this a bit. – ggorlen Aug 25 '22 at 21:38
4

I think you need to use the "transition" property of CSS.

body {
  background: green;
  transition: 0.3s all;
}

Then you can add, remove elements along with change color:

$(function() {
$(window).scroll(function () {
   if ($(this).scrollTop() > 50) {
      $(‘body’).addClass(‘colorChange’)
      $(‘header’).addClass(‘displayNone’)
      $(‘nav’).removeClass(‘navBackgroundStart’)
      $(‘nav ul’).addClass(‘addBlackBackground’)
   } 
   if ($(this).scrollTop() < 50) {
      $(‘body’).removeClass(‘colorChange’)
      $(‘header’).removeClass(‘displayNone’)
      $(‘nav’).addClass(‘navBackgroundStart’)
      $(‘nav ul’).removeClass(‘addBlackBackground’)
   } 
});
});
heyitsvajid
  • 1,023
  • 1
  • 10
  • 19
4

I tried to use the solution of atomiks with a custom end color but it was too difficult. I found myself a better solution by using chroma.js

You need to generate a scale with your two colors (or more) :

var scale = chroma.scale(['#1abc9c', '#e74c3c']).domain([0, $(document).height()]);
$(window).on('scroll', function () {
    $('.section').css('background-color', scale(window.pageYOffset));
});

Here I create a scale with the color I want and then I add a custom domain so that my scale can use the offset position which can go from 0 (top of the page) 3600 (bottom of my page). Or, you can try to get the scroll position value between 0 and 1 with some math.

Then, when we scroll, we can use our scale with the current scroll position. It will generate a RGBA color between our two colors before reaching the last one at the bottom of the page #e74c3c

Baptiste
  • 1,688
  • 1
  • 15
  • 25
  • This works. Chroma.js calculates all the possible color values between two colors/positions. You set the color of your element to the color chroma gives you at each scroll position, as per above answer, – Tom Apr 11 '22 at 16:26
2

This jQuery code changes the background color of the body.

$(document).ready(function() {
    var scroll_pos = 0;
    $(document).scroll(function() {
        scroll_pos = $(this).scrollTop();
        if(scroll_pos > 300) {
            $("body").css('background-color', 'blue');
        } else {
            $("body").css('background-color', 'red');
        }
    });
});

Don't forget to add the transition CSS to get that effect you see in your example.

body {
  -webkit-transition: all 0.5s ease;
  -moz-transition: all 0.5s ease;
  -o-transition: all 0.5s ease;
  transition: all 0.5s ease;
}

Make the backgrounds of your section classes transparent.

.section1, section2 {
  background-color: transparent;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sebastian
  • 96
  • 10
  • Not a same with my purpose. It changed like second site. But I want to change color like first site. – Hide Oct 04 '18 at 02:51
0

This effect can be gotten with a bit of JavaScript

You first create a div with some content in it. You create a box that is smaller than the content and set the overflow property to scroll.

With a tiny bit of JavaScript you add the functionality. You first create an event Listener than triggers a function when the scroll effect is used. When that event Listener triggers you then dynamically set the background color to your requirements.

document.getElementById("myDIV").addEventListener("scroll", myFunction);

function myFunction() {
  document.getElementById("myDIV").style.backgroundColor = "red";
}
div {
  border: 1px solid black;
  width: 400px;
  height: 400px;
  overflow: scroll;
  transition: all 0.5s ease;
}
<p>Try the scrollbar in the div</p>
<div id="myDIV">Why do we use it?
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
<br><br>
Where does it come from?
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32.</div>
Neil Meyer
  • 473
  • 4
  • 15