React實(shí)現(xiàn)錨點(diǎn)跳轉(zhuǎn)組件附帶吸頂效果的示例代碼
React實(shí)現(xiàn)錨點(diǎn)跳轉(zhuǎn)組件附帶吸頂效果
import React, { useRef, useState, useEffect } from 'react'; import styles from './index.less'; import classnames from 'classnames'; function AnchorTabber(props) { const root = useRef(null); const header = useRef(null); const { attrbute = 'data-anchor', offsetY = 60, list = [], initialKey = list[0]?.key, children, } = props; const [state, setState] = useState({ activeKey: initialKey, }); const [key2Bottom, setKey2Bottom] = useState({ key2Bottom: {}, }); /** @type { HTMLDivElement } */ const scrollElement = document.querySelector('#root').firstChild; function scrollTo(key) { // if(!scrollElement) { // scrollElement = document.querySelector('#root').firstChild; // } const attribute = attrbute; /** @type { HTMLDivElement } */ const targetEl = root.current?.querySelector(`[${attribute}=${key}]`); if (targetEl) { const clientRect = targetEl.getBoundingClientRect(); const top = scrollElement.scrollTop + clientRect.top - offsetY; scrollElement.scrollTo({ top, behavior: 'smooth', }); // targetEl.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } function updateElementsPosition() { const elements = document.querySelectorAll(`[${attrbute}]`); Array.from(elements).forEach(element => { const targetAttr = element.getAttribute(`${attrbute}`); const clientRect = element.getBoundingClientRect(); const bottom = clientRect.top + scrollElement.scrollTop; key2Bottom[targetAttr] = bottom; }); setKey2Bottom(key2Bottom); } function handleScroll() { const top = scrollElement.scrollTop + offsetY; // eslint-disable-next-line no-unused-vars const target = Object.entries(key2Bottom) .sort(([, v1], [, v2]) => v2 - v1) .find(([, v]) => v <= top); if (target) { setState({ activeKey: target[0], }); } } useEffect(() => { updateElementsPosition(); // document.addEventListener('touchstart', updateElementsPosition); scrollElement.addEventListener('scroll', handleScroll); return () => { // document.removeEventListener('touchstart', updateElementsPosition); scrollElement.removeEventListener('scroll', handleScroll); }; }); function handleItemClick(tabber) { scrollTo(tabber.key); } function render() { let { activeKey } = state; if(typeof(activeKey) === "undefined") { activeKey = initialKey } return ( <div className={styles.mAnchorTabber}> <div className={styles.header} ref={header}> {list?.map(tabber => ( <div className={classnames(styles.item, { [styles.active]: activeKey === tabber.key })} onClick={() => handleItemClick(tabber)} > {tabber.name} </div> ))} </div> <div className={styles.container} ref={root}> {children} </div> </div> ); } return render(); } export default AnchorTabber;
對(duì)應(yīng)樣式(less)
.mAnchorTabber { .header { display: flex; align-items: center; position: sticky; top: 0; height: .len(46) []; box-sizing: border-box; box-shadow: 0 .len(2) [] .len(2) [] .len(1) [] #c4c4c4; background-color: @basecolor; .item { height: 100%; flex-grow: 1; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-weight: 400; font-size: @font-size-base-normal; line-height: .len(20) []; &.active { background-color: @bgc-product-detail; color: @basecolor; } } } }
.len(46) []這個(gè)改成對(duì)應(yīng) px單位即可,本單位是為了移動(dòng)端適配轉(zhuǎn)換
測(cè)試test
對(duì)應(yīng)測(cè)試文件
import React from 'react'; import { connect } from 'dva'; import AnchorTabber from '@/components/m/AnchorTabber'; @connect(({ common }) => ({ common, })) class ApplicationsRecord extends React.Component { constructor(props) { super(props); this.state = { list: [ { name: 'test1', key: 'test1', }, { name: 'test2', key: 'test2', }, { name: 'test3', key: 'test3', }, ], }; } render() { const { list } = this.state; const index2Attr = { 0: 'test1', 100: 'test2', 200: 'test3', }; return ( <AnchorTabber list={list}> <ul className="list"> {new Array(1000).fill(null).map((_item, index) => ( <li data-anchor={index2Attr[index]}>{index}</li> ))} </ul> </AnchorTabber> ); } } export default ApplicationsRecord;
到此這篇關(guān)于React實(shí)現(xiàn)錨點(diǎn)跳轉(zhuǎn)組件附帶吸頂效果的示例代碼的文章就介紹到這了,更多相關(guān)React錨點(diǎn)跳轉(zhuǎn)組件附帶吸頂效果內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React實(shí)現(xiàn)導(dǎo)入導(dǎo)出Excel文件
本文主要介紹了React實(shí)現(xiàn)導(dǎo)入導(dǎo)出Excel文件,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07react+ts實(shí)現(xiàn)簡(jiǎn)單jira項(xiàng)目的最佳實(shí)踐記錄
這篇文章主要介紹了react+ts實(shí)現(xiàn)簡(jiǎn)單jira項(xiàng)目,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07利用React-router+Webpack快速構(gòu)建react程序
目前 React、Webpack 等技術(shù)如火如荼,你是不是還在愁苦如何把這些雜亂的知識(shí)怎么學(xué)習(xí)一下,開(kāi)啟一段新的前端開(kāi)發(fā)之路呢?那么這篇將給大家運(yùn)用示例代碼詳細(xì)的介紹使用React-router和Webpack如何快速構(gòu)建一個(gè)react程序,感興趣的朋友們下面來(lái)一起看看吧。2016-10-10解決React報(bào)錯(cuò)Cannot assign to 'current'
這篇文章主要為大家介紹了React報(bào)錯(cuò)Cannot assign to 'current' because it is a read-only property的解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12封裝一個(gè)最簡(jiǎn)單ErrorBoundary組件處理react異常
這篇文章主要介紹了一個(gè)處理react異常的ErrorBoundary組件,簡(jiǎn)單實(shí)用,代碼詳細(xì),對(duì)這個(gè)組件感興趣的朋友可以參考下2021-04-04React實(shí)現(xiàn)一個(gè)倒計(jì)時(shí)hook組件實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了React實(shí)現(xiàn)一個(gè)倒計(jì)時(shí)hook組件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02基于React實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要為大家詳細(xì)介紹了如何基于React實(shí)現(xiàn)倒計(jì)時(shí)功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考一下2024-02-02