// update the background when scrolling
window.addEventListener("scroll", updateBackgroundColor);
const wrapper = document.querySelector("body");
const clock = document.querySelector(".clock");
function updateBackgroundColor(){
const wrapperRect = wrapper.getBoundingClientRect();
const clockRect = clock.getBoundingClientRect();
// in this case we only care about top and bottom, because the gradient is a straight 180°.
// so we get the relative position inside the outer relevant element
const relativePosition = {
top: (clockRect.top - wrapperRect.top) / wrapperRect.height,
bottom: (clockRect.top + clockRect.height - wrapperRect.top) / wrapperRect.height,
}
// since the gradient only starts at 25% down, we need to adjust the calculation to use those relative values instead
const startPercentage = 0.25;
// this math is just a fancy way of saying "(top - 0.25) * (4/3)" because that is what scales the values for us to the desired range.
relativePosition.top = (relativePosition.top - startPercentage) * ((1 / startPercentage) / ((1 - startPercentage) / startPercentage));
relativePosition.bottom = (relativePosition.bottom - startPercentage) * ((1 / startPercentage) / ((1 - startPercentage) / startPercentage));
// if value is negative, set to 0
if(relativePosition.top < 0) relativePosition.top = 0;
if(relativePosition.bottom < 0) relativePosition.bottom = 0;
let newTopColor = getGradientColor({r:42, g:84, b:112} /*#2a5470*/, {r:76, g:65, b:119} /*#4c4177*/, relativePosition.top);
let newBottomColor = getGradientColor({r:42, g:84, b:112} /*#2a5470*/, {r:76, g:65, b:119} /*#4c4177*/, relativePosition.bottom);
clock.style.background = `linear-gradient(180deg, rgb(${newTopColor.r}, ${newTopColor.g}, ${newTopColor.b}) 0%, rgb(${newBottomColor.r}, ${newBottomColor.g}, ${newBottomColor.b}) 100%)`;
}
// returns the linearly interpolated gradient color value between two colors
// colors are represented as {r:, g:, b:} objects for simplicity
function getGradientColor(color1, color2, percentage){
return {
r: color1.r + ((color2.r - color1.r) * percentage),
g: color1.g + ((color2.g - color1.g) * percentage),
b: color1.b + ((color2.b - color1.b) * percentage),
}
}
// run once so it works immediately not just after the first scrolling
updateBackgroundColor();
body {
margin: 0;
padding: 0;
background-repeat: no-repeat;
min-height: 200vh;
}
body, .clock {
background: linear-gradient(180deg, #2a5470 25%, #4c4177 100%);
}
.border {
padding: 10px;
border-radius: 20px;
background-image: linear-gradient(25deg, #2a5470 25%, #4c4177 100%);
max-width: 200px;
margin: 50px auto 0;
position: sticky;
top: 10px;
}
.clock {
display: flex;
flex-direction: column;
align-items: center;
border-radius: 20px;
}
h2, .clock div {
color: #fff;
}
.clock div {
font-size: 50px;
}
<!DOCTYPE html>
<html>
<body>
<div class="border">
<div class="clock">
<h2>Break/Session</h2>
<div>25:00</div>
</div>
</div>
</body>
</html>