如何让水平滚动更流畅?

How to make horizontal scroll smoother?

我将此代码添加到我的基于 WordPress 的网站,以使其首页水平。但它并不流畅,我无法添加滚动捕捉或锚点。你能帮我解决这些问题吗?我的网站是 https://kozb.art

<script type="text/javascript">
function replaceVerticalScrollByHorizontal( event ) {
  if ( event.deltaY !== 0 ) {
   window.scroll(window.scrollX + event.deltaY * 2, window.scrollY );
   event.preventDefault();
  }
 }

 const mediaQuery = window.matchMedia( '(min-width: 770px)' );

 if ( mediaQuery.matches ) {
  window.addEventListener( 'wheel', replaceVerticalScrollByHorizontal );
 }
</script>

编辑:这是我的 CSS 使首页水平的代码:

.elementor-section-wrap{
    display: inline-flex;
}

.elementor-section{
 width:100vw;
}
body{
    overflow-y: hidden;
    overscroll-behavior-y: none;
}

@media (max-width:768px){
    .elementor-section-wrap{
      display: block;
    }
    body{
      overflow-y: auto;
      overflow-x: hidden;
      overscroll-behavior-x: none;
    }
}

您需要动画知识才能顺利移动水平滚动条。让我们从水平滚动环境开始。

// index.html
...
<head>
  <link href="index.css" rel="stylesheet">
</head>
<body>
  <div id="screen">
    <div id="content1"></div><div id="content2"></div><div id="content3"></div><div id="content4"></div>
  </div>
  <script src="./main.js"></script>
</body>
...
/* index.css */
body {
  margin: 0;
  width: 100vw;
  height: 100vh;
  overflow-y: hidden;
}
#screen {
  white-space: nowrap;
  height: 100%;
}

#screen > div {
  width: 100vw;
  height: 100vh;
  display: inline-block;
}
#screen > div:nth-child(1) {
  background: aqua;
}
#screen > div:nth-child(2) {
  background: blueviolet
}
#screen > div:nth-child(3) {
  background: chocolate
}
#screen > div:nth-child(4) {
  background: darkolivegreen;
}

现在已经创建了一个网页,它有四个部分,每个部分占用一个屏幕大小。对于平滑、快速的水平滚动应用程序,让我们逐步思考我们需要为动画制作代码。

要实现 Snap,您需要知道滚动 X 应该移动到什么值。在当前布局中,该值为 section 元素的 offsetLeft 值。并且部分大小会根据浏览器大小而变化。所以获取section的offsetLeft的代码可以这样创建:

let sectionAnchorPointer = [];
const resizeHandler = () => {
  const content1 = document.getElementById('content1');
  const content2 = document.getElementById('content2');
  const content3 = document.getElementById('content3');
  const content4 = document.getElementById('content4');

  sectionAnchorPointer = [content1.offsetLeft, content2.offsetLeft, content3.offsetLeft, content4.offsetLeft];
};
addEventListener('resize', resizeHandler);
addEventListener('DOMContentLoaded', () => {
  resizeHandler();
});

为了从一开始就存储section offsetLeft值,当DOMContentLoaded发生时,执行了一个函数来更新section offsetLeft。如果你想让它更有效率,请将 debounce 应用到调整大小事件处理程序。

接下来,当车轮出现时,找到要移动的部分。为了找到要移动的路段,需要确定路段所在的位置,然后根据车轮的方向进行计算。代码如下:

let nextSectionIndex = 0;

const getCurrentSectionIndex = () => sectionAnchorPointer.findIndex((leftValue, i, array) => {
  const scrollX = Math.ceil(window.scrollX); // Fixed a bug where scrollX was decimalized
  const rightValue = array[i + 1] ?? Infinity;
  return leftValue <= scrollX && scrollX < rightValue;
});

window.addEventListener('wheel', ({ deltaY }) => {
  const currentSectionIndex = getCurrentSectionIndex();
  const add = Math.abs(deltaY) / deltaY;

  nextSectionIndex = currentSectionIndex + add;
  nextSectionIndex = Math.min(sectionAnchorPointer.length - 1, Math.max(0, nextSectionIndex)); // To avoid pointing to a section index that does not exist
  console.log(sectionAnchorPointer[nextSectionIndex]);
});

访问页面时保存滚动位置,在DOMContentLoaded事件发生时调用该函数

...
addEventListener('DOMContentLoaded', () => {
  resizeHandler();
  nextSectionIndex = getCurrentSectionIndex();
});
...

接下来,应用动画,使其慢慢变为需要移动当前scrollX 值的部分的offsetLeft 值。为了便于理解,我们将其做成一个没有加速的线性动画。代码如下:

const SCROLL_SPEED = 70; // It can be changed for speed control.
requestAnimationFrame(function scroll() {
  const nextScrollX = sectionAnchorPointer[nextSectionIndex];

  // linear animtion
  if (Math.abs(window.scrollX - nextScrollX) > SCROLL_SPEED) {
    const val = -Math.abs(window.scrollX - nextScrollX) / (window.scrollX - nextScrollX);
    window.scroll(window.scrollX + val * SCROLL_SPEED, window.scrollY);
  } else {
    window.scroll(nextScrollX, window.scrollY);
  }

  requestAnimationFrame(scroll);
});

要通过添加加速来应用动态动画,请添加以下代码而不是上面的代码。

requestAnimationFrame(function scroll() {
  const nextScrollX = sectionAnchorPointer[nextSectionIndex];

  // curve animation
  if (Math.abs(window.scrollX - nextScrollX) > 1) {
    let val = (nextScrollX - window.scrollX) / 8; // You can change 8 to another value to adjust the animation speed.
    
    val = val > 0 ? Math.max(val, 1) : Math.min(val, -1);

    window.scroll(window.scrollX + val, window.scrollY);
  } else {
    window.scroll(nextScrollX, window.scrollY);
  }

  requestAnimationFrame(scroll);
});

这是一个简单的实现示例,以供理解。 Here is a link to the project using the code. If you are interested, please refer to the following link to understand Javascript animation. For your information, it would be more convenient to make an animation using a known library such as anime.js or popmotion.


这是适合您结构的脚本代码。删除现有的车轮侦听器并插入此内容。

const wrap = document.querySelectorAll('.elementor-section-wrap')[1]
let sectionAnchorPointer = [];
let resultX = 0;
const resizeHandler = () => {
  sectionAnchorPointer = [...new Set([...wrap.children].map(el => el.offsetLeft))];
};
addEventListener('resize', resizeHandler);
addEventListener('DOMContentLoaded', () => {
  resizeHandler();
  nextSectionIndex = getCurrentSectionIndex();
});
resizeHandler();

let nextSectionIndex = 0;

const getCurrentSectionIndex = () => sectionAnchorPointer.findIndex((leftValue, i, array) => {
  const scrollX = Math.ceil(resultX); // Fixed a bug where scrollX was decimalized
  const rightValue = array[i + 1] ?? Infinity;
  return leftValue <= resultX && resultX < rightValue;
});

window.addEventListener('wheel', (ev) => {
    const {deltaY} = ev;
  const currentSectionIndex = getCurrentSectionIndex();
  const add = Math.abs(deltaY) / deltaY;

  nextSectionIndex = currentSectionIndex + add;
  nextSectionIndex = Math.min(sectionAnchorPointer.length - 1, Math.max(0, nextSectionIndex)); // To avoid pointing to a section index that does not exist
  console.log(sectionAnchorPointer[nextSectionIndex]);
});

const SCROLL_SPEED = 70; // It can be changed for speed control.
requestAnimationFrame(function scroll() {
  const nextScrollX = sectionAnchorPointer[nextSectionIndex];

  // linear animtion
  if (Math.abs(resultX - nextScrollX) > SCROLL_SPEED) {
    const val = -Math.abs(resultX - nextScrollX) / (resultX - nextScrollX);
    resultX = resultX + val * SCROLL_SPEED;
  } else {
    resultX = nextScrollX;
  }
  window.scroll(resultX , 0);
  requestAnimationFrame(scroll);
});