我正在尝试找到一种优雅的解决方案,只需很少的 JavaScript 即可创建可以具有一列或多列的轮播。单击按钮,它应该滚动到下一列。
想法(我认为JS太多了):
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
我正在尝试找到一种解决方案来避免使用 ResizeObserver。理想情况下,CSS 会自动处理列数、列宽以及侧滚动,而无需复杂的 JS 逻辑。有人有一个优雅的解决方案吗?
CSS 可以做你需要的一切,除了在单击下一个和上一个按钮时处理它们。
此代码段定义了一个元素,即视图,您可以为该元素指定要在其中查看幻灯片的宽度。这是居中的。
其中有一个元素滑块,它通过使所有项目并排显示为内联块。 [这里似乎没有特别需要使用 grid 或 flex]。
代码片段定义了项目之间的间隙,然后计算出每个元素的宽度,但当然您也可以反过来做。
通过改变滑块的左侧值来移动到下一张/上一张幻灯片。
幻灯片必须有两份,因为如果你想减少 JS,你需要 CSS 才能显示下一张(例如,移动到下一张时可能是第一个)。
JS 用于两个单击事件,当它检测到滑块到达一半时,它会重新定位它,以便右侧始终有项目。用户看不到这种重新定位,因为它仅在一帧中发生(使用 requestAnimationFrame),然后发生实际所需的转换。
CSS 调整宽度以适应视口宽度,无需 JS 感知调整大小。
<style>
* {
margin: 0;
}
.container {
position: relative;
width: 100vw;
left: 50%;
transform: translateX(-50%);
}
.view {
position: relative;
left: 50%;
transform: translateX(-50%);
overflow: hidden;
white-space: nowrap;
width: var(--visibleW);
--visibleW: 80vw;
/* the width of slider that you want to be actually showing */
}
.slider {
position: relative;
--showN: 1;
/* the number of items to show at a time, varies with viewport width */
transition: left var(--time);
left: calc(-1 * var(--itemI) * var(--visibleW) / var(--showN));
}
.slider>* {
--margin: 5vw;
/* half the gap between items */
margin: 0 var(--margin);
height: min(80vh, 300px);
/* for demo, set to what you want */
display: inline-block;
width: calc((var(--visibleW) - (var(--showN) * 2 * var(--margin))) / var(--showN))/* the width of each item including the 'gap' */
}
@media (min-width: 1024px) {
.slider {
--showN: 3;
}
}
@media (min-width: 1500px) {
.slider {
--showN: 4;
}
}
button {
position: absolute;
top: 50%;
font-size: min(24px, 5vw);
padding: 10px;
background: transparent;
}
.prev {
transform: translateY(-50%);
}
.next {
right: 0;
transform: translateY(-50%);
}
</style>
<div class="container">
<div class="view">
<div class="slider">
<div style="background: red;"></div>
<div style="background: green;"></div>
<div style="background: blue;"></div>
<div style="background: yellow;"></div>
<div style="background: red;"></div>
<div style="background: green;"></div>
<div style="background: blue;"></div>
<div style="background: yellow;"></div>
</div>
</div>
<button class="prev"><</button>
<button class="next">></button>
</div>
<script>
const view = document.querySelector('.view');
const slider = document.querySelector('.slider');
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');
const num = slider.children.length;
const time = '1s'; //set this to the time you want the sliding to next one to take
let itemI = 0; // this is the index of the item that is the leftmost in the view
view.style.setProperty('--itemI', itemI);
slider.style.setProperty('--time', time);
prev.addEventListener('click', function() {
if (itemI > 0) {
itemI--;
view.style.setProperty('--itemI', itemI);
} else {
itemI = num / 2;
view.style.setProperty('--itemI', itemI);
slider.style.transition = "left 0s";
requestAnimationFrame(function() {
itemI--;
slider.style.transition = "left var(--time)";
view.style.setProperty('--itemI', itemI);
});
}
});
next.addEventListener('click', function() {
if (itemI != (num / 2)) {
itemI++;
slider.style.transition = "left var(--time)";
view.style.setProperty('--itemI', itemI);
} else {
itemI = 0;
view.style.setProperty('--itemI', itemI);
slider.style.transition = "left 0s";
requestAnimationFrame(function() {
itemI++;
slider.style.transition = "left var(--time)";
view.style.setProperty('--itemI', itemI);
});
}
});
</script>