Застрял в реализации сложного поведения прокрутки - PullRequest
1 голос
/ 31 мая 2019

Я столкнулся с довольно сложной проблемой в приложении, над которым я работаю, и какое-то время бился головой о стол, пытаясь решить его. Любые советы будут с благодарностью!

По сути, у меня есть контейнер, в котором содержится много элементов; так много, что они переполняются и контейнер прокручивается. В контейнере есть один особый элемент, который я хочу всегда размещать в середине контейнера - , даже если элементы постоянно добавляются над и под ним. Я достиг этого, однако, здесь есть одна загвоздка: как только пользователь прокручивает этот контейнер вручную, я хотел бы отключить предыдущее поведение, когда этот специальный элемент остается в центре контейнера, что я не смог сделать.

Ниже приведена моя лучшая попытка реализовать пример этого, и вот ссылка на коды и ящик моей попытки . Моя реализация работает так: у меня есть контейнер, который содержит список элементов, отображаемых из массива, затем «специальный элемент», а затем еще кучу элементов, отображаемых из другого массива. Когда компонент монтируется, я запускаю две функции: center и updater. center изначально центрирует специальный элемент, и программа обновления начинает добавлять элементы в оба массива очень часто с помощью setInterval. Каждый раз, когда элемент добавляется в один из массивов, center запускается снова, чтобы сохранить специальный элемент по центру в его родительском контейнере. До сих пор это работает идеально, чтобы держать специальный элемент по центру.

Чтобы отключить это центрирующее поведение, у меня есть логическое значение, которое изначально false называется hasScrolled в состоянии, и прослушиватель событий, прикрепленный к событию onWheel основного контейнера, называемый userScrollHandler, который устанавливает hasScrolled в true. Моя функция center центрирует специальный элемент только в том случае, если hasScrolled имеет значение false, поэтому теоретически, когда пользователь прокручивает, hasScrolled будет true, а затем поведение центрирования прекратится. К сожалению, это не похоже на работу. Я уверен, что и событие вызывается, когда пользователь прокручивает контейнер (откройте консоль, чтобы увидеть журнал каждой прокрутки), и что hasScrolled устанавливается в true. По какой-то причине функция center считает, что hasScrolled ложно. Я пробовал множество других вещей, таких как передача этих значений в функцию center, не сохранение hasScrolled в состоянии и просто наличие его в качестве религиозной переменной, использование контекста и т. Д. Я теряюсь в том, как это сделать, и почему ни одна из моих реализаций не работает. У кого-нибудь есть идеи, как это сделать или что я делаю неправильно?

Моя реализация:

import React, { useState, useEffect, useRef, useReducer } from "react";
import ReactDOM from "react-dom";

const items1 = [];
for (let i = 0; i < 10; i++) {
  items1.push("top");
}
const items2 = [];
for (let i = 0; i < 10; i++) {
  items2.push("bottom");
}

const App = () => {
  const [hasScrolled, setHasScrolled] = useState(false);
  const [tops, addTopItem] = useReducer(state => {
    const stateCopy = [...state];
    stateCopy.push(Math.random());
    return stateCopy;
  }, items1);
  const [bottoms, addBottomItem] = useReducer(state => {
    const stateCopy = [...state];
    stateCopy.unshift(Math.random());
    return stateCopy;
  }, items2);

  const middle = useRef(null);
  const container = useRef(null);

  const center = () => {
    if (hasScrolled) return;

    container.current.scrollTo(
      0,
      middle.current.offsetTop -
        container.current.offsetTop -
        container.current.clientHeight / 2
    );
  };

  const updater = () => {
    setInterval(() => {
      addTopItem();
      if (container.current && middle.current) center();
    }, 500);

    setInterval(() => {
      addBottomItem();
      if (container.current && middle.current) center();
    }, 700);
  };

  const userScrollHandler = e => {
    console.log("user scrolled");
    setHasScrolled(true);
  };

  useEffect(() => {
    center();
    updater();
  }, []);

  return (
    <div ref={container} onWheel={userScrollHandler} style={containerStyle}>
      {tops.map((item, idx) => {
        return (
          <div key={idx} style={itemStyle}>
            {item}
          </div>
        );
      })}
      <div ref={middle} style={middleItemStyle}>
        Middle
      </div>
      {bottoms.map((item, idx) => {
        return (
          <div key={idx} style={itemStyle}>
            {item}
          </div>
        );
      })}
    </div>
  );
};

const containerStyle = {
  margin: "0 auto",
  marginTop: "10vh",
  height: "400px",
  width: "300px",
  background: "#eee",
  overflow: "auto"
};

const itemStyle = {
  width: "100%",
  height: "40px",
  borderBottom: "1px solid blue",
  display: "flex",
  justifyContent: "center",
  alignItems: "center"
};

const middleItemStyle = { ...itemStyle };
middleItemStyle.background = "lime";

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

1 Ответ

0 голосов
/ 31 мая 2019

Можете ли вы сделать Средний элемент независимым и просто не отображать его?

{hasScrolled && <div ref={middle} style={middleItemStyle}>
        Middle
      </div>}
...