Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 121 additions & 2 deletions src/packages/noticebar/__test__/noticebar.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ test('vertical test move', async () => {
await act(() => {
expect(
container.querySelector('.nut-noticebar-box-horseLamp-list')
).toHaveAttribute('style', 'transition: all 0.8s; margin-top: -30px;')
).not.toHaveAttribute('style')
})
})

Expand Down Expand Up @@ -253,7 +253,6 @@ test('vertical container height calculation with children', async () => {
// 验证容器高度应该是 (childCount + 1) * height
// childCount = 4, height = 50, 所以期望高度是 (4 + 1) * 50 = 250px
const expectedHeight = `${(horseLamp1.length + 1) * height}px`
console.log(wrapElement, 'wrapElement')
expect(wrapElement).toHaveStyle(`height: ${expectedHeight}`)
}
},
Expand Down Expand Up @@ -352,3 +351,123 @@ test('dynamic children update test', async () => {
})
})
})

describe('NoticeBar Vertical Scrolling with refined timing logic', () => {
beforeEach(() => {
vi.useFakeTimers()
})

afterEach(() => {
vi.useRealTimers()
})

// 测试 list Prop 模式下的时间和动画
test('list mode: should correctly display items based on scrollList and style', async () => {
const listData = ['公告1', '公告2', '公告3']
const itemHeight = 30
const animationSpeed = 15
const calculatedAnimationTime = (30 / 15 / 4) * 1000
const pauseDuration = 2000

const { container } = render(
<NoticeBar
list={listData}
direction="vertical"
height={itemHeight}
speed={animationSpeed}
duration={pauseDuration}
/>
)

const listElement = container.querySelector<HTMLElement>(
'.nut-noticebar-box-horseLamp-list'
)
expect(listElement).toBeInTheDocument()
let items = listElement?.querySelectorAll(
'.nut-noticebar-box-horseLamp-list-item'
)

// 初始状态,显示第一项 "公告1"
expect(items?.[0]).toHaveTextContent('公告1')

// 第一次滚动
act(() => {
vi.advanceTimersByTime(pauseDuration + 1)
})
await waitFor(() => {
expect(listElement).toHaveStyle(`margin-top: -${itemHeight}px`)
expect(listElement).toHaveStyle(
`transition: all ${calculatedAnimationTime}ms`
)
})

// 动画结束并且开始下一轮
act(() => {
vi.advanceTimersByTime(calculatedAnimationTime - 1)
})
await waitFor(() => {
expect(listElement).not.toHaveStyle(`margin-top: -${itemHeight}px`)
items = listElement?.querySelectorAll(
'.nut-noticebar-box-horseLamp-list-item'
)
// 内部 scrollList.current 变为 ['公告2', '公告3', '公告1']
expect(items?.[0]).toHaveTextContent('公告2')
expect(items?.[1]).toHaveTextContent('公告3')
expect(items?.[2]).toHaveTextContent('公告1')
})
})

// 测试 children Prop 模式下的时间和动画
test('children mode: should use duration as pause, speed/height for animation', async () => {
const itemHeight = 30
const animationSpeed = 15
const calculatedAnimationTime = (30 / 15 / 4) * 1000
const pauseDuration = 2000

const { container } = render(
<NoticeBar
direction="vertical"
height={itemHeight}
speed={animationSpeed}
duration={pauseDuration}
>
<div className="child-item">公告1</div>
<div className="child-item">公告2</div>
<div className="child-item">公告3</div>
</NoticeBar>
)

const listElement = container.querySelector<HTMLElement>(
'.nut-noticebar-box-wrap'
)
expect(listElement).toBeInTheDocument()

// 第一次滚动
act(() => {
vi.advanceTimersByTime(pauseDuration + 1)
})

await waitFor(() => {
expect(listElement).toHaveStyle(
`transition-duration: ${calculatedAnimationTime}ms`
)
expect(listElement).toHaveStyle(
`transform: translate3D(0,-${itemHeight}px,0)`
)
})

// 开始下一轮滚动
act(() => {
vi.advanceTimersByTime(calculatedAnimationTime + pauseDuration)
})

await waitFor(() => {
expect(listElement).toHaveStyle(
`transition-duration: ${calculatedAnimationTime}ms`
)
expect(listElement).toHaveStyle(
`transform: translate3D(0,-${2 * itemHeight}px,0)`
)
})
})
})
2 changes: 1 addition & 1 deletion src/packages/noticebar/demos/h5/demo10.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const Demo9 = () => {
direction="vertical"
height={50}
speed={10}
duration={1000}
duration={3000}
closeable
onClose={() => {
console.log('close')
Expand Down
2 changes: 1 addition & 1 deletion src/packages/noticebar/demos/h5/demo11.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Demo10 = () => {
direction="vertical"
list={horseLamp1}
speed={10}
duration={1000}
duration={3000}
onItemClick={(e, v) => {
console.log('onclick-custom', v)
}}
Expand Down
55 changes: 33 additions & 22 deletions src/packages/noticebar/noticebar.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,26 +190,45 @@ export const NoticeBar: FunctionComponent<
}, 0)
}

// 滚动时间间隔
const scrollAnimationTimeMs = useMemo(() => {
if (Number(speed) <= 0 || Number(height) <= 0) return 0

// 用于计算时间(秒到毫秒,并处理 <1 秒的情况)
const calculateTimeInMs = (baseSeconds: number): number => {
if (baseSeconds < 1) {
// 例如 0.04s -> "0.0" -> 0ms. 0.05s -> "0.1" -> 100ms.
return Number(baseSeconds.toFixed(1)) * 1000
}
return Math.floor(baseSeconds) * 1000
}

// 尝试使用 /4 因子计算
const timeWithFactor4 = calculateTimeInMs(
Number(height) / Number(speed) / 4
)

if (timeWithFactor4 === 0) {
// 如果带 /4 因子的时间为0,则回退到不带 /4 因子的计算
return calculateTimeInMs(Number(height) / Number(speed))
}
return timeWithFactor4
}, [height, speed])

const startRollEasy = () => {
showhorseLamp()
const time =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1)) * 1000
: ~~(height / speed / 4) * 1000
const timerCurr = window.setInterval(showhorseLamp, time + Number(duration))
const timerCurr = window.setInterval(
showhorseLamp,
scrollAnimationTimeMs + Number(duration)
)
SetTimer(timerCurr)
}
const showhorseLamp = () => {
SetAnimate(true)
const time =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1)) * 1000
: ~~(height / speed / 4) * 1000
setTimeout(() => {
scrollList.current.push(scrollList.current[0])
scrollList.current.shift()
SetAnimate(false)
}, time)
}, scrollAnimationTimeMs)
}

// 点击滚动单元
Expand Down Expand Up @@ -237,16 +256,8 @@ export const NoticeBar: FunctionComponent<
height: isVertical ? `${height}px` : '',
}

const duringTime =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1))
: ~~(height / speed / 4)
const noDuring =
height / speed < 1 ? (height / speed).toFixed(1) : ~~(height / speed)
const horseLampStyle = {
transition: animate
? `all ${duringTime === 0 ? noDuring : duringTime}s`
: '',
transition: animate ? `all ${scrollAnimationTimeMs}ms` : '',
marginTop: animate ? `-${height}px` : '',
}

Expand Down Expand Up @@ -306,7 +317,7 @@ export const NoticeBar: FunctionComponent<
next()
autoplay()
},
Number(duration) + 100 * speed
Number(duration) + scrollAnimationTimeMs
)
}

Expand Down Expand Up @@ -375,7 +386,7 @@ export const NoticeBar: FunctionComponent<
moveOffset + Number(activeRef.current === childCount - 1 && val / 2)

target.style.transitionDuration = `${
swiperRef.current.moving ? 0 : duration
swiperRef.current.moving ? 0 : scrollAnimationTimeMs
}ms`
target.style.height = `${Number(height) * (childCount + 1)}px`
target.style.transform = `translate3D(0,${_offset}px,0)`
Expand Down
51 changes: 33 additions & 18 deletions src/packages/noticebar/noticebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,40 @@ export const NoticeBar: FunctionComponent<
SetAnimationClass('play-infinite')
}, 0)
}

// 滚动时间间隔
const time =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1)) * 1000
: ~~(height / speed / 4) * 1000
const scrollAnimationTimeMs = useMemo(() => {
if (Number(speed) <= 0 || Number(height) <= 0) return 0

// 用于计算时间(秒到毫秒,并处理 <1 秒的情况)
const calculateTimeInMs = (baseSeconds: number): number => {
if (baseSeconds < 1) {
// 例如 0.04s -> "0.0" -> 0ms. 0.05s -> "0.1" -> 100ms.
return Number(baseSeconds.toFixed(1)) * 1000
}
return Math.floor(baseSeconds) * 1000
}

// 尝试使用 /4 因子计算
const timeWithFactor4 = calculateTimeInMs(
Number(height) / Number(speed) / 4
)

if (timeWithFactor4 === 0) {
// 如果带 /4 因子的时间为0,则回退到不带 /4 因子的计算
return calculateTimeInMs(Number(height) / Number(speed))
}
return timeWithFactor4
}, [height, speed])

Comment on lines +190 to +215
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Web 版本实现与 Taro 版本保持一致

scrollAnimationTimeMs 的计算逻辑与 Taro 版本完全一致,确保了跨平台行为的统一性。

静态分析显示第211-212行未被测试覆盖。这些行处理了动画时间为0时的回退逻辑,建议补充相关测试用例。

运行以下脚本检查现有测试是否覆盖了边界情况:


🏁 Script executed:

#!/bin/bash
# 检查测试文件中是否有测试 speed 或 height 为极小值的情况
rg -A 5 -B 5 "speed.*[0-9]" src/packages/noticebar/__test__/noticebar.spec.tsx | grep -E "(speed|height).*[=:].*[0-5]\b"

Length of output: 432


请补充测试以覆盖「动画时间为0时的回退逻辑」

静态分析和现有测试结果显示,以下分支尚无覆盖:

  • speedheight0 时,scrollAnimationTimeMs 应直接返回 0
  • height/speed/4 计算后小于 1s(触发 toFixed(1) 四舍五入),且结果为 0 时,应进入不带 /4 因子的回退计算分支

建议在 src/packages/noticebar/__test__/noticebar.spec.tsx 中新增至少三个测试用例,分别验证上述场景:

  • speed={0}height={0}
  • speedheight 组合使得 height/speed/4 < 1 且四舍五入后等于 0
  • 对比带 /4 因子与不带因子的计算结果是否符合预期(回退分支)
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 211-212: src/packages/noticebar/noticebar.tsx#L211-L212
Added lines #L211 - L212 were not covered by tests

🤖 Prompt for AI Agents
In src/packages/noticebar/__test__/noticebar.spec.tsx, add tests to cover the
scrollAnimationTimeMs calculation edge cases from lines 190 to 215 in
src/packages/noticebar/noticebar.tsx. Specifically, create tests where speed or
height is zero to verify the function returns 0 immediately, tests where
height/speed/4 results in a value less than 1 second that rounds to 0 to trigger
the fallback calculation without the /4 factor, and tests comparing the timing
results with and without the /4 factor to confirm the fallback logic works as
intended.

/**
* 滚动方式一,普通垂直滚动
*/
const startRollEasy = () => {
showhorseLamp()
const timerCurr = window.setInterval(showhorseLamp, time + Number(duration))
const timerCurr = window.setInterval(
showhorseLamp,
scrollAnimationTimeMs + Number(duration)
)
SetTimer(timerCurr)
}

Expand All @@ -207,7 +230,7 @@ export const NoticeBar: FunctionComponent<
scrollList.current.push(scrollList.current[0])
scrollList.current.shift()
SetAnimate(false)
}, time)
}, scrollAnimationTimeMs)
}

// 点击滚动单元
Expand Down Expand Up @@ -235,16 +258,8 @@ export const NoticeBar: FunctionComponent<
height: isVertical ? `${height}px` : '',
}

const duringTime =
height / speed / 4 < 1
? Number((height / speed / 4).toFixed(1))
: ~~(height / speed / 4)
const noDuring =
height / speed < 1 ? (height / speed).toFixed(1) : ~~(height / speed)
const horseLampStyle = {
transition: animate
? `all ${duringTime === 0 ? noDuring : duringTime}s`
: '',
transition: animate ? `all ${scrollAnimationTimeMs}ms` : '',
marginTop: animate ? `-${height}px` : '',
}

Expand Down Expand Up @@ -301,7 +316,7 @@ export const NoticeBar: FunctionComponent<
next()
autoplay()
},
Number(duration) + 100 * speed
Number(duration) + scrollAnimationTimeMs
)
}

Expand Down Expand Up @@ -371,7 +386,7 @@ export const NoticeBar: FunctionComponent<
moveOffset + Number(activeRef.current === childCount - 1 && val / 2)

target.style.transitionDuration = `${
swiperRef.current.moving ? 0 : duration
swiperRef.current.moving ? 0 : scrollAnimationTimeMs
}ms`
target.style.height = `${Number(height) * (childCount + 1)}px`
target.style.transform = `translate3D(0,${_offset}px,0)`
Expand Down
Loading