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)文章
關(guān)于React狀態(tài)管理的三個(gè)規(guī)則總結(jié)
隨著 JavaScript 單頁應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時(shí)候都要多的 state (狀態(tài)),這篇文章主要給大家介紹了關(guān)于React狀態(tài)管理的三個(gè)規(guī)則,需要的朋友可以參考下2021-07-07
react數(shù)據(jù)管理機(jī)制React.Context源碼解析
這篇文章主要為大家介紹了react數(shù)據(jù)管理機(jī)制React.Context源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
React中的路由嵌套和手動(dòng)實(shí)現(xiàn)路由跳轉(zhuǎn)的方式詳解
這篇文章主要介紹了React中的路由嵌套和手動(dòng)實(shí)現(xiàn)路由跳轉(zhuǎn)的方式,手動(dòng)路由的跳轉(zhuǎn),主要是通過Link或者NavLink進(jìn)行跳轉(zhuǎn)的,實(shí)際上我們也可以通JavaScript代碼進(jìn)行跳轉(zhuǎn),需要的朋友可以參考下2022-11-11
React項(xiàng)目中動(dòng)態(tài)插入HTML內(nèi)容的實(shí)現(xiàn)
本文主要介紹了React項(xiàng)目中動(dòng)態(tài)插入HTML內(nèi)容的實(shí)現(xiàn),通過使用React的dangerouslySetInnerHTML屬性,我們可以將HTML內(nèi)容插入到組件中,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
Rect Intersection判斷兩個(gè)矩形是否相交
這篇文章主要為大家介紹了Rect Intersection判斷兩個(gè)矩形是否相交的算法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06

