diff --git a/src/packages/noticebar/__test__/noticebar.spec.tsx b/src/packages/noticebar/__test__/noticebar.spec.tsx index 367e19a15e..521ce8e4ae 100644 --- a/src/packages/noticebar/__test__/noticebar.spec.tsx +++ b/src/packages/noticebar/__test__/noticebar.spec.tsx @@ -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') }) }) @@ -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}`) } }, @@ -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( + + ) + + const listElement = container.querySelector( + '.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( + +
公告1
+
公告2
+
公告3
+
+ ) + + const listElement = container.querySelector( + '.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)` + ) + }) + }) +}) diff --git a/src/packages/noticebar/demos/h5/demo10.tsx b/src/packages/noticebar/demos/h5/demo10.tsx index 646c5299c3..fbf3f6a8db 100644 --- a/src/packages/noticebar/demos/h5/demo10.tsx +++ b/src/packages/noticebar/demos/h5/demo10.tsx @@ -17,7 +17,7 @@ const Demo9 = () => { direction="vertical" height={50} speed={10} - duration={1000} + duration={3000} closeable onClose={() => { console.log('close') diff --git a/src/packages/noticebar/demos/h5/demo11.tsx b/src/packages/noticebar/demos/h5/demo11.tsx index c6ab025834..3b0e8b7a93 100644 --- a/src/packages/noticebar/demos/h5/demo11.tsx +++ b/src/packages/noticebar/demos/h5/demo11.tsx @@ -16,7 +16,7 @@ const Demo10 = () => { direction="vertical" list={horseLamp1} speed={10} - duration={1000} + duration={3000} onItemClick={(e, v) => { console.log('onclick-custom', v) }} diff --git a/src/packages/noticebar/noticebar.taro.tsx b/src/packages/noticebar/noticebar.taro.tsx index 1a36175485..5c2fd56869 100644 --- a/src/packages/noticebar/noticebar.taro.tsx +++ b/src/packages/noticebar/noticebar.taro.tsx @@ -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) } // 点击滚动单元 @@ -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` : '', } @@ -306,7 +317,7 @@ export const NoticeBar: FunctionComponent< next() autoplay() }, - Number(duration) + 100 * speed + Number(duration) + scrollAnimationTimeMs ) } @@ -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)` diff --git a/src/packages/noticebar/noticebar.tsx b/src/packages/noticebar/noticebar.tsx index a371e56538..0b95d0b415 100644 --- a/src/packages/noticebar/noticebar.tsx +++ b/src/packages/noticebar/noticebar.tsx @@ -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]) + /** * 滚动方式一,普通垂直滚动 */ const startRollEasy = () => { - showhorseLamp() - const timerCurr = window.setInterval(showhorseLamp, time + Number(duration)) + const timerCurr = window.setInterval( + showhorseLamp, + scrollAnimationTimeMs + Number(duration) + ) SetTimer(timerCurr) } @@ -207,7 +230,7 @@ export const NoticeBar: FunctionComponent< scrollList.current.push(scrollList.current[0]) scrollList.current.shift() SetAnimate(false) - }, time) + }, scrollAnimationTimeMs) } // 点击滚动单元 @@ -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` : '', } @@ -301,7 +316,7 @@ export const NoticeBar: FunctionComponent< next() autoplay() }, - Number(duration) + 100 * speed + Number(duration) + scrollAnimationTimeMs ) } @@ -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)`