* fix(tabs): fix showMoreTabs overflow calculation lag caused by CSS transition The nav element has CSS transition: transform 0.3s, which causes getBoundingClientRect() to return pre-transition positions when calcMorePanes runs in requestAnimationFrame (before the transition has advanced). Fix: use tab-relative-to-nav position delta (transition-invariant since both share the same transform coordinate space) plus navOffset to compute the final expected positions, bypassing the transition intermediate state entirely. Also replace nextTick with requestAnimationFrame in deferred calcMorePanes calls to ensure all cascading DOM updates from scrollToActiveTab -> updated() chains have settled. * fix(tab-nav): trigger calcMorePanes after scrollPrev/scrollNext arrow click * test(tabs): 修正 custom-more-icon 溢出下拉数量断言 showMoreTabs 溢出计算修正后按真实布局计算,容器 400px 减去 更多按钮占位后仅 5 个 tab 完整可见,下拉应为 Tab 6/7/8 共 3 项, 原断言 2 是基于旧实现 mount 早期量宽了 navScroll 的错误结果。
This commit is contained in:
parent
f83a76b7c3
commit
fdb2129f86
|
|
@ -18,7 +18,8 @@ test('"定义更多按钮"', async ({ page }) => {
|
|||
|
||||
await expect(listWidth).toBeGreaterThan(boxWidth)
|
||||
await expect(tabItems).toHaveCount(8)
|
||||
await expect(dropdownItems).toHaveCount(2)
|
||||
// 溢出计算修正后按真实布局计算:容器 400px 减去"更多"按钮占位后,仅 5 个 tab 完整可见,下拉为 Tab 6/7/8 共 3 项
|
||||
await expect(dropdownItems).toHaveCount(3)
|
||||
await expect(headerBox).toHaveCSS('overflow', 'hidden')
|
||||
await moreIcon.hover()
|
||||
await dropdownLastItem.click()
|
||||
|
|
|
|||
|
|
@ -229,6 +229,11 @@ export const scrollPrev =
|
|||
const newOffset = currentOffset > containerSize ? currentOffset - containerSize : 0
|
||||
|
||||
state.navOffset = newOffset
|
||||
|
||||
// 箭头滚动后通知父级 Tabs 重新计算溢出,用 rAF 等待级联 DOM 更新落定
|
||||
requestAnimationFrame(() => {
|
||||
;(state.rootTabs as Record<string, unknown>)?.calcMorePanes?.()
|
||||
})
|
||||
}
|
||||
|
||||
export const scrollNext =
|
||||
|
|
@ -246,6 +251,11 @@ export const scrollNext =
|
|||
navSize - currentOffset > containerSize * 2 ? currentOffset + containerSize : navSize - containerSize
|
||||
|
||||
state.navOffset = newOffset
|
||||
|
||||
// 箭头滚动后通知父级 Tabs 重新计算溢出,用 rAF 等待级联 DOM 更新落定
|
||||
requestAnimationFrame(() => {
|
||||
;(state.rootTabs as Record<string, unknown>)?.calcMorePanes?.()
|
||||
})
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export const calcMorePanes =
|
|||
|
||||
const el = parent.$el
|
||||
const tabs = el.querySelectorAll('.tiny-tabs__item')
|
||||
const tabNavRefs = refs.nav.$refs
|
||||
const navScrollEl = refs.nav.$refs.navScroll as HTMLElement
|
||||
|
||||
// 此处不同步aui。新规范适配
|
||||
if (props.moreShowAll) {
|
||||
|
|
@ -129,29 +129,40 @@ export const calcMorePanes =
|
|||
return
|
||||
}
|
||||
|
||||
if (tabs && tabs.length) {
|
||||
let tabsAllWidth = 0
|
||||
if (!tabs || !tabs.length) {
|
||||
return
|
||||
}
|
||||
|
||||
if (state.currentIndex === -1) {
|
||||
state.currentIndex = state.panes.findIndex((item) => item.state.paneName === state.currentName)
|
||||
}
|
||||
const currentIndex = state.currentIndex < 0 ? 0 : state.currentIndex
|
||||
const tabsHeaderWidth = tabNavRefs.navScroll.offsetWidth
|
||||
if (state.currentIndex === -1) {
|
||||
state.currentIndex = state.panes.findIndex((item) => item.state.paneName === state.currentName)
|
||||
}
|
||||
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tabItem = tabs[i] as HTMLElement
|
||||
// 遮住元素一半则隐藏
|
||||
tabsAllWidth = tabItem.offsetLeft + tabItem.offsetWidth / 2
|
||||
if (tabsAllWidth > tabsHeaderWidth && currentIndex >= 0) {
|
||||
if (currentIndex >= i + 1) {
|
||||
state.showPanesCount = currentIndex
|
||||
} else {
|
||||
state.showPanesCount = i
|
||||
}
|
||||
break
|
||||
}
|
||||
const navScrollRect = navScrollEl.getBoundingClientRect()
|
||||
const navScrollLeft = navScrollRect.left
|
||||
const navScrollRight = navScrollRect.right
|
||||
|
||||
// nav 元素上有 CSS transition: transform 0.3s,
|
||||
// 导致 scrollToActiveTab 改变 navOffset 后 getBoundingClientRect 返回过渡前的坐标。
|
||||
// 解决方案:用 tab 相对于 nav 的位置差(不受 transition 影响)+ navOffset 推算最终位置。
|
||||
const navEl = refs.nav.$refs.nav as HTMLElement
|
||||
const navRect = navEl.getBoundingClientRect()
|
||||
const stateNavOffset = ((refs.nav?.state as Record<string, unknown>)?.navOffset as number) || 0
|
||||
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tabRect = tabs[i].getBoundingClientRect()
|
||||
// tabOffsetInNav 不受 CSS transition 影响,因为 tab 和 nav 共享同一个 transform 坐标系
|
||||
const tabOffsetInNav = tabRect.left - navRect.left
|
||||
// 计算 navOffset 完全生效后的最终视口中心位置
|
||||
const finalCenter = navScrollLeft + tabOffsetInNav + tabRect.width / 2 - stateNavOffset
|
||||
// 遮住元素一半则隐藏
|
||||
if (finalCenter > navScrollRight) {
|
||||
state.showPanesCount = i
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 所有 tab 都在可视范围内,无需下拉菜单
|
||||
state.showPanesCount = tabs.length
|
||||
}
|
||||
|
||||
export const calcExpandPanes =
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export const api = [
|
|||
'state',
|
||||
'handleTabAdd',
|
||||
'calcPaneInstances',
|
||||
'calcMorePanes',
|
||||
'handleTabRemove',
|
||||
'handleTabClick',
|
||||
'handleTabDragStart',
|
||||
|
|
@ -83,6 +84,11 @@ const initWatcher = ({
|
|||
() => {
|
||||
nextTick(() => {
|
||||
refs.nav.scrollToActiveTab()
|
||||
// scrollToActiveTab → updated 调整 navOffset 可能触发多轮 DOM 更新,
|
||||
// 用 rAF 确保所有布局计算完成后再读取 getBoundingClientRect,消除滞后
|
||||
requestAnimationFrame(() => {
|
||||
api.calcMorePanes()
|
||||
})
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
|
|
@ -137,12 +143,21 @@ export const renderless = (
|
|||
api.calcPaneInstances()
|
||||
api.calcMorePanes()
|
||||
api.calcExpandPanes()
|
||||
// TabNav 在 render 中通过 $nextTick 异步 $forceUpdate,
|
||||
// 用 rAF 确保所有级联 DOM 更新完成后再重算
|
||||
requestAnimationFrame(() => {
|
||||
api.calcMorePanes()
|
||||
})
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
api.calcPaneInstances()
|
||||
api.calcMorePanes()
|
||||
api.calcExpandPanes()
|
||||
// 同上
|
||||
requestAnimationFrame(() => {
|
||||
api.calcMorePanes()
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
|
|
|||
|
|
@ -145,7 +145,13 @@ export default defineComponent({
|
|||
const TabNavComponent = h(TabNav, { ...navData })
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nav && this.$refs.nav.$forceUpdate()
|
||||
if (this.$refs.nav) {
|
||||
this.$refs.nav.$forceUpdate()
|
||||
// 用 rAF 确保级联 DOM 更新完成后再重算溢出
|
||||
requestAnimationFrame(() => {
|
||||
this.calcMorePanes()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const header = (
|
||||
|
|
|
|||
Loading…
Reference in New Issue