mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
refactor(components): [carousel-item] (#10219)
Co-authored-by: JeremyWuuuuu <15975785+JeremyWuuuuu@users.noreply.github.com>
This commit is contained in:
@@ -13,4 +13,4 @@ export const ElCarouselItem = withNoopInstall(CarouselItem)
|
||||
export * from './src/carousel'
|
||||
export * from './src/carousel-item'
|
||||
|
||||
export type { CarouselInstance } from './src/instance'
|
||||
export type { CarouselInstance, CarouselItemInstance } from './src/instance'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { buildProps } from '@element-plus/utils'
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
import type CarouselItem from './carousel-item.vue'
|
||||
|
||||
export const carouselItemProps = buildProps({
|
||||
name: { type: String, default: '' },
|
||||
@@ -11,5 +10,3 @@ export const carouselItemProps = buildProps({
|
||||
} as const)
|
||||
|
||||
export type CarouselItemProps = ExtractPropTypes<typeof carouselItemProps>
|
||||
|
||||
export type CarouselItemInstance = InstanceType<typeof CarouselItem>
|
||||
|
||||
@@ -18,20 +18,10 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
getCurrentInstance,
|
||||
inject,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
reactive,
|
||||
ref,
|
||||
unref,
|
||||
} from 'vue'
|
||||
import { debugWarn, isUndefined } from '@element-plus/utils'
|
||||
import { computed, unref } from 'vue'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { carouselContextKey } from '@element-plus/tokens'
|
||||
import { carouselItemProps } from './carousel-item'
|
||||
import { useCarouselItem } from './use-carousel-item'
|
||||
|
||||
import type { CSSProperties } from 'vue'
|
||||
|
||||
@@ -43,35 +33,18 @@ const props = defineProps(carouselItemProps)
|
||||
const ns = useNamespace('carousel')
|
||||
const COMPONENT_NAME = 'ElCarouselItem'
|
||||
// inject
|
||||
const carouselContext = inject(carouselContextKey)!
|
||||
// instance
|
||||
const instance = getCurrentInstance()!
|
||||
if (!carouselContext) {
|
||||
debugWarn(
|
||||
COMPONENT_NAME,
|
||||
'usage: <el-carousel></el-carousel-item></el-carousel>'
|
||||
)
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
debugWarn(
|
||||
COMPONENT_NAME,
|
||||
'compositional hook can only be invoked inside setups'
|
||||
)
|
||||
}
|
||||
|
||||
const CARD_SCALE = 0.83
|
||||
|
||||
const hover = ref(false)
|
||||
const translate = ref(0)
|
||||
const scale = ref(1)
|
||||
const active = ref(false)
|
||||
const ready = ref(false)
|
||||
const inStage = ref(false)
|
||||
const animating = ref(false)
|
||||
|
||||
// computed
|
||||
const { isCardType, isVertical } = carouselContext
|
||||
const {
|
||||
active,
|
||||
animating,
|
||||
hover,
|
||||
inStage,
|
||||
isVertical,
|
||||
translate,
|
||||
isCardType,
|
||||
scale,
|
||||
ready,
|
||||
handleItemClick,
|
||||
} = useCarouselItem(props, COMPONENT_NAME)
|
||||
|
||||
const itemStyle = computed<CSSProperties>(() => {
|
||||
const translateType = `translate${unref(isVertical) ? 'Y' : 'X'}`
|
||||
@@ -83,112 +56,4 @@ const itemStyle = computed<CSSProperties>(() => {
|
||||
transform,
|
||||
}
|
||||
})
|
||||
|
||||
// methods
|
||||
|
||||
function processIndex(index: number, activeIndex: number, length: number) {
|
||||
const lastItemIndex = length - 1
|
||||
const prevItemIndex = activeIndex - 1
|
||||
const nextItemIndex = activeIndex + 1
|
||||
const halfItemIndex = length / 2
|
||||
|
||||
if (activeIndex === 0 && index === lastItemIndex) {
|
||||
return -1
|
||||
} else if (activeIndex === lastItemIndex && index === 0) {
|
||||
return length
|
||||
} else if (index < prevItemIndex && activeIndex - index >= halfItemIndex) {
|
||||
return length + 1
|
||||
} else if (index > nextItemIndex && index - activeIndex >= halfItemIndex) {
|
||||
return -2
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
function calcCardTranslate(index: number, activeIndex: number) {
|
||||
const parentWidth = carouselContext.root.value?.offsetWidth || 0
|
||||
if (inStage.value) {
|
||||
return (parentWidth * ((2 - CARD_SCALE) * (index - activeIndex) + 1)) / 4
|
||||
} else if (index < activeIndex) {
|
||||
return (-(1 + CARD_SCALE) * parentWidth) / 4
|
||||
} else {
|
||||
return ((3 + CARD_SCALE) * parentWidth) / 4
|
||||
}
|
||||
}
|
||||
|
||||
function calcTranslate(
|
||||
index: number,
|
||||
activeIndex: number,
|
||||
isVertical: boolean
|
||||
) {
|
||||
const rootEl = carouselContext.root.value
|
||||
if (!rootEl) return 0
|
||||
|
||||
const distance = (isVertical ? rootEl.offsetHeight : rootEl.offsetWidth) || 0
|
||||
return distance * (index - activeIndex)
|
||||
}
|
||||
|
||||
const translateItem = (
|
||||
index: number,
|
||||
activeIndex: number,
|
||||
oldIndex?: number
|
||||
) => {
|
||||
const _isCardType = unref(isCardType)
|
||||
const carouselItemLength = carouselContext.items.value.length ?? Number.NaN
|
||||
|
||||
const isActive = index === activeIndex
|
||||
if (!_isCardType && !isUndefined(oldIndex)) {
|
||||
animating.value = isActive || index === oldIndex
|
||||
}
|
||||
|
||||
if (!isActive && carouselItemLength > 2 && carouselContext.loop) {
|
||||
index = processIndex(index, activeIndex, carouselItemLength)
|
||||
}
|
||||
|
||||
const _isVertical = unref(isVertical)
|
||||
active.value = isActive
|
||||
|
||||
if (_isCardType) {
|
||||
if (_isVertical) {
|
||||
debugWarn('Carousel', 'vertical direction is not supported for card mode')
|
||||
}
|
||||
inStage.value = Math.round(Math.abs(index - activeIndex)) <= 1
|
||||
translate.value = calcCardTranslate(index, activeIndex)
|
||||
scale.value = unref(active) ? 1 : CARD_SCALE
|
||||
} else {
|
||||
translate.value = calcTranslate(index, activeIndex, _isVertical)
|
||||
}
|
||||
|
||||
ready.value = true
|
||||
}
|
||||
|
||||
function handleItemClick() {
|
||||
if (carouselContext && unref(isCardType)) {
|
||||
const index = carouselContext.items.value.findIndex(
|
||||
({ uid }) => uid === instance.uid
|
||||
)
|
||||
carouselContext.setActiveItem(index)
|
||||
}
|
||||
}
|
||||
|
||||
// lifecycle
|
||||
onMounted(() => {
|
||||
carouselContext.addItem({
|
||||
props,
|
||||
states: reactive({
|
||||
hover,
|
||||
translate,
|
||||
scale,
|
||||
active,
|
||||
ready,
|
||||
inStage,
|
||||
animating,
|
||||
}),
|
||||
uid: instance.uid,
|
||||
translateItem,
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
carouselContext.removeItem(instance.uid)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type Carousel from './carousel.vue'
|
||||
import type CarouselItem from './carousel-item.vue'
|
||||
|
||||
export type CarouselInstance = InstanceType<typeof Carousel>
|
||||
export type CarouselItemInstance = InstanceType<typeof CarouselItem>
|
||||
|
||||
173
packages/components/carousel/src/use-carousel-item.ts
Normal file
173
packages/components/carousel/src/use-carousel-item.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import {
|
||||
getCurrentInstance,
|
||||
inject,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
reactive,
|
||||
ref,
|
||||
unref,
|
||||
} from 'vue'
|
||||
import { debugWarn, isUndefined } from '@element-plus/utils'
|
||||
import { carouselContextKey } from '@element-plus/tokens'
|
||||
|
||||
import type { CarouselItemProps } from './carousel-item'
|
||||
|
||||
export const useCarouselItem = (
|
||||
props: CarouselItemProps,
|
||||
componentName: string
|
||||
) => {
|
||||
const carouselContext = inject(carouselContextKey)!
|
||||
// instance
|
||||
const instance = getCurrentInstance()!
|
||||
if (!carouselContext) {
|
||||
debugWarn(
|
||||
componentName,
|
||||
'usage: <el-carousel></el-carousel-item></el-carousel>'
|
||||
)
|
||||
}
|
||||
|
||||
if (!instance) {
|
||||
debugWarn(
|
||||
componentName,
|
||||
'compositional hook can only be invoked inside setups'
|
||||
)
|
||||
}
|
||||
|
||||
const CARD_SCALE = 0.83
|
||||
|
||||
const hover = ref(false)
|
||||
const translate = ref(0)
|
||||
const scale = ref(1)
|
||||
const active = ref(false)
|
||||
const ready = ref(false)
|
||||
const inStage = ref(false)
|
||||
const animating = ref(false)
|
||||
|
||||
// computed
|
||||
const { isCardType, isVertical } = carouselContext
|
||||
|
||||
// methods
|
||||
|
||||
function processIndex(index: number, activeIndex: number, length: number) {
|
||||
const lastItemIndex = length - 1
|
||||
const prevItemIndex = activeIndex - 1
|
||||
const nextItemIndex = activeIndex + 1
|
||||
const halfItemIndex = length / 2
|
||||
|
||||
if (activeIndex === 0 && index === lastItemIndex) {
|
||||
return -1
|
||||
} else if (activeIndex === lastItemIndex && index === 0) {
|
||||
return length
|
||||
} else if (index < prevItemIndex && activeIndex - index >= halfItemIndex) {
|
||||
return length + 1
|
||||
} else if (index > nextItemIndex && index - activeIndex >= halfItemIndex) {
|
||||
return -2
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
function calcCardTranslate(index: number, activeIndex: number) {
|
||||
const parentWidth = carouselContext.root.value?.offsetWidth || 0
|
||||
if (inStage.value) {
|
||||
return (parentWidth * ((2 - CARD_SCALE) * (index - activeIndex) + 1)) / 4
|
||||
} else if (index < activeIndex) {
|
||||
return (-(1 + CARD_SCALE) * parentWidth) / 4
|
||||
} else {
|
||||
return ((3 + CARD_SCALE) * parentWidth) / 4
|
||||
}
|
||||
}
|
||||
|
||||
function calcTranslate(
|
||||
index: number,
|
||||
activeIndex: number,
|
||||
isVertical: boolean
|
||||
) {
|
||||
const rootEl = carouselContext.root.value
|
||||
if (!rootEl) return 0
|
||||
|
||||
const distance =
|
||||
(isVertical ? rootEl.offsetHeight : rootEl.offsetWidth) || 0
|
||||
return distance * (index - activeIndex)
|
||||
}
|
||||
|
||||
const translateItem = (
|
||||
index: number,
|
||||
activeIndex: number,
|
||||
oldIndex?: number
|
||||
) => {
|
||||
const _isCardType = unref(isCardType)
|
||||
const carouselItemLength = carouselContext.items.value.length ?? Number.NaN
|
||||
|
||||
const isActive = index === activeIndex
|
||||
if (!_isCardType && !isUndefined(oldIndex)) {
|
||||
animating.value = isActive || index === oldIndex
|
||||
}
|
||||
|
||||
if (!isActive && carouselItemLength > 2 && carouselContext.loop) {
|
||||
index = processIndex(index, activeIndex, carouselItemLength)
|
||||
}
|
||||
|
||||
const _isVertical = unref(isVertical)
|
||||
active.value = isActive
|
||||
|
||||
if (_isCardType) {
|
||||
if (_isVertical) {
|
||||
debugWarn(
|
||||
'Carousel',
|
||||
'vertical direction is not supported for card mode'
|
||||
)
|
||||
}
|
||||
inStage.value = Math.round(Math.abs(index - activeIndex)) <= 1
|
||||
translate.value = calcCardTranslate(index, activeIndex)
|
||||
scale.value = unref(active) ? 1 : CARD_SCALE
|
||||
} else {
|
||||
translate.value = calcTranslate(index, activeIndex, _isVertical)
|
||||
}
|
||||
|
||||
ready.value = true
|
||||
}
|
||||
|
||||
function handleItemClick() {
|
||||
if (carouselContext && unref(isCardType)) {
|
||||
const index = carouselContext.items.value.findIndex(
|
||||
({ uid }) => uid === instance.uid
|
||||
)
|
||||
carouselContext.setActiveItem(index)
|
||||
}
|
||||
}
|
||||
|
||||
// lifecycle
|
||||
onMounted(() => {
|
||||
carouselContext.addItem({
|
||||
props,
|
||||
states: reactive({
|
||||
hover,
|
||||
translate,
|
||||
scale,
|
||||
active,
|
||||
ready,
|
||||
inStage,
|
||||
animating,
|
||||
}),
|
||||
uid: instance.uid,
|
||||
translateItem,
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
carouselContext.removeItem(instance.uid)
|
||||
})
|
||||
|
||||
return {
|
||||
active,
|
||||
animating,
|
||||
hover,
|
||||
inStage,
|
||||
isVertical,
|
||||
translate,
|
||||
isCardType,
|
||||
scale,
|
||||
ready,
|
||||
handleItemClick,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user