【问题标题】:How to dynamically calculate the and restrict the width of a scrollable overflow div?如何动态计算和限制可滚动溢出div的宽度?
【发布时间】:2021-06-06 12:58:55
【问题描述】:

我有一个可水平滚动的 div 来呈现一些卡片。有一些按钮可以在单击时向左或向右(前后)变换 div。

我遇到的麻烦是试图弄清楚如何在到达最后一张卡片后禁用Next 按钮。禁用Back 按钮很简单,但我遇到了麻烦。

目前,它一直在无限向右滚动。

这是代码,

import * as React from "react";

function getRandomNumber(min, max) {
  return Math.random() * (max - min) + min;
}

const cardWith = 200;
const cards = 10;
export default function Swipeable() {
  const [currentTransform, setCurrentTransform] = React.useState(0);
  const content = Array.from({ length: cards }, (_, index) => (
    <div
      style={{
        minWidth: cardWith,
        height: 300,
        backgroundColor: `rgba(${getRandomNumber(0, 255)},${getRandomNumber(
          0,
          255
        )}, ${getRandomNumber(0, 255)})`
      }}
    >
      card {index}
    </div>
  ));

  return (
    <div style={{ width: "100%", overflow: "hidden", position: "relative" }}>
      <div
        onClick={() => setCurrentTransform(currentTransform + 1)}
        style={{
          position: "absolute",
          right: 10,
          top: 100,
          cursor: "pointer",
          zIndex: 5,
          display: "grid",
          placeItems: "center",
          width: 50,
          height: 50,
          transition: "0.25s",
          backgroundColor: "cyan",
          borderRadius: "0.25rem"
        }}
      >
        Next
      </div>
      <div
        onClick={() =>
          currentTransform === 0
            ? undefined
            : setCurrentTransform(currentTransform - 1)
        }
        style={{
          position: "absolute",
          left: 10,
          top: 100,
          display: "grid",
          placeItems: "center",
          cursor: "pointer",
          zIndex: 5,
          width: 50,
          height: 50,
          transition: "0.25s",
          backgroundColor: "cyan",
          borderRadius: "0.25rem"
        }}
      >
        Back
      </div>
      <div
        style={{
          transition: "0.25s",
          display: "flex",
          transform: `translateX(-${currentTransform * cardWith}px)`
        }}
      >
        {content}
      </div>
    </div>
  );
}

【问题讨论】:

    标签: javascript css reactjs


    【解决方案1】:

    我认为只需在“下一步”按钮上放置一个条件即可获得您想要的结果:

    <div
        onClick={() =>
          currentTransform === 6
            ? undefined
            : setCurrentTransform(currentTransform + 1)
        }
    ...
    

    为了让它更漂亮,也许你可以用宽度做一个计算,你希望它到底是多少?

    【讨论】:

    • 你不能只硬编码数字 6,因为屏幕可以是所有不同类型的宽度。有些可能显示 2 张卡片,有些可能显示 10 张卡片。您肯定需要考虑到这一点。
    • 我正要说同样的话。我正在寻找一种动态执行此操作的方法。不过还是谢谢你的建议
    【解决方案2】:

    您只需检查下一个索引与卡片数量的比较

    function setCurrentTransform(index) {
        if (!index) {
            // If index is 0, disable the back button
            nextButton.disabled = false;
            backButton.disabled = true;
        } else if (index == cards) {
            // If index is the same as the number of cards, disable the next button
            nextButton.disabled = true;
            backButton.disabled = false;
        } else nextButton.disabled = backButton.disabled = false; // the index is somewhere between the start and end
    
        ...
    }
    

    那么你的 react 渲染可能是这样的

    返回按钮

    <div onClick={
        () => setCurrentTransform(currentTransform - 1)
    }
    ...
    

    下一步按钮

    <div onClick={
        () => setCurrentTransform(currentTransform + 1)
    }
    ...
    

    【讨论】:

      【解决方案3】:

      您可能需要考虑使用scrollIntoViewgetBoundingClientRect,而不是尝试制作自己的滚动功能,它们内置于浏览器中。这样,您还可以删除 overflow: hidden 并使用原生滚动条,或者允许移动滑动。

      您只需要将当前卡片存储在状态中,然后当人们单击不在视口中的下一个项目的按钮时将其递增或递减。您只需使用浏览器的原生滚动,而不是使用 translateX。

      import React, { useState, useRef, useEffect } from "react";
      
      function getRandomNumber(min, max) {
        return Math.random() * (max - min) + min;
      }
      
      function isElementInViewport(el) {
        var rect = el.getBoundingClientRect();
      
        return (
          rect.left >= 0 &&
          rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        );
      }
      
      const cardWith = 200;
      const cards = 10;
      
      export default function Swipeable() {
        const refs = useRef([]);
        const [currentCard, setCurrentCard] = useState(0);
      
        useEffect(() => {
          refs.current = refs.current.slice(0, cards);
        }, []);
      
        const content = Array.from({ length: cards }, (_, index) => (
          <div
            ref={(el) => (refs.current[index] = el)}
            key={index}
            style={{
              minWidth: cardWith,
              height: 300,
              backgroundColor: `rgba(${getRandomNumber(0, 255)},${getRandomNumber(
                0,
                255
              )}, ${getRandomNumber(0, 255)})`
            }}
          >
            card {index}
          </div>
        ));
      
        const back = () => {
          for (let i = currentCard; i >= 0; i--) {
            if (refs.current[i] && !isElementInViewport(refs.current[i])) {
              setCurrentCard(i);
              refs.current[i].scrollIntoView({ behavior: "smooth" });
              break;
            }
          }
        };
      
        const forward = () => {
          for (let i = currentCard; i < cards; i++) {
            if (refs.current[i] && !isElementInViewport(refs.current[i])) {
              setCurrentCard(i);
              refs.current[i].scrollIntoView({ behavior: "smooth" });
              break;
            }
          }
        };
      
        return (
          <>
            <div
              style={{
                width: "100%",
                overflow: "hidden",
                position: "relative",
                whiteSpace: "nowrap"
              }}
            >
              <div
                style={{
                  transition: "0.25s",
                  display: "flex"
                }}
              >
                {content}
              </div>
            </div>
            <div
              onClick={forward}
              style={{
                position: "absolute",
                right: 10,
                top: 100,
                cursor: "pointer",
                zIndex: 5,
                display: "grid",
                placeItems: "center",
                width: 50,
                height: 50,
                transition: "0.25s",
                backgroundColor: "cyan",
                borderRadius: "0.25rem"
              }}
            >
              Next
            </div>
            <div
              onClick={back}
              style={{
                position: "absolute",
                left: 10,
                top: 100,
                display: "grid",
                placeItems: "center",
                cursor: "pointer",
                zIndex: 5,
                width: 50,
                height: 50,
                transition: "0.25s",
                backgroundColor: "cyan",
                borderRadius: "0.25rem"
              }}
            >
              Back
            </div>
          </>
        );
      }
      

      CodeSandbox

      【讨论】:

        猜你喜欢
        • 2015-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-30
        • 2012-01-14
        • 2011-12-26
        • 2011-06-16
        • 1970-01-01
        相关资源
        最近更新 更多