带有导航(下一个,上一个)按钮的 CSS 滚动捕捉点

seb*_*ier 10 javascript css frontend scroll

我正在使用CSS snap points构建一个非常简约的旋转木马。对我来说只有CSS 选项很重要,但我可以用javascript没有框架)增强一点。

我正在尝试添加上一个和下一个按钮以编程方式滚动到下一个或上一个元素。如果 javascript 被禁用,按钮将被隐藏并且轮播仍然起作用。

我的问题是如何触发滚动到下一个捕捉点

所有项目都有不同的大小,我发现的大多数解决方案都需要像素值(如scrollBy示例中使用的)。AscrollBy 40px适用于第 2 页,但不适用于其他页面,因为它们太大(基于视口的大小)。

function goPrecious() {
  document.getElementById('container').scrollBy({ 
    top: -40,
    behavior: 'smooth' 
  });
}

function goNext() {
  document.getElementById('container').scrollBy({ 
    top: 40,
    behavior: 'smooth' 
  });
}
Run Code Online (Sandbox Code Playgroud)
#container {
  scroll-snap-type: y mandatory;
  overflow-y: scroll;

  border: 2px solid var(--gs0);
  border-radius: 8px;
  height: 60vh;
}

#container div {
  scroll-snap-align: start;

  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 4rem;
}
#container div:nth-child(1) {
  background: hotpink;
  color: white;
  height: 50vh;
}
#container div:nth-child(2) {
  background: azure;
  height: 40vh;
}
#container div:nth-child(3) {
  background: blanchedalmond;
  height: 60vh;
}
#container div:nth-child(4) {
  background: lightcoral;
  color: white;
  height: 40vh;
}
Run Code Online (Sandbox Code Playgroud)
<div id="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

<button onClick="goPrecious()">previous</button>
<button onClick="goNext()">next</button>
Run Code Online (Sandbox Code Playgroud)

don*_*rdi 13

我最近刚刚做了类似的事情。这个想法是使用IntersectionObserver来跟踪当前视图中的项目,然后将上一个/下一个按钮连接到调用Element.scrollIntoView()的事件处理程序。

无论如何,Safari 目前不支持滚动行为选项。因此,您可能希望使用polyfill.app服务按需填充它。

let activeIndex = 0;
const container = document.querySelector("#container");
const elements = [...document.querySelectorAll("#container div")];

function handleIntersect(entries){
  const entry = entries.find(e => e.isIntersecting);
  if (entry) {
    const index = elements.findIndex(
      e => e === entry.target
    );
    activeIndex = index;
  }
}

const observer = new IntersectionObserver(handleIntersect, {
  root: container,
  rootMargin: "0px",
  threshold: 0.75
});

elements.forEach(el => {
  observer.observe(el);
});

function goPrevious() {
  if(activeIndex > 0) {
    elements[activeIndex - 1].scrollIntoView({
      behavior: 'smooth'
    })
  }
}

function goNext() {
  if(activeIndex < elements.length - 1) {
    elements[activeIndex + 1].scrollIntoView({
      behavior: 'smooth'
    })
  }
}
Run Code Online (Sandbox Code Playgroud)
#container {
  scroll-snap-type: y mandatory;
  overflow-y: scroll;

  border: 2px solid var(--gs0);
  border-radius: 8px;
  height: 60vh;
}

#container div {
  scroll-snap-align: start;

  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 4rem;
}
#container div:nth-child(1) {
  background: hotpink;
  color: white;
  height: 50vh;
}
#container div:nth-child(2) {
  background: azure;
  height: 40vh;
}
#container div:nth-child(3) {
  background: blanchedalmond;
  height: 60vh;
}
#container div:nth-child(4) {
  background: lightcoral;
  color: white;
  height: 40vh;
}
Run Code Online (Sandbox Code Playgroud)
<div id="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

<button onClick="goPrevious()">previous</button>
<button onClick="goNext()">next</button>
Run Code Online (Sandbox Code Playgroud)


Aza*_*zin 10

好问题!我把这当作一个挑战。
所以,我增加JavaScript了它的动态工作。按照我的详细解决方案(最后是完整代码):

首先,添加position: relative.container,因为它需要被引用scrollheight检查内部.container

然后,让我们创建 3 个全局辅助variables

1)将项目scroll positions(顶部和底部)arrays放入array. 示例:([[0, 125], [125, 280], [280, 360]]本例中为 3 个项目)。
3)一个存储一半的.container height(稍后会用到)。
2)另一个存储item indexforscroll位置

var carouselPositions;
var halfContainer;
var currentItem;
Run Code Online (Sandbox Code Playgroud)

现在,function被称为getCarouselPositions是创建array与项目位置(存储在carouselPositions)计算的一半.container(存储在halfContainer):

function getCarouselPositions() {
  carouselPositions = [];
  document.querySelectorAll('#container div').forEach(function(div) {
    carouselPositions.push([div.offsetTop, div.offsetTop + div.offsetHeight]); // add to array the positions information
  })
  halfContainer = document.querySelector('#container').offsetHeight/2;
}

getCarouselPositions(); // call it once
Run Code Online (Sandbox Code Playgroud)

让我们替换functionson buttons。现在,当您单击它们时,function将调用相同的函数,但带有"next""previous"参数:

<button onClick="goCarousel('previous')">previous</button>
<button onClick="goCarousel('next')">next</button>
Run Code Online (Sandbox Code Playgroud)

这里是关于它goCarousel function本身:

首先,它创建 2variables存储旋转木马的top scroll位置和bottom scroll位置。

然后,有 2 个条件来查看当前轮播位置是在 mosttop还是 most 上bottom
如果打开top并单击"next" button,它将转到第二个项目位置。如果它打开bottom并单击"previous" button,它将在最后一项之前转到上一项。

如果两个条件都失败,则表示当前项目不是第一个或最后一个。因此,它检查以查看 是什么current position,并在循环中使用容器的一半进行计算array of positions以查看item显示的内容。然后,它结合"previous""next"检查为currentItem变量设置正确的下一个位置。

最后,它通过scrollTo使用currentItem新值到达正确的位置。

下面是完整的代码:

var carouselPositions;
var halfContainer;
var currentItem;
Run Code Online (Sandbox Code Playgroud)
function getCarouselPositions() {
  carouselPositions = [];
  document.querySelectorAll('#container div').forEach(function(div) {
    carouselPositions.push([div.offsetTop, div.offsetTop + div.offsetHeight]); // add to array the positions information
  })
  halfContainer = document.querySelector('#container').offsetHeight/2;
}

getCarouselPositions(); // call it once
Run Code Online (Sandbox Code Playgroud)
<button onClick="goCarousel('previous')">previous</button>
<button onClick="goCarousel('next')">next</button>
Run Code Online (Sandbox Code Playgroud)

另一个要添加的好细节是getCarouselPositions在窗口调整大小时再次调用函数:

window.addEventListener('resize', getCarouselPositions);
Run Code Online (Sandbox Code Playgroud)

就是这样。
这样做很酷。我希望它能以某种方式有所帮助。


小智 5

使用 React 完成的更简单的方法。

export const AppCarousel = props => {

  const containerRef = useRef(null);
  const carouselRef = useRef(null);


  const [state, setState] = useState({
    scroller: null,
    itemWidth: 0,
    isPrevHidden: true,
    isNextHidden: false

  })

  const next = () => {
    state.scroller.scrollBy({left: state.itemWidth * 3, top: 0, behavior: 'smooth'});

    // Hide if is the last item
    setState({...state, isNextHidden: true, isPrevHidden: false});
  }


   const prev = () => {
    state.scroller.scrollBy({left: -state.itemWidth * 3, top: 0, behavior: 'smooth'});
    setState({...state, isNextHidden: false, isPrevHidden: true});
    // Hide if is the last item
    // Show remaining
   }

  useEffect(() => {

      const items = containerRef.current.childNodes;
      const scroller = containerRef.current;
      const itemWidth = containerRef.current.firstElementChild?.clientWidth;

      setState({...state, scroller, itemWidth});

    return () => {

    }
  },[props.items])


  return (<div className="app-carousel" ref={carouselRef}>

      <div className="carousel-items shop-products products-swiper" ref={containerRef}>
          {props.children}
      </div>
      <div className="app-carousel--navigation">
        <button className="btn prev" onClick={e => prev()} hidden={state.isPrevHidden}>&lt;</button>
        <button className="btn next" onClick={e => next()} hidden={state.isNextHidden}>&gt;</button>
      </div>

  </div>)
}

Run Code Online (Sandbox Code Playgroud)