Я столкнулся с довольно сложной проблемой в приложении, над которым я работаю, и какое-то время бился головой о стол, пытаясь решить его. Любые советы будут с благодарностью!
По сути, у меня есть контейнер, в котором содержится много элементов; так много, что они переполняются и контейнер прокручивается. В контейнере есть один особый элемент, который я хочу всегда размещать в середине контейнера - , даже если элементы постоянно добавляются над и под ним. Я достиг этого, однако, здесь есть одна загвоздка: как только пользователь прокручивает этот контейнер вручную, я хотел бы отключить предыдущее поведение, когда этот специальный элемент остается в центре контейнера, что я не смог сделать.
Ниже приведена моя лучшая попытка реализовать пример этого, и вот ссылка на коды и ящик моей попытки . Моя реализация работает так: у меня есть контейнер, который содержит список элементов, отображаемых из массива, затем «специальный элемент», а затем еще кучу элементов, отображаемых из другого массива. Когда компонент монтируется, я запускаю две функции: 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);