欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用React實(shí)現(xiàn)內(nèi)容滑動(dòng)組件效果

 更新時(shí)間:2023年05月17日 15:14:52   作者:SaebaRyo  
這篇文章主要介紹了使用React實(shí)現(xiàn)一個(gè)內(nèi)容滑動(dòng)組件效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

使用React實(shí)現(xiàn)一個(gè)內(nèi)容滑動(dòng)組件

最近在做項(xiàng)目時(shí)遇到一個(gè)需求,需要讓一個(gè)列表能夠通過點(diǎn)擊按鈕進(jìn)行滾動(dòng),每次都是一屏的距離,不足則結(jié)束。并且,這個(gè)列表項(xiàng)是在react-grid-layout中的某一個(gè)模塊內(nèi)。所以包裹這個(gè)列表的容器會(huì)隨時(shí)發(fā)生變化。在完成這個(gè)組件后,通過這篇文章總結(jié)一下。

UI/原型分析

那么從上面的功能描述以及項(xiàng)目中的UI,我們可以分析得到這樣一個(gè)假想圖:

image.png

  • 我們需要實(shí)現(xiàn)一個(gè)容器來作為我們的可視區(qū)域,并且這個(gè)容器是可以伸縮的。
  • 列表內(nèi)容如果超出容器的可視區(qū)域,那么就會(huì)被隱藏。
  • 需要左右都有按鈕,來支持用戶左右滑動(dòng)內(nèi)容來查看,每次滑動(dòng)距離為 容器的寬度,也就是可視區(qū)域
  • 當(dāng)在伸縮容器的時(shí)候,如果是向右側(cè)伸縮,并且可視區(qū)域已經(jīng)被拉伸的超出了列表被隱藏的右側(cè)內(nèi)容時(shí),右側(cè)的隱藏內(nèi)容需要有一個(gè)吸附效果,即跟著被伸縮的容器移動(dòng),直到左側(cè)隱藏內(nèi)容的偏移值為0。

話不多說,我們先上簡單的最終效果圖

有固定寬度,不可伸縮

slider-demo.gif

  • 無固定寬度,可伸縮(這里直接用幣安的效果來展示,如果你有興趣,可以自己下載個(gè)react-grid-layout或者其他庫來試一下效果)

mxefs-bl7av.gif

功能實(shí)現(xiàn)

監(jiān)聽元素尺寸變化

工欲善其事必先利其器

在分析完后,我們發(fā)現(xiàn)有一個(gè)點(diǎn)。如果要支持react-grid-layout這類的伸縮功能,需要能夠監(jiān)聽到元素的動(dòng)態(tài)變化。

那么,我們可以先將這部分邏輯抽離,封裝成一個(gè)hook:

hooks/useResizeObserver.ts

import { useLayoutEffect, useState } from "react";
// 接收保存被監(jiān)聽dom的ref
const useResizeObserver = (ref: React.RefObject<HTMLElement>) => {
  const [width, setWidth] = useState<number>(0);
  useLayoutEffect(() => {
     // 使用ResizeObserver來監(jiān)聽DOM的變化
    const resizeObserver = new ResizeObserver(() => {
      setWidth((ref.current as HTMLElement).clientWidth);
    });
    resizeObserver.observe(ref.current as HTMLElement);
    return () => {
      resizeObserver.disconnect();
    };
  }, [ref]);
  return width;
};
export default useResizeObserver;

其中核心的邏輯是使用ResizeObserver類來監(jiān)聽一個(gè)元素的尺寸變化。然后返回變化后的width

組件開發(fā)。

有了上面的分析,再實(shí)現(xiàn)代碼就是一步步走即可。所以我們直接貼代碼:

components/SliderContainer/index.tsx

import React, {
  useMemo,
  useState,
  useRef,
  useLayoutEffect,
  useEffect,
} from "react";
import type { ReactElement } from "react";
import "./index.css";
import ArrowLeft from "@/assets/arrow-left.svg";
import ArrowRight from "@/assets/arrow-right.svg";
import useResizeObserver from "@/hooks/useResizeObserver";
export interface SliderContainerProps {
  width: number;
  children: ReactElement; // 需要包括的內(nèi)容
}
const LEFT = "left";
const RIGHT = "right";
export const SliderContainer: React.FC<SliderContainerProps> = ({
  width = "inherit",
  children,
}) => {
  const listRef = useRef<HTMLDivElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const containerWidth = useResizeObserver(containerRef);
  const [listWidth, setListWidth] = useState(0);
  const [translateX, setTranslateX] = useState(0);
  // 緩存
  const cache = useRef(containerWidth);
  // 處理容器寬度變化時(shí),內(nèi)部元素的吸附效果
  useEffect(() => {
    if (
      containerWidth > cache.current && // 當(dāng)容器可拖拽時(shí),表示用戶正在向右拖拽
      translateX < 0 && // 表示左側(cè)有內(nèi)容被隱藏
      listWidth - Math.abs(translateX) - containerWidth <= 0 //表示右側(cè)已經(jīng)沒有被隱藏的內(nèi)容了
    ) {
      const distance = containerWidth - cache.current;
      setTranslateX((cur) => cur + distance);
    }
    // 更新緩存
    cache.current = containerWidth;
  }, [containerWidth, translateX, listWidth]);
  useLayoutEffect(() => {
    setListWidth((listRef.current as HTMLDivElement).clientWidth);
  }, [children]);
  // 判斷按鈕是否可見
  const [leftArrowVisible, rightArrowVisible] = useMemo(() => {
    let leftArrowVisible,
      rightArrowVisible = false;
    // listWidth - Math.abs(translateX) - containerWidth 為右側(cè)隱藏內(nèi)容
    if (listWidth - Math.abs(translateX) - containerWidth > 0) {
      rightArrowVisible = true;
    }
    if (translateX < 0) {
      leftArrowVisible = true;
    }
    return [leftArrowVisible, rightArrowVisible];
  }, [listWidth, translateX, containerWidth]);
  const handleArrowClick = (direction: string) => {
    if (direction === LEFT) {
      // 左側(cè)隱藏內(nèi)容
      const leftSpaceWidth = Math.abs(translateX);
      if (leftSpaceWidth > containerWidth) {
        setTranslateX((cur) => cur + containerWidth);
      } else {
        setTranslateX((cur) => cur + leftSpaceWidth);
      }
    }
    if (direction === RIGHT) {
      // 右側(cè)隱藏內(nèi)容
      const rightSpaceWidth = listWidth - Math.abs(translateX) - containerWidth;
      if (rightSpaceWidth > containerWidth) {
        setTranslateX((cur) => cur - containerWidth);
      } else {
        setTranslateX((cur) => cur - rightSpaceWidth);
      }
    }
  };
  return (
    <div ref={containerRef} style={{ width: width }} className="container">
      {leftArrowVisible && (
        <>
          <button
            color="white"
            className="leftArrow btn"
            onClick={() => handleArrowClick(LEFT)}
          >
            <img src={ArrowLeft} alt="" />
          </button>
          <div className="linerGrid leftGradient"></div>
        </>
      )}
      <div
        ref={listRef}
        className="list"
        style={{
          transform: `translateX(${translateX}px)`,
          transition: "all 0.3s linear",
        }}
      >
        {children}
      </div>
      {rightArrowVisible && (
        <>
          <div className="linerGrid rightGradient"></div>
          <button
            color="white"
            className="rightArrow btn"
            onClick={() => handleArrowClick(RIGHT)}
          >
            <img src={ArrowRight} alt="" />
          </button>
        </>
      )}
    </div>
  );
};

組件目前設(shè)置了兩個(gè)屬性widthchildren

  • width: 如果不傳,默認(rèn)inherit,表示該容器是可伸縮的,那么組件內(nèi)部會(huì)自己計(jì)算;如果傳了固定寬度,則按照該固定寬度來設(shè)置
  • children: 需要滾動(dòng)的內(nèi)容

其中,主要是有5個(gè)點(diǎn)需要著重理解

  • useLayoutEffect中,需要通過傳入的children來判斷是否需要更新list的長度,防止計(jì)算不準(zhǔn)確
  • 判斷按鈕何時(shí)顯示
  • 按鈕點(diǎn)擊時(shí)需要處理的UI邏輯
  • 如果容器是可伸縮的,需要通過useRef的緩存來判斷用戶向哪個(gè)方向伸縮容器
  • 使用transformtransition讓動(dòng)畫更流暢自然

對應(yīng)的css樣式為:

components/SliderContainer/index.css

.container {
  display: flex;
  overflow: hidden;
  position: relative;
  height: 100%;
}
.list {
  display: flex;
  align-items: center;
}
.btn {
  width: 40px;
  height: 100%;
  position: absolute;
  z-index: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}
.leftArrow {
  left: 0;
}
.rightArrow {
  right: 0;
  z-index: 0;
}
.linerGrid {
  position: absolute;
  width: 20px;
  height: 100%;
}
.leftGradient {
  left: 40px;
  z-index: 1;
  background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0.2));
}
.rightGradient {
  right: 40px;
  background: linear-gradient(269.21deg, #ffffff, rgba(255, 255, 255, 0.2));
}

使用方式

我們可以通過下面的方式來使用

const list = [
  { key: "1", name: "列表項(xiàng)1" },
  { key: "2", name: "列表項(xiàng)2" },
  { key: "3", name: "列表項(xiàng)3" },
  { key: "4", name: "列表項(xiàng)4" },
  { key: "5", name: "列表項(xiàng)5" },
  { key: "6", name: "列表項(xiàng)6" },
  { key: "7", name: "列表項(xiàng)7" },
  { key: "8", name: "列表項(xiàng)8" },
  { key: "9", name: "列表項(xiàng)9" },
  { key: "10", name: "列表項(xiàng)10" },
];
function App() {
  return (
    <div className="App">
      <SliderContainer width={300}>
        <>
          {list.map((item) => (
            <div style={{ width: 100 }} key={item.key}>
              {item.name}
            </div>
          ))}
        </>
      </SliderContainer>
    </div>
  );
}

到此這篇關(guān)于使用React實(shí)現(xiàn)內(nèi)容滑動(dòng)組件效果的文章就介紹到這了,更多相關(guān)React滑動(dòng)組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • react組件中過渡動(dòng)畫的問題解決

    react組件中過渡動(dòng)畫的問題解決

    這篇文章主要為大家介紹了react組件中過渡動(dòng)畫的問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • react:swr接口緩存案例代碼

    react:swr接口緩存案例代碼

    useSWR 是一個(gè) React Hooks,是 HTTP 緩存庫 SWR 的核心方法之一,SWR 是一個(gè)輕量級的 React Hooks 庫,通過自動(dòng)緩存數(shù)據(jù)來實(shí)現(xiàn) React 的數(shù)據(jù)獲取,本文給大家介紹react:swr接口緩存案例詳解,感興趣的朋友一起看看吧
    2023-11-11
  • React?正確使用useCallback?useMemo的方式

    React?正確使用useCallback?useMemo的方式

    這篇文章主要介紹了React?正確使用useCallback?useMemo的方式,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下
    2022-08-08
  • 詳解在React.js中使用PureComponent的重要性和使用方式

    詳解在React.js中使用PureComponent的重要性和使用方式

    這篇文章主要介紹了詳解在React.js中使用PureComponent的重要性和使用方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • React?Streaming?SSR原理示例深入解析

    React?Streaming?SSR原理示例深入解析

    這篇文章主要為大家介紹了React?Streaming?SSR原理示例深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 關(guān)于React中setState同步或異步問題的理解

    關(guān)于React中setState同步或異步問題的理解

    相信很多小伙伴們都一直在疑惑,setState 到底是同步還是異步。本文就詳細(xì)的介紹一下React中setState同步或異步問題,感興趣的可以了解一下
    2021-11-11
  • React+TypeScript項(xiàng)目中使用CodeMirror的步驟

    React+TypeScript項(xiàng)目中使用CodeMirror的步驟

    CodeMirror被廣泛應(yīng)用于許多Web應(yīng)用程序和開發(fā)工具,之前做需求用到過codeMirror這個(gè)工具,覺得還不錯(cuò),功能很強(qiáng)大,所以記錄一下改工具的基礎(chǔ)用法,對React+TypeScript項(xiàng)目中使用CodeMirror的步驟感興趣的朋友跟隨小編一起看看吧
    2023-07-07
  • ReactNative支付密碼輸入框?qū)崿F(xiàn)詳解

    ReactNative支付密碼輸入框?qū)崿F(xiàn)詳解

    這篇文章主要為大家介紹了ReactNative支付密碼輸入框?qū)崿F(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • React中的JSX??{?}的使用詳解

    React中的JSX??{?}的使用詳解

    這篇文章主要介紹了React中的JSX{?}的使用,React使用JSX來替代常規(guī)的JavaScript,JSX可以理解為的JavaScript語法擴(kuò)展,它里面的標(biāo)簽申明要符合XML規(guī)范要求,對React?JSX使用感興趣的朋友一起看看吧
    2022-08-08
  • 如何將你的AngularJS1.x應(yīng)用遷移至React的方法

    如何將你的AngularJS1.x應(yīng)用遷移至React的方法

    本篇文章主要介紹了如何將你的AngularJS1.x應(yīng)用遷移至React的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02

最新評論