自动滚动(如电影演职员表),但用户只需滚动即可接管

autoscroll (like movie credits) but the user can takeover by simply scrolling

我正在考虑创建一个网站,作为音乐剧的数字传单。这个想法是有一个自动滚动的信用列表作为登陆页面。我查看了 codepen 上的示例,了解如何实现这种效果。但我也希望用户可以根据需要进行交互和滚动。当他们停止滚动时,演职员表将变回自动滚动。我没有找到任何解决这个问题的例子。你们中有人知道可以帮助我解决这个问题的脚本(JS 或普通 css…)吗?

最直接的方法是设置一个 requestAnimationFrame() 函数并相应地增加值,然后为其设置滚动位置。 然后添加 wheel 事件来检测用户何时滚动(尽管不要使用 'scroll' 事件,当您更改 body 的 scrollTop 值时它已经被调用),也不要忘记取消 requestAnimationFrame( ) 功能。代码看起来像这样:

let body = document.body,
  starter = document.querySelector("h1"),
  scroll_counter = 0,
  scrolled,
  auto_scroll_kicked = false;

starter.addEventListener("click", start_scrolling);

function start_scrolling() {
  auto_scroll_kicked = true;
  body.offsetHeight > scroll_counter
    ? (scroll_counter += 1.12)
    : (scroll_counter = body.offsetHeight);
  document.documentElement.scrollTop = scroll_counter;
  scroller = window.requestAnimationFrame(start_scrolling);

  if (scroll_counter >= body.offsetHeight) {
    window.cancelAnimationFrame(scroller);
  }
}
window.addEventListener("wheel", (e) => {
  if (auto_scroll_kicked) {
    window.cancelAnimationFrame(scroller);
    scroll_counter = 0;
  }
});

如果您愿意,可以使用 codepen: https://codepen.io/SaltyMedStudent/pen/QWqVwaR?editors=0010

有许多选项可供使用:缓动函数等,但希望现在这些就足够了。

在更改位置之前的自动滚动例程中,检查之前的位置是否与当前滚动位置相同,如果不相同 - 用户滚动它:

let el = document.documentElement,
    footer = document.getElementById("status").querySelectorAll("td"),
    scroll_position = 0,
    scroll_speed = 0,
    scroll_delta = 1.12,
    scroller,
    status = "stopped";

el.addEventListener("click", scroll);

info();

function scroll(e)
{
  if (e.type == "click")
  {
    window.cancelAnimationFrame(scroller);
    scroll_position = el.scrollTop; //make sure we start from current position
    scroll_speed++; //increase speed with each click
    info("auto scroll");
  }
  //if previous position is different, this means user scrolled
  if (scroll_position != el.scrollTop)
  {
    scroll_speed = 0;
    info("stopped by user");
    return;
  }

  el.scrollTop += scroll_delta * scroll_speed; //scroll to new position
  scroll_position = el.scrollTop; //get the current position

  //loop only if we didn't reach the bottom
  if (el.scrollHeight - el.scrollTop - el.clientHeight > 0)
  {
    scroller = window.requestAnimationFrame(scroll); //loop
  }
  else
  {
    el.scrollTop = el.scrollHeight; //make sure it's all the way to the bottom
    scroll_speed = 0;
    info("auto stopped");
  }
}

function info(s)
{
  if (typeof s === "string")
    status = s;

  footer[1].textContent = el.scrollTop;
  footer[3].textContent = scroll_speed;
  footer[5].textContent = status;

}

//generate html demo sections
for(let i = 2, section = document.createElement("section"); i < 6; i++)
{
  section = section.cloneNode(false);
  section.textContent = "Section " + i;
  document.body.appendChild(section);
}

//register scroll listener for displaying info
window.addEventListener("scroll", info);
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body
{
  font-family: "Roboto", Arial;
  user-select: none;
}

section
{
  min-height: 100vh;
  font-size: 3em;
  font-weight: 500;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #fff;
}

section:nth-child(even)
{
  background: #0b0d19;
}

section:nth-child(odd)
{
  background: #131524;
}

#status
{
  position: fixed;
  bottom: 0;
  color: #fff;
  margin: 0.5em;
}

#status td:first-of-type
{
  text-align: end;
  padding-right: 0.4em;
}
#status td:last-of-type
{
  font-weight: bold;
}
<section>
  Click to start Scrolling
</section>

<table id="status">
  <tr><td>position:</td><td></td></tr>
  <tr><td>speed:</td><td></td></tr>
  <tr><td>status:</td><td></td></tr>
</table>