【问题标题】:How to make set interval to work properly如何使设置的间隔正常工作
【发布时间】:2022-12-21 20:41:29
【问题描述】:

我正在尝试制作一个包含三列的 div,其中所有三列都通过动画滚动到底部。

当到达底部时,他们应该开始滚动到顶部。然后当到达顶部时,他们应该再次开始滚动到底部,依此类推。

我正在使用 React,我尝试通过将每列的顶部位置添加到状态并在 setInterval 中更改它来做到这一点。然后我尝试检测是否到达顶部的底部,然后相应地更改顶部位置。

问题是它滚动到底部,但是由于某种原因它不会滚动到顶部。我不确定为什么下一个代码不起作用。

代码如下:

import React, { useState, useEffect } from "react";
import { map } from "lodash";

const column1InitialYPosition = -300;
const column2InitialYPosition = -150;
const column3InitialYPosition = 0;
const intervalTime = 500;

const imageUrl =
  "https://images.freeimages.com/variants/jzAZ1zYpxh11gSbgYPMheWun/f4a36f6589a0e50e702740b15352bc00e4bfaf6f58bd4db850e167794d05993d";
const column1Images = [imageUrl, imageUrl, imageUrl, imageUrl, imageUrl];
const column2Images = [imageUrl, imageUrl, imageUrl, imageUrl, imageUrl];
const column3Images = [imageUrl, imageUrl, imageUrl, imageUrl, imageUrl];

const isElementInViewport = el => {
  let r, html;
  if (!el || 1 !== el.nodeType) {
    return false;
  }
  html = document.documentElement;
  r = el.getBoundingClientRect();
  return !!r && r.bottom >= 0 && r.top <= html.clientHeight;
};

export default function App() {
  const [column1YPosition, setColumn1YPosition] = useState(
    column1InitialYPosition
  );
  const [column2YPosition, setColumn2YPosition] = useState(
    column2InitialYPosition
  );
  const [column3YPosition, setColumn3YPosition] = useState(
    column3InitialYPosition
  );

  let intervalScroll;

  useEffect(() => {
    setTimeout(() => startScrollInterval(), 3000);
    return () => removeScrollToBottomInterval();
  }, []);

  const calculateNewPosition = (
    currentPosition,
    maxPosition,
    minPosition,
    bottomReached,
    topReached
  ) => {
    let newPosition = bottomReached
      ? currentPosition + 100
      : currentPosition - 100;
    newPosition = -1 * newPosition > maxPosition ? -maxPosition : newPosition;
    newPosition = newPosition > minPosition ? minPosition - 1 : newPosition;
    return newPosition;
  };

  const startScrollInterval = () => {
    const screenHeight = window ? window?.innerHeight : 0;
    const column1Bottom = document.getElementById("column-1-bottom");
    const column1Top = document.getElementById("column-1-top");
    const column1 = document.getElementById("column-1");
    const column1MaxTop = column1 ? column1?.scrollHeight - screenHeight : 0;

    const column2Bottom = document.getElementById("column-2-bottom");
    const column2Top = document.getElementById("column-2-top");
    const column2 = document.getElementById("column-2");
    const column2MaxTop = column2 ? column2?.scrollHeight - screenHeight : 0;

    const column3Bottom = document.getElementById("column-3-bottom");
    const column3Top = document.getElementById("column-3-top");
    const column3 = document.getElementById("column-3");
    const column3MaxTop = column3 ? column3?.scrollHeight - screenHeight : 0;

    const intervalHandler = () => {
      const column1BottomReached = isElementInViewport(column1Bottom);
      const column1TopReached = isElementInViewport(column1Top);
      const column2BottomReached = isElementInViewport(column2Bottom);
      const column2TopReached = isElementInViewport(column2Top);
      const column3BottomReached = isElementInViewport(column3Bottom);
      const column3TopReached = isElementInViewport(column3Top);

      setColumn1YPosition(column1YPosition => {
        return calculateNewPosition(
          column1YPosition,
          column1MaxTop,
          column1InitialYPosition,
          column1BottomReached,
          column1TopReached
        );
      });

      setColumn2YPosition(column2YPosition => {
        return calculateNewPosition(
          column2YPosition,
          column2MaxTop,
          column2InitialYPosition,
          column2BottomReached,
          column2TopReached
        );
      });
      setColumn3YPosition(column3YPosition => {
        return calculateNewPosition(
          column3YPosition,
          column3MaxTop,
          column3InitialYPosition,
          column3BottomReached,
          column3TopReached
        );
      });
    };

    intervalScroll = setInterval(intervalHandler, intervalTime);
  };

  const removeScrollToBottomInterval = () => {
    clearInterval(intervalScroll);
    intervalScroll = null;
  };

  return (
    <div className="relative w-full h-screen overflow-hidden">
      <div className="grid grid-cols-3 px-24 gap-24">
        <div className="relative overflow-hidden h-screen no-scrollbar">
          <div
            id="column-1"
            className="absolute inset-0 grid grid-cols-1 gap-24"
            style={{
              transition: "all 6s linear",
              top: column1YPosition
            }}
          >
            {map(column1Images, (image, i) => {
              const isFirst = i === 0;
              const isLast = i === column1Images?.length - 1;
              return (
                <div key={`column-1-vehicle-${i}`} className="relative w-full">
                  <img src={image} alt="Vehicle" className="w-full h-80" />
                  {isFirst && (
                    <div
                      id="column-1-top"
                      className="absolute opacity-1 w-full h-20"
                      style={{ top: -1 * column1InitialYPosition }}
                    />
                  )}
                  {isLast && (
                    <div
                      id="column-1-bottom"
                      className="absolute bottom-0 opacity-1 w-full h-20"
                    />
                  )}
                </div>
              );
            })}
          </div>
        </div>

        <div className="relative overflow-hidden h-screen no-scrollbar">
          <div
            id="column-2"
            className="absolute inset-0 grid grid-cols-1 gap-24"
            style={{
              transition: "all 5s linear",
              top: column2YPosition
            }}
          >
            {map(column2Images, (image, i) => {
              const isFirst = i === 0;
              const isLast = i === column2Images?.length - 1;
              return (
                <div key={`column-2-vehicle-${i}`} className="relative w-full">
                  <img src={image} alt="Vehicle" className="w-full h-80" />
                  {isFirst && (
                    <div
                      id="column-2-top"
                      className="absolute opacity-1 w-full h-20"
                      style={{ top: -1 * column2InitialYPosition }}
                    />
                  )}
                  {isLast && (
                    <div
                      id="column-2-bottom"
                      className="absolute bottom-0 opacity-1 w-full h-20"
                    />
                  )}
                </div>
              );
            })}
          </div>
        </div>

        <div className="relative overflow-hidden h-screen no-scrollbar">
          <div
            id="column-3"
            className="absolute inset-0 grid grid-cols-1 gap-24"
            style={{
              transition: "all 6s linear",
              top: column3YPosition
            }}
          >
            {map(column3Images, (image, i) => {
              const isFirst = i === 0;
              const isLast = i === column3Images?.length - 1;
              return (
                <div key={`column-3-vehicle-${i}`} className="relative w-full">
                  <img src={image} alt="Vehicle" className="w-full h-80" />
                  {isFirst && (
                    <div
                      id="column-3-top"
                      className="absolute opacity-1 w-full h-20"
                      style={{ top: -1 * column3InitialYPosition }}
                    />
                  )}
                  {isLast && (
                    <div
                      id="column-3-bottom"
                      className="absolute bottom-0 opacity-1 w-full h-20"
                    />
                  )}
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </div>
  );
}

我已经在沙箱中添加了一个示例。 Here is the link

知道如何解决吗?

【问题讨论】:

  • 你能创建一个最小的可重现示例吗?
  • codesandbox 的链接已经添加。
  • 抱歉,我的意思是,是否可以缩短代码示例?
  • 您的应用程序显示三个图像列并使用不同的初始 Y 偏移量,内部元素使用绝对位置,外部元素使用相对位置。垂直滚动到底部或顶部时可能会出现一些问题。你能不能只用一列来测试滚动的顶部和底部作为演示?

标签: javascript reactjs


【解决方案1】:

我的解决方案将仅基于第一列,因此您需要将其扩展为 N 列。

首先定义这些全局变量:

// +1 => we are moving up, -1 => down
var direction = 1;
// was previously top reached or not
var wasTopReached = false;
// was previously bottom reached or not
var wasBottomReached = false;

然后将您的 column1 位置计算函数重新设计为:

  const calculateNewPositionForCol1 = (
    currentPosition,
    maxPosition,
    minPosition,
    bottomReached,
    topReached
  ) => {
    direction = (bottomReached && !wasBottomReached) || 
                (topReached    && !wasTopReached) ? -direction : direction;
    [wasBottomReached, wasTopReached] = [bottomReached, topReached];
    let newPosition = currentPosition + direction * 100;
    return newPosition;
  };

简而言之,- 每次撞到同一堵墙时,您只需要改变一次运动方向,- 而不是像之前在您的函数 calculateNewPosition() 中那样多次。

【讨论】:

    猜你喜欢
    • 2021-11-22
    • 2012-07-28
    • 1970-01-01
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多