From 53bc6517f2bd3668614d178f8aac1d391cc0267b Mon Sep 17 00:00:00 2001 From: Jed <510374040@qq.com> Date: Thu, 29 Feb 2024 14:54:06 +0800 Subject: [PATCH] feat(components): [Carousel] Add motion blur functionality (#15603) * feat(components): [Carousel] Add motion blur functionality docs(components): [Carousel] Add example properties docs(components): [Carousel] Add docs properties * fix(components): [Carousel] change the id name in filter in svg fix(components): [Carousel] button to remove motion blur effect fix(components): [Carousel] svg hides when motion blur is false * update(components): [Carousel] docs/en-US/component/carousel.md Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com> * update(components): [Carousel] docs/examples/carousel/motion-blur.vue Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com> * update(components): [Carousel] docs/examples/carousel/motion-blur.vue Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com> * update(components): [Carousel] packages/components/carousel/src/carousel.ts Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com> * docs(components): [Carousel] add feature version labels --------- Co-authored-by: kooriookami <38392315+kooriookami@users.noreply.github.com> --- docs/en-US/component/carousel.md | 37 +++--- docs/examples/carousel/motion-blur.vue | 43 +++++++ .../carousel/__tests__/carousel.test.tsx | 22 ++++ packages/components/carousel/src/carousel.ts | 4 + packages/components/carousel/src/carousel.vue | 105 ++++++++++++------ .../components/carousel/src/use-carousel.ts | 26 +++++ packages/theme-chalk/src/carousel.scss | 8 ++ 7 files changed, 196 insertions(+), 49 deletions(-) create mode 100644 docs/examples/carousel/motion-blur.vue diff --git a/docs/en-US/component/carousel.md b/docs/en-US/component/carousel.md index 1789ec5b33..5af7d7d47a 100644 --- a/docs/en-US/component/carousel.md +++ b/docs/en-US/component/carousel.md @@ -15,6 +15,16 @@ carousel/basic ::: +## Motion blur ^(2.6.0) + +Add motion blur to infuse dynamism and smoothness into the carousel. + +:::demo Enabling motion blur enhances the dynamism and smoothness of the carousel. By default, the `motion-blur` parameter is set to `false`, activating this feature and providing a visually engaging experience. + +carousel/motion-blur + +::: + ## Indicators Indicators can be displayed outside the carousel @@ -67,19 +77,20 @@ carousel/vertical ## Carousel Attributes -| Name | Description | Type | Accepted Values | Default | -| ------------------ | ----------------------------------------------------- | ------- | ------------------- | ---------- | -| height | height of the carousel | string | — | — | -| initial-index | index of the initially active slide (starting from 0) | number | — | 0 | -| trigger | how indicators are triggered | string | hover/click | hover | -| autoplay | whether automatically loop the slides | boolean | — | true | -| interval | interval of the auto loop, in milliseconds | number | — | 3000 | -| indicator-position | position of the indicators | string | outside/none | — | -| arrow | when arrows are shown | string | always/hover/never | hover | -| type | type of the Carousel | string | card | — | -| loop | display the items in loop | boolean | - | true | -| direction | display direction | string | horizontal/vertical | horizontal | -| pause-on-hover | pause autoplay when hover | boolean | - | true | +| Name | Description | Type | Accepted Values | Default | +| ---------------------- | ----------------------------------------------------- | ------- | ------------------- | ---------- | +| height | height of the carousel | string | — | — | +| initial-index | index of the initially active slide (starting from 0) | number | — | 0 | +| trigger | how indicators are triggered | string | hover/click | hover | +| autoplay | whether automatically loop the slides | boolean | — | true | +| interval | interval of the auto loop, in milliseconds | number | — | 3000 | +| indicator-position | position of the indicators | string | outside/none | — | +| arrow | when arrows are shown | string | always/hover/never | hover | +| type | type of the Carousel | string | card | — | +| loop | display the items in loop | boolean | - | true | +| direction | display direction | string | horizontal/vertical | horizontal | +| pause-on-hover | pause autoplay when hover | boolean | - | true | +| motion-blur ^(2.6.0) | infuse dynamism and smoothness into the carousel | boolean | - | false | ## Carousel Events diff --git a/docs/examples/carousel/motion-blur.vue b/docs/examples/carousel/motion-blur.vue new file mode 100644 index 0000000000..9f5acbc9bc --- /dev/null +++ b/docs/examples/carousel/motion-blur.vue @@ -0,0 +1,43 @@ + + + diff --git a/packages/components/carousel/__tests__/carousel.test.tsx b/packages/components/carousel/__tests__/carousel.test.tsx index e8832b3ed9..f053636bc1 100644 --- a/packages/components/carousel/__tests__/carousel.test.tsx +++ b/packages/components/carousel/__tests__/carousel.test.tsx @@ -195,6 +195,28 @@ describe('Carousel', () => { await wait(60) expect(items[1].classList.contains('is-active')).toBeTruthy() }) + + it('motion blur', async () => { + const state = reactive({ + val: -1, + oldVal: -1, + }) + + wrapper = createComponent({ + onChange(val: number, prevVal: number) { + state.val = val + state.oldVal = prevVal + }, + interval: 100, + 'motion-blur': true, + }) + + await nextTick() + await wait(100) + const items = wrapper.vm.$el.querySelectorAll('.el-transitioning') + expect(items.length).toBe(1) + }) + it('should guarantee order of indicators', async () => { const data = reactive([1, 2, 3, 4]) wrapper = mount({ diff --git a/packages/components/carousel/src/carousel.ts b/packages/components/carousel/src/carousel.ts index 0f41b34e3e..3366df7d40 100644 --- a/packages/components/carousel/src/carousel.ts +++ b/packages/components/carousel/src/carousel.ts @@ -51,6 +51,10 @@ export const carouselProps = buildProps({ type: Boolean, default: true, }, + motionBlur: { + type: Boolean, + default: false, + }, } as const) export const carouselEmits = { diff --git a/packages/components/carousel/src/carousel.vue b/packages/components/carousel/src/carousel.vue index 7eb069568c..2ce251e927 100644 --- a/packages/components/carousel/src/carousel.vue +++ b/packages/components/carousel/src/carousel.vue @@ -5,42 +5,46 @@ @mouseenter.stop="handleMouseEnter" @mouseleave.stop="handleMouseLeave" > -
- - - - - - + + + + + + +
@@ -65,6 +69,21 @@ + + + + + + + + + +
@@ -95,9 +114,11 @@ const { containerStyle, handleButtonEnter, handleButtonLeave, + isTransitioning, handleIndicatorClick, handleMouseEnter, handleMouseLeave, + handleTransitionEnd, setActiveItem, prev, next, @@ -118,6 +139,18 @@ const carouselClasses = computed(() => { return classes }) +const carouselContainer = computed(() => { + const classes = [ns.e('container')] + if (props.motionBlur && unref(isTransitioning)) { + classes.push( + unref(isVertical) + ? `${ns.namespace.value}-transitioning-vertical` + : `${ns.namespace.value}-transitioning` + ) + } + return classes +}) + const indicatorsClasses = computed(() => { const classes = [ns.e('indicators'), ns.em('indicators', props.direction)] if (unref(hasLabel)) { diff --git a/packages/components/carousel/src/use-carousel.ts b/packages/components/carousel/src/use-carousel.ts index 93b76a186d..d3f7085230 100644 --- a/packages/components/carousel/src/use-carousel.ts +++ b/packages/components/carousel/src/use-carousel.ts @@ -46,6 +46,8 @@ export const useCarousel = ( const root = ref() const containerHeight = ref(0) const isItemsTwoLength = ref(true) + const isFirstCall = ref(true) + const isTransitioning = ref(false) // computed const arrowDisplay = computed( @@ -102,6 +104,11 @@ export const useCarousel = ( } const playSlides = () => { + if (!isFirstCall.value) { + isTransitioning.value = true + } + isFirstCall.value = false + if (activeIndex.value < items.value.length - 1) { activeIndex.value = activeIndex.value + 1 } else if (props.loop) { @@ -110,6 +117,11 @@ export const useCarousel = ( } function setActiveItem(index: number | string) { + if (!isFirstCall.value) { + isTransitioning.value = true + } + isFirstCall.value = false + if (isString(index)) { const filteredItems = items.value.filter( (item) => item.props.name === index @@ -176,6 +188,10 @@ export const useCarousel = ( startTimer() } + function handleTransitionEnd() { + isTransitioning.value = false + } + function handleButtonEnter(arrow: 'left' | 'right') { if (unref(isVertical)) return items.value.forEach((item, index) => { @@ -193,12 +209,20 @@ export const useCarousel = ( } function handleIndicatorClick(index: number) { + if (index !== activeIndex.value) { + if (!isFirstCall.value) { + isTransitioning.value = true + } + } activeIndex.value = index } function handleIndicatorHover(index: number) { if (props.trigger === 'hover' && index !== activeIndex.value) { activeIndex.value = index + if (!isFirstCall.value) { + isTransitioning.value = true + } } } @@ -319,11 +343,13 @@ export const useCarousel = ( hasLabel, hover, isCardType, + isTransitioning, items, isVertical, containerStyle, isItemsTwoLength, handleButtonEnter, + handleTransitionEnd, handleButtonLeave, handleIndicatorClick, handleMouseEnter, diff --git a/packages/theme-chalk/src/carousel.scss b/packages/theme-chalk/src/carousel.scss index 7fa5554e66..496c9e1420 100644 --- a/packages/theme-chalk/src/carousel.scss +++ b/packages/theme-chalk/src/carousel.scss @@ -178,3 +178,11 @@ transform: translateY(-50%) translateX(10px); opacity: 0; } + +.#{$namespace}-transitioning { + filter: url('#elCarouselHorizontal'); +} + +.#{$namespace}-transitioning-vertical { + filter: url('#elCarouselVertical'); +}