Skip to content

Commit

Permalink
fix: tabs组件在抖音小程序展示异常 (#1211)
Browse files Browse the repository at this point in the history
  • Loading branch information
oasis-cloud authored Jul 14, 2023
1 parent 6871ce5 commit 549786d
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 43 deletions.
2 changes: 2 additions & 0 deletions src/packages/tabs/demo.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ const TabsDemo = () => {
</Tabs>
<h2>{translated.title4}</h2>
<Tabs
name="a1"
value={tab91value}
onChange={(value) => {
setTab91value(value)
Expand All @@ -296,6 +297,7 @@ const TabsDemo = () => {
</Tabs>
<h2>{translated.title4}</h2>
<Tabs
name="a2"
value={tab92value}
style={{ height: '300px' }}
onChange={(value) => {
Expand Down
23 changes: 23 additions & 0 deletions src/packages/tabs/tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
width: 0;
background: transparent;
}
.nut-tabs__list {
width: 100%;
height: auto;
display: flex;
flex-shrink: 0;
}
}

.nut-tabs__titles--left {
Expand Down Expand Up @@ -53,6 +59,11 @@
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

&.taro {
height: 46px;
line-height: 46px;
}
}

.nut-tabs__titles-item--left,
Expand Down Expand Up @@ -129,6 +140,12 @@
padding: 0;
width: $tabs-vertical-titles-width;
flex-shrink: 0;
.nut-tabs__list {
width: 100%;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
}

.nut-tabs__titles--scrollable {
Expand Down Expand Up @@ -181,6 +198,12 @@
overflow-x: auto;
overflow-y: hidden;
height: $tabs-horizontal-titles-height;
.nut-tabs__list {
width: 100%;
display: flex;
flex-direction: row;
flex-shrink: 0;
}
}

.nut-tabs__content {
Expand Down
225 changes: 182 additions & 43 deletions src/packages/tabs/tabs.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import React, { FunctionComponent, useEffect, useRef, useState } from 'react'
import { ScrollView, View } from '@tarojs/components'
import classNames from 'classnames'
import { JoySmile } from '@nutui/icons-react-taro'
import Taro, { nextTick } from '@tarojs/taro'
import { BasicComponent, ComponentDefaults } from '@/utils/typings'
import TabPane from '@/packages/tabpane/index.taro'
import { usePropsValue } from '@/utils/use-props-value'
import { useForceUpdate } from '@/utils/use-force-update'
import raf from '@/utils/raf'

export type TabsTitle = {
title: string
Expand All @@ -19,6 +21,7 @@ export interface TabsProps extends BasicComponent {
value: string | number
defaultValue: string | number
activeColor: string
name: string
direction: 'horizontal' | 'vertical'
activeType: 'line' | 'smile'
duration: number | string
Expand All @@ -33,6 +36,7 @@ export interface TabsProps extends BasicComponent {
const defaultProps = {
...ComponentDefaults,
tabStyle: {},
name: '',
activeColor: '',
direction: 'horizontal',
activeType: 'line',
Expand All @@ -52,6 +56,7 @@ export const Tabs: FunctionComponent<Partial<TabsProps>> & {
duration,
align,
title,
name,
children,
onClick,
onChange,
Expand Down Expand Up @@ -122,6 +127,131 @@ export const Tabs: FunctionComponent<Partial<TabsProps>> & {
color: activeType === 'smile' ? activeColor : '',
background: activeType === 'line' ? activeColor : '',
}
const getRect = (selector: string) => {
return new Promise((resolve) => {
Taro.createSelectorQuery()
.select(selector)
.boundingClientRect()
.exec((rect = []) => {
resolve(rect[0])
})
})
}
const getAllRect = (selector: string) => {
return new Promise((resolve) => {
Taro.createSelectorQuery()
.selectAll(selector)
.boundingClientRect()
.exec((rect = []) => resolve(rect[0]))
})
}
type RectItem = {
bottom: number
dataset: { sid: string }
height: number
id: string
left: number
right: number
top: number
width: number
}
const scrollWithAnimation = useRef(false)
const navRectRef = useRef<any>()
const titleRectRef = useRef<RectItem[]>([])
const canShowLabel = useRef(false)
const scrollLeft = useRef(0)
const scrollTop = useRef(0)
const scrollDirection = (
to: number,
direction: 'horizontal' | 'vertical'
) => {
let count = 0
const from =
direction === 'horizontal' ? scrollLeft.current : scrollTop.current
const frames = 1

function animate() {
if (direction === 'horizontal') {
scrollLeft.current += (to - from) / frames
} else {
scrollTop.current += (to - from) / frames
}

if (++count < frames) {
raf(animate)
}
}

animate()
}
const scrollIntoView = () => {
console.log(name, props)
if (!name) return

raf(() => {
Promise.all([
getRect(`#nut-tabs__titles_${name}`),
getAllRect(`#nut-tabs__titles_${name} .nut-tabs__titles-item`),
]).then(([navRect, titleRects]: any) => {
navRectRef.current = navRect
titleRectRef.current = titleRects

if (navRectRef.current) {
if (props.direction === 'vertical') {
const titlesTotalHeight = titleRects.reduce(
(prev: number, curr: RectItem) => prev + curr.height,
0
)
if (titlesTotalHeight > navRectRef.current.height) {
canShowLabel.current = true
} else {
canShowLabel.current = false
}
} else {
const titlesTotalWidth = titleRects.reduce(
(prev: number, curr: RectItem) => prev + curr.width,
0
)
if (titlesTotalWidth > navRectRef.current.width) {
canShowLabel.current = true
} else {
canShowLabel.current = false
}
}
}

// @ts-ignore
const titleRect: RectItem = titleRectRef.current[value]

let to = 0
if (props.direction === 'vertical') {
const DEFAULT_PADDING = 11
const top = titleRects
.slice(0, value)
.reduce(
(prev: number, curr: RectItem) => prev + curr.height + 0,
DEFAULT_PADDING
)
to = top - (navRectRef.current.height - titleRect.height) / 2
} else {
const DEFAULT_PADDING = 31
const left = titleRects
.slice(0, value)
.reduce(
(prev: number, curr: RectItem) => prev + curr.width + 20,
DEFAULT_PADDING
)
to = left - (navRectRef.current.width - titleRect.width) / 2
}

nextTick(() => {
scrollWithAnimation.current = true
})

scrollDirection(to, direction)
})
})
}

const scrollIntoRef = useRef(0)
useEffect(() => {
Expand All @@ -135,6 +265,8 @@ export const Tabs: FunctionComponent<Partial<TabsProps>> & {
})
const scrollToIndex = index - 2
scrollIntoRef.current = scrollToIndex < 0 ? 0 : scrollToIndex

scrollIntoView()
}, [value])

const tabChange = (item: TabsTitle, index: number) => {
Expand All @@ -151,59 +283,66 @@ export const Tabs: FunctionComponent<Partial<TabsProps>> & {
enableFlex
scrollX={direction === 'horizontal'}
scrollY={direction === 'vertical'}
scrollLeft={scrollLeft.current}
scrollTop={scrollTop.current}
showScrollbar={false}
scrollIntoViewAlignment="center"
scrollWithAnimation
scrollIntoView={`scrollIntoView${scrollIntoRef.current}`}
scrollWithAnimation={scrollWithAnimation.current}
// scrollIntoView={`scrollIntoView${scrollIntoRef.current}`}
id={`nut-tabs__titles_${name}`}
className={classesTitle}
style={{ ...tabStyle }}
ref={navRef}
>
{!!title && typeof title === 'function'
? title()
: titles.current.map((item, index) => {
return (
<View
ref={(ref: HTMLDivElement) => titleItemsRef.current.push(ref)}
id={`scrollIntoView${index}`}
onClick={(e) => {
tabChange(item, index)
}}
className={classNames(`${classPrefix}__titles-item`, {
[`nut-tabs__titles-item--active`]:
!item.disabled && String(item.value) === String(value),
[`nut-tabs__titles-item--disabled`]: item.disabled,
[`nut-tabs__titles-item--${align}`]: align,
})}
key={item.value}
>
{activeType === 'line' && (
<View
className={`${classPrefix}__titles-item__line`}
style={tabsActiveStyle}
/>
)}
{activeType === 'smile' && (
<View className="nut-tabs__list">
{!!title && typeof title === 'function'
? title()
: titles.current.map((item, index) => {
return (
<View
ref={(ref: HTMLDivElement) =>
titleItemsRef.current.push(ref)
}
id={`scrollIntoView${index}`}
onClick={(e) => {
tabChange(item, index)
}}
className={classNames(`${classPrefix}__titles-item taro`, {
[`nut-tabs__titles-item--active`]:
!item.disabled && String(item.value) === String(value),
[`nut-tabs__titles-item--disabled`]: item.disabled,
[`nut-tabs__titles-item--${align}`]: align,
})}
key={item.value}
>
{activeType === 'line' && (
<View
className={`${classPrefix}__titles-item__line`}
style={tabsActiveStyle}
/>
)}
{activeType === 'smile' && (
<View
className={`${classPrefix}__titles-item__smile`}
style={tabsActiveStyle}
>
<JoySmile color={activeColor} width={40} height={20} />
</View>
)}
<View
className={`${classPrefix}__titles-item__smile`}
style={tabsActiveStyle}
className={classNames(
{
ellipsis: true,
},
`${classPrefix}__titles-item__text`
)}
>
<JoySmile color={activeColor} width={40} height={20} />
{item.title}
</View>
)}
<View
className={classNames(
{
ellipsis: true,
},
`${classPrefix}__titles-item__text`
)}
>
{item.title}
</View>
</View>
)
})}
)
})}
</View>
</ScrollView>
<View className={`${classPrefix}__content__wrap`}>
<View className={`${classPrefix}__content`} style={contentStyle}>
Expand Down

0 comments on commit 549786d

Please sign in to comment.