首页 > 解决方案 > 根据滚动位置沿路径绝对定位元素

问题描述

我正在尝试将 div 元素定位在用作其路径的正弦波上。当用户滚动时,回调应该能够计算正弦波的lefttop属性。Ball我的定位有问题。我正在使用Math.absy 轴,并添加 8(或 -8)个像素来处理 x 轴。

我注意到的另一件事是滚动事件侦听器回调有时会错过某些断点。我已经控制台记录了滚动位置及其真实情况,回调要么每 3 个像素执行一次,要么浏览器scroll出于某种原因自行限制事件(我可以理解,跟踪每个像素滚动没有意义)。

无论如何,我想知道为什么我目前的方法不起作用,是否有更好的解决方案来解决这个问题?我觉得事情太多了,这可以通过更好的方式实现。这是我所拥有的:

import React from "react";
import styled from "styled-components";

const lineHeight = 200;
export default React.memo(() => {
  const [top, setTop] = React.useState(275);
  const [left, setLeft] = React.useState(0);
  const [previousPosition, setPreviousPosition] = React.useState(0);

  React.useEffect(() => { const s = skrollr.init() }, []);

  React.useEffect(() => {
    window.addEventListener("scroll", handleScroll);

    return () => window.removeEventListener("scroll", handleScroll);
  }, [previousPosition, left]);

  const handleScroll = React.useCallback(() => {
    const pageYOffset = window.pageYOffset;
    const isMovingForward = pageYOffset > previousPosition;

    setPreviousPosition(window.pageYOffset);
    setTop(lineHeight - Math.abs(lineHeight - pageYOffset));

    if (isMovingForward) {
      if (pageYOffset > 575 && pageYOffset <= 770) setLeft(left + 8);
    } else {
      if (pageYOffset <= 770 && pageYOffset >= 575) setLeft(left - 8)
    }
  }, [previousPosition, left]);

  return (
    <Main>
      <Container
        data-500p="transform: translateX(0%)"
        data-1000p="transform: translateX(-800%)"
      >
        <Content>
          <WaveContainer>
            <Wave src="https://www.designcrispy.com/wp-content/uploads/2017/11/Sine-Wave-Curve.png" />
          </WaveContainer>
          <BallContainer top={top} left={left}>
            <Ball />
          </BallContainer>
        </Content>
      </Container>
    </Main>
  );
});

const Main = styled.div`
  margin: 0;
  padding: 0;
  box-sizing: border-box;
`;

const Container = styled.div`
  position: fixed;
  width: 100%;
  display: flex;
  top: 0;
  left: 0;
`;

const Content = styled.div`
  min-width: 100vw;
  height: auto;
  display: grid;
  place-items: center;
  height: 100vh;
  background: #fffee1;
  font-weight: 900;
  position: relative;
`;

const Wave = styled.img`
  width: 600px;
`;

const WaveContainer = styled.div`
  position: absolute;
  left: -40px;
  top: 45%;
`;

const Ball = styled.div`
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: black;
`;

const BallContainer = styled.div`
  position: absolute;
  transition: 0.25s;
  ${({ top, left }) => `top: ${top}px; left: ${left}px;`};
`;

我正在使用Skrollr来处理固定的画布 + 滚动长度。

代码沙盒

标签: javascriptreactjs

解决方案


我更改了您的代码沙箱,所以它可以满足您的需要:https ://codesandbox.io/s/determined-nobel-64odf (我希望)

我应该补充一点,我更改了有关您的代码的各种内容:

  • 每次左侧或先前位置更改时,无需设置新的滚动侦听器。关于先前位置和决定页面是向上还是向下滚动的所有代码是什么?我删除了它。
  • 动画由于过渡而被限制:在您的 Ball 容器上为 0.25 秒。为了计算球相对于图像的位置 - 正弦波,我将它们移动到同一个容器中。
  • 要计算球的确切位置,需要使用 WAVE_WIDTH 和 WAVE_HEIGHT 常量,并且需要使用适当的数学 - 图像上的正弦曲线似乎很长,为 2,5 个周期。然而 2,58 更好地适应动画。我会尝试使用不同的正弦波并弄清楚。

推荐阅读