Scroll duration for behaviour = smooth in different browsers
Asked Answered
G

2

7

I'm want to use the window.scrollTo function with smooth behaviour. For example:

window.scrollTo({ top: 0, behavior: 'smooth' })

Parallel I want to change an elements color. For this I could use the scroll event to calculate the color for the current scroll position. But this would result in a bad performance because the scroll callback will called to often. The better solution would be to start a transition at the same time. But for this I have to know the scroll duration. Since it's not possible to define it manually, I need to know which duration the browsers uses.

Giuseppinagiustina answered 30/10, 2020 at 15:35 Comment(7)
I am not sure for which elements you want to change the color. I understand that calculates the color using the JS code may cause a performance issue. I suggest you can refer to this example on which page is divided into several sections and the static color value assigned to the CSS class. On Window.scroll() we can change the class as per the scroll value. You can try to refer to this example and let us know whether it can fulfill your requirements or not.Motion
Sorry but I don't see there is an automatic scrolling on this page.Giuseppinagiustina
please check the code sample in the answer. It can help to automatically scroll the page and change the colors.Motion
I think that the Intersection Observer API was created for use cases like this one.Electronarcosis
No it has nothing to do with IntersectionObserver. The question is only how long the scroll duration is to change the color of an element with the same duration. But I assume there is no solution.Giuseppinagiustina
You should make a scroll listener with a debounce; it would only run logic every x seconds, so a lot less of performance hit.Englacial
No @MarsAndBack, why should I register a scroll event? Not to register and use it is even more performant as debounce an event I never need. ;-)Giuseppinagiustina
W
5

The specification says

The scrolling box is scrolled in a smooth fashion using a user-agent-defined timing function over a user-agent-defined period of time. User agents should follow platform conventions, if any.

Right away that makes it a complicated question to which there is likely no answer, or at least no answer which will reliably stay put.

Browsers might

  • Aim for a target speed rather than duration, and so scroll for a longer period of time if there are several pages to scroll
  • Adjust the scrolling behaviour if many nested scrolling panels are scrolling at once, such as sequencing them one after another (I see some code in Chromium which might be doing something like this)
  • Allow it to be user-configurable, so people with poor vision or motion sickness can tweak it to their liking, or disable it
  • Defer to the operating system, which might have its own quirks and customizations
  • Change any of this with no notice, or the underlying OS might

Here is an excerpt of a comment in some Firefox code to do with smooth scrolling. I did not dig in to whether this is actually strictly relevant to the sorts of scroll you are doing, but it gives an idea:

* |Smooth| scrolls have a symmetrical acceleration and deceleration curve
* modeled with a set of splines that guarantee that the destination will be
* reached over a fixed time interval.  |Smooth| will only be smooth if smooth
* scrolling is actually enabled.  This behavior is utilized by keyboard and
* mouse wheel scrolling events.
*
* |SmoothMsd| implements a physically based model that approximates the
* behavior of a mass-spring-damper system.  |SmoothMsd| scrolls have a
* non-symmetrical acceleration and deceleration curve, can potentially
* overshoot the destination on intermediate frames, and complete over a
* variable time interval.  |SmoothMsd| will only be smooth if cssom-view
* smooth-scrolling is enabled.

And here is a little bit of code you can use to test for yourself. In my experimentation I'm seeing the duration varying based on how far it scrolls, both in Firefox and Chromium, and I'm seeing different speeds in each of those.

const qs = document.querySelector.bind(document);

const viewportHeightInput = qs("#viewport-height");
const contentHeightInput = qs("#content-height");
const viewport = qs("#viewport");
const content = qs("#content");
const output = qs("#output");

function update() {
  viewport.style.height = `${viewportHeightInput.value}px`;
  content.style.height = `${contentHeightInput.value}px`;
}
update();

viewportHeightInput.addEventListener("input", update);
contentHeightInput.addEventListener("input", update);

qs("#to-top").addEventListener("click", () => {
  start = performance.now();
  scrollEvents = 0;
  updateScrollEvents();
  viewport.scrollTo({
    behavior: "smooth",
    top: 0,
  })
});
qs("#to-bottom").addEventListener("click", () => {
  start = performance.now();
  scrollEvents = 0;
  updateScrollEvents();
  viewport.scrollTo({
    behavior: "smooth",
    top: viewport.scrollHeight - viewport.clientHeight,
  })
});

const scrollEventsOutput = qs("#scroll-events");
const elapsedOutput = qs("#elapsed");
let scrollEvents = 0;
let start = performance.now();
let last = null;
viewport.addEventListener("scroll", () => {
  last = performance.now();
  scrollEvents++;
  updateScrollEvents();
});

function updateScrollEvents() {
  scrollEventsOutput.value = scrollEvents;
  elapsedOutput.value = last == null ? 0 : last - start;
}
#controls {
  display: grid;
  grid-template-columns: 10rem 1fr;
}

#controls fieldset {
  display: contents;
}

#viewport {
  overflow: auto;
  border: thick solid orange;
  margin: 4rem 0;
}

#content {
  background-image: linear-gradient(to bottom left, black, white);
  position: relative;
}

#content::before,
#content::after {
  position: absolute;
  left: 0;
  background-color: black;
  color: white;
  display: block;
}

#content::before {
  content: "start";
  top: 0;
}

#content::after {
  content: "end";
  bottom: 0;
}
<div id="controls">
  <fieldset>
    <label for="viewport-height">Viewport height</label>
    <input id="viewport-height" type="number" min="1" value="400">
  </fieldset>
  <fieldset>
    <label for="content-height">Content height</label>
    <input id="content-height" type="number" min="1" value="1000">
  </fieldset>
  <fieldset>
    <label>Triggers</label>
    <ul>
      <li><button id="to-top">Scroll to top</button></li>
      <li><button id="to-bottom">Scroll to bottom</button></li>
    </ul>
  </fieldset>
  <fieldset>
    <label for="scroll-events">Scroll events</label>
    <input id="scroll-events" type="number" readonly value="0">
  </fieldset>
  <fieldset>
    <label for="elapsed">Milliseconds between start of scroll and last scroll event</label>
    <input id="elapsed" type="number" readonly value="0">
  </fieldset>
</div>

<div id="viewport">
  <div id="content"></div>
</div>
Weisman answered 10/3, 2022 at 0:21 Comment(0)
M
0

The code example I shared in the comment is just for reference. You further need to modify the code as per your own requirement for scrolling the page.

Here, I have tried to modify that sample code to scroll the page.

$(window).scroll(function() {
  
  // selectors
  var $window = $(window),
      $body = $('body'),
      $panel = $('.panel');
  
  // Change 33% earlier than scroll position so colour is there when you arrive.
  var scroll = $window.scrollTop() + ($window.height() / 3);
 
  $panel.each(function () {
    var $this = $(this);
    
    // if position is within range of this panel.
    // So position of (position of top of div <= scroll position) && (position of bottom of div > scroll position).
    // Remember we set the scroll to 33% earlier in scroll var.
    if ($this.position().top <= scroll && $this.position().top + $this.height() > scroll) {
          
      // Remove all classes on body with color-
      $body.removeClass(function (index, css) {
        return (css.match (/(^|\s)color-\S+/g) || []).join(' ');
      });
       
      // Add class of currently active div
      $body.addClass('color-' + $(this).data('color'));
    }
  });    
  
}).scroll();

$(document).ready(function(){
  $("button").click(function(){
     $("html, body").animate({
      scrollTop: $("#bottom").offset().top - 220
    }, 8000);
  // document.getElementById('bottom').scrollIntoView({behavior: "smooth"});
  });
});
/* Setting fade transition and default settings */
body {
  color: #000;
  background-color: #f4f4f4;
  transition: background-color 1s ease;
}

/* panel styles */
.panel {
  /* min height incase content is higher than window height */
  min-height: 100vh;
  display: flex;
  justify-content: space-around;
  align-items: center;
  font-family: sans-serif;
  /* outline: 10px solid hotpink; */
  /* turn above on to see the edge of panels */
}

/* colours */
.color-violet {
  background-color: #7A4EAB;
}
.color-indigo {
  background-color: #4332CF;
}
.color-blue {
  background-color: #2F8FED;
}
.color-green {
  background-color: #4DCF42;
}
.color-yellow {
  background-color: #FAEB33;
}
.color-orange {
  background-color: #F19031;
}
.color-red {
  background-color: #F2293A;
}

/* styling for demo, can ignore */
body {
  text-align: center;
  font-size: 120%;
  line-height: 1.618;
}
h1, h2 {
  font-size: 3em;
  letter-spacing: -0.05em;
  line-height: 1.1;
}
p {
  max-width: 30em;
  margin-bottom: 1.618em;
}
a {
  color: #4332CF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>


<div class="panel" data-color="white">


    <div>
            <h1>scrolling example</h1>
      <div><button>Click me to scroll to bottom</button></div>
  </div>
</div>
<div class="panel" data-color="violet">
  <h2>Violet panel</h2>
</div>
<div class="panel" data-color="indigo">
  <h2>Indigo panel</h2>
</div>
<div class="panel" data-color="blue">
  <h2>Blue panel</h2>
</div>
<div class="panel" data-color="green">
  <h2>Green panel</h2>
</div>
<div class="panel" data-color="yellow">
  <h2>Yellow panel</h2>
</div>
<div class="panel" data-color="orange">
  <h2>Orange panel</h2>
</div>
<div class="panel" data-color="red">
  <h2>Red panel</h2>
</div>
<div id="bottom"></div>

Output in the MS Edge Chromium browser:

enter image description here

Further, you can try to modify the code sample as per your requirements.

Reference:

Code example link

Motion answered 3/11, 2020 at 6:8 Comment(4)
What you have done there is to use the scroll event to modify the color. That's what I mean what results in a bad performance, because the event is fired with each scrolling step.I don't need the event because in my case the user doesn't scroll manually to trigger the animation. I have only a button where the user clicks whereupon the page scrolls to a target. In parallel there is a CSS transition which transforms a color to a new value. Optimally the transitions duration is the same as the scrolling duration. But I don't know the scroll duration and that's my only one problem.Giuseppinagiustina
Currently I use a duration of 500ms. It looks like this is the same duration, but I'm not sure. I only have to know which duration the browsers uses for scroll duration when used JavaScript with the smooth scrolling behaviour.Giuseppinagiustina
So for clarification, what I need is only a list of durations like: Firefox 500ms, Opera 800ms, Chrome 500ms, ... Or whatever the values are.Giuseppinagiustina
Yes, this info would be very useful. Further, having control over duration would be the best!Englacial

© 2022 - 2024 — McMap. All rights reserved.