Skip to content

Commit

Permalink
feat(indexes): 右侧索引跟随滚动区域高亮 #1693
Browse files Browse the repository at this point in the history
  • Loading branch information
robinv8 committed Jan 23, 2024
1 parent 28e47b6 commit 0140a61
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 11 deletions.
58 changes: 53 additions & 5 deletions packages/taro-ui/rn/components/indexes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default class AtIndexes extends React.Component<
private listId: string
private timeoutTimer: NodeJS.Timeout | number | undefined
private listRef: any
private indexMap: { key: string; startHeight: number; endHeight: number }[]

public constructor(props: AtIndexesProps) {
super(props)
Expand All @@ -39,7 +40,8 @@ export default class AtIndexes extends React.Component<
_scrollTop: 0,
_tipText: '',
_isShowToast: false,
isWEB: Taro.getEnv() === Taro.ENV_TYPE.WEB
isWEB: Taro.getEnv() === Taro.ENV_TYPE.WEB,
currentIndex: -1
}
// 右侧导航高度
this.menuHeight = 0
Expand All @@ -50,6 +52,7 @@ export default class AtIndexes extends React.Component<
// 当前索引
this.currentIndex = -1
this.listId = isTest() ? 'indexes-list-AOTU2018' : `list-${uuid()}`
this.indexMap = []
}

private handleClick = (item: Item): void => {
Expand Down Expand Up @@ -136,23 +139,65 @@ export default class AtIndexes extends React.Component<
}
}

private initData(): void {
private async initData(): Promise<void> {
delayQuerySelector('.at-indexes__menu').then(rect => {
const len = this.props.list.length
this.menuHeight = rect[0].height
this.startTop = rect[0].top
this.itemHeight = Math.floor(this.menuHeight / (len + 1))
})

const headerHeight =
(await delayQuerySelector('#at-indexes__top'))?.[0]?.height || 0
const itemHeight =
(await delayQuerySelector('.at-list__item'))?.[0].height || 0
const titleHeight =
(await delayQuerySelector('.at-indexes__list-title'))?.[0].height || 0

this.indexMap = []
this.props.list.forEach((dataList, i) => {
if (i === 0) {
this.indexMap.push({
key: dataList.key,
startHeight: headerHeight,
endHeight:
dataList.items.length * itemHeight + headerHeight + titleHeight
})
} else {
const prev = this.indexMap[i - 1]
this.indexMap.push({
key: dataList.key,
startHeight: prev.endHeight,
endHeight:
prev.endHeight + dataList.items.length * itemHeight + titleHeight
})
}
})
}

private handleScroll(e: CommonEvent): void {
if (e && e.detail) {
const scrollTop = e.detail.scrollTop

this.setState({
_scrollTop: e.detail.scrollTop
_scrollTop: scrollTop
})

this.getAnchorIndex(scrollTop)
}
}

// 根据滚动高度,判断当前应该显示的索引值
private getAnchorIndex(scrollTop: number) {
const index = this.indexMap.findIndex(item => {
return scrollTop >= item.startHeight && scrollTop < item.endHeight
})

this.setState({
currentIndex: index
})
}

public UNSAFE_componentWillReceiveProps(nextProps: AtIndexesProps): void {
if (nextProps.list.length !== this.props.list.length) {
this.initData()
Expand All @@ -178,7 +223,8 @@ export default class AtIndexes extends React.Component<
_scrollIntoView,
_tipText,
_isShowToast,
isWEB
isWEB,
currentIndex
} = this.state

const toastStyle = { minWidth: pxTransform(100) }
Expand All @@ -189,7 +235,9 @@ export default class AtIndexes extends React.Component<
const targetView = `at-indexes__list-${key}`
return (
<View
className='at-indexes__menu-item'
className={classNames('at-indexes__menu-item', {
'at-indexes__menu-item--active': currentIndex === i
})}
key={key}
onClick={this.jumpTarget.bind(this, targetView, i + 1)}
>
Expand Down
65 changes: 59 additions & 6 deletions packages/taro-ui/src/components/indexes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default class AtIndexes extends React.Component<
private listId: string
private timeoutTimer: NodeJS.Timeout | number | undefined
private listRef: any
private indexMap: { key: string; startHeight: number; endHeight: number }[]

public constructor(props: AtIndexesProps) {
super(props)
Expand All @@ -39,7 +40,8 @@ export default class AtIndexes extends React.Component<
_scrollTop: 0,
_tipText: '',
_isShowToast: false,
isWEB: Taro.getEnv() === Taro.ENV_TYPE.WEB
isWEB: Taro.getEnv() === Taro.ENV_TYPE.WEB,
currentIndex: -1
}
// 右侧导航高度
this.menuHeight = 0
Expand All @@ -50,6 +52,7 @@ export default class AtIndexes extends React.Component<
// 当前索引
this.currentIndex = -1
this.listId = isTest() ? 'indexes-list-AOTU2018' : `list-${uuid()}`
this.indexMap = []
}

private handleClick = (item: Item): void => {
Expand Down Expand Up @@ -136,23 +139,65 @@ export default class AtIndexes extends React.Component<
}
}

private initData(): void {
private async initData(): Promise<void> {
delayQuerySelector('.at-indexes__menu').then(rect => {
const len = this.props.list.length
this.menuHeight = rect[0].height
this.startTop = rect[0].top
this.itemHeight = Math.floor(this.menuHeight / (len + 1))
})

const headerHeight =
(await delayQuerySelector('#at-indexes__top'))?.[0]?.height || 0
const itemHeight =
(await delayQuerySelector('.at-list__item'))?.[0].height || 0
const titleHeight =
(await delayQuerySelector('.at-indexes__list-title'))?.[0].height || 0

this.indexMap = []
this.props.list.forEach((dataList, i) => {
if (i === 0) {
this.indexMap.push({
key: dataList.key,
startHeight: headerHeight,
endHeight:
dataList.items.length * itemHeight + headerHeight + titleHeight
})
} else {
const prev = this.indexMap[i - 1]
this.indexMap.push({
key: dataList.key,
startHeight: prev.endHeight,
endHeight:
prev.endHeight + dataList.items.length * itemHeight + titleHeight
})
}
})
}

private handleScroll(e: CommonEvent): void {
if (e && e.detail) {
const scrollTop = e.detail.scrollTop

this.setState({
_scrollTop: e.detail.scrollTop
_scrollTop: scrollTop
})

this.getAnchorIndex(scrollTop)
}
}

// 根据滚动高度,判断当前应该显示的索引值
private getAnchorIndex(scrollTop: number) {
const index = this.indexMap.findIndex(item => {
return scrollTop >= item.startHeight && scrollTop < item.endHeight
})

this.setState({
currentIndex: index
})
}

public UNSAFE_componentWillReceiveProps(nextProps: AtIndexesProps): void {
if (nextProps.list.length !== this.props.list.length) {
this.initData()
Expand All @@ -173,8 +218,14 @@ export default class AtIndexes extends React.Component<

public render(): JSX.Element {
const { className, customStyle, animation, topKey, list } = this.props
const { _scrollTop, _scrollIntoView, _tipText, _isShowToast, isWEB } =
this.state
const {
_scrollTop,
_scrollIntoView,
_tipText,
_isShowToast,
isWEB,
currentIndex
} = this.state

const toastStyle = { minWidth: pxTransform(100) }
const rootCls = classNames('at-indexes', className)
Expand All @@ -184,7 +235,9 @@ export default class AtIndexes extends React.Component<
const targetView = `at-indexes__list-${key}`
return (
<View
className='at-indexes__menu-item'
className={classNames('at-indexes__menu-item', {
'at-indexes__menu-item--active': currentIndex === i
})}
key={key}
onClick={this.jumpTarget.bind(this, targetView, i + 1)}
>
Expand Down
6 changes: 6 additions & 0 deletions packages/taro-ui/src/style/components/indexes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
color: $at-indexes-nav-color;
font-size: $at-indexes-nav-font-size;
text-align: center;

&--active {
background-color: $at-indexes-nav-color;
color: $color-white;
border-radius: $border-radius-md;
}
}

&__body {
Expand Down
1 change: 1 addition & 0 deletions packages/taro-ui/types/indexes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface AtIndexesState {
_tipText: string
_isShowToast: boolean
isWEB: boolean
currentIndex: number
}

declare const AtIndexes: ComponentClass<AtIndexesProps>
Expand Down

0 comments on commit 0140a61

Please sign in to comment.