我正在尝试创建一个可以无限向左和向右滚动的日历。当用户向前(轻松)或向后滚动(问题出在这里)时,它必须动态加载新内容。
当我在页面末尾添加内容时,它可以正常工作-滚动条将调整为新的container.scrollWidth
。但是,当我不得不在开始处添加内容时,整个日历会向右移动400像素,这是因为container.scrollLeft
属性没有改变,并且开始处还有一个新元素。
我正在尝试通过将container.scrollLeft
增加400px(新创建的元素的宽度)来缓解这种情况。这种方法有效,但仅在使用鼠标滚轮(Shift +鼠标滚轮向侧面滚动)或移动触摸屏滚动时有效。
如果我使用鼠标拖动滚动条,则会出现故障-我猜是它一直试图滚动到旧的scrollLeft
位置,而无视我增加的位置。
您能建议一种用于所有滚动方式的行为的方法吗?
如果您能将我指向使用此技术的站点,以便我自己进行调查,那也很好。
这是我的半职业例子:
function Container() {
const rectangleWidth = 400;
const container = React.useRef(null);
const [leftRectangles, setLeftRectangles] = React.useState([0]);
const [rightRectangles, setRightRectangles] = React.useState([1, 2, 3, 4]);
// When we just rendered a new rectangle in the left of our container,
// move the scroll position back
React.useEffect(() => {
container.current.scrollLeft += rectangleWidth;
}, [leftRectangles]);
const loadLeftRectangle = () => {
const newAddress = leftRectangles[0] - 1;
setLeftRectangles([newAddress, ...leftRectangles]);
};
const loadRightRectangle = () => {
const newAddress = rightRectangles[rightRectangles.length - 1] + 1;
setRightRectangles([...rightRectangles, newAddress]);
};
const handleScroll = (e) => {
// When there is only 100px of content left, load new content
const loadingOffset = 100;
if (e.target.scrollLeft < loadingOffset) {
loadLeftRectangle(e.target);
} else if (e.target.scrollLeft > e.target.scrollWidth - e.target.clientWidth - loadingOffset) {
loadRightRectangle(e.target);
}
};
return (
<div
className="container"
onScroll={handleScroll}
ref={container}
>
{leftRectangles.map((address) => (
<div className="rectangle" key={address}>
{address}
</div>
))}
{rightRectangles.map((address) => (
<div className="rectangle" key={address}>
{address}
</div>
))}
</div>
);
}
ReactDOM.render(<Container />, document.querySelector("#app"))
.container {
display: flex;
overflow-x: scroll;
}
.rectangle {
border: 1px solid #000;
box-sizing: border-box;
flex-shrink: 0;
height: 165px;
width: 400px;
font-size: 50px;
line-height: 165px;
text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
答案 0 :(得分:2)
我认为在这种情况下,不应使用本机浏览器滚动区域。如果您考虑一下,如果滚动条在两个方向上继续无限长,则没有任何意义。或它们的唯一含义是“已查看的区域”。滚动条对于无限区域不是一个很好的隐喻,因此滚动框是在此处执行操作的较差方法。
如果换种方式考虑,则只显示了适合已知屏幕宽度的几个月时间。我要采用的方法是将每个日历绝对定位在容器内,并根据用户在虚拟视图中穿梭的位置计算它们在渲染循环中的位置。当日历离屏幕太远时,这还允许您删除日历,并在用户滚动到其位置之前在屏幕外创建/缓冲日历以供显示。这样可以防止您拥有任意宽的对象,而这最终会降低渲染速度。
一种解决方法是从1970年开始每个月简单地编号,并将您的视图沿这条线占据小数位置viewX
。 x
的月份的排名应该为viewX - x
。当用户拖动时,您将反向移动viewX
并重新定位缓冲的元素。当用户停止拖动时,您将获得最后一个速度并将其减慢直到viewX - x
为整数。
这将避免大多数跨浏览器问题和抵销问题。在显示器运动时只需要一个渲染循环。
答案 1 :(得分:1)
在动态添加内容之前,先使用read-write
scrollLeft
获取滚动位置,然后使用.scrollLeft
方法将滚动位置重新定位到所需位置。您可能需要启动显示不确定进度指示器的对话框(或仅显示文本“ working ...”),以防止 junking < / em>。
跨浏览器功能的诀窍是众所周知的dialog
元素在跨设备类型的一致性方面具有挑战性,因此我建议您为对话框使用UI库或构建自己的对话框,但要使其保持超级简单。该进度指示器将解决屏幕问题。
要考虑扭曲的另一个功能是CSS transitions,其中添加的内容(例如,块元素)将逐渐淡入/赋予视口动画。