封装 dots 组件,计算 dots 滚动的算法如下
import React, { useEffect, useRef } from 'react'
import { ReactNode } from 'react'
import type { FC } from 'react'
export interface IProps {
children?: ReactNode
selectIndex?: number
}
const Indicator: FC<IProps> = function (props) {
const { children, selectIndex = 0 } = props
// 1.定义获取dots的ref
const dotsRef = useRef<HTMLDivElement>(null)
useEffect(() => {
// 2.获取selectIndex对应的子元素
const selectEL = dotsRef.current?.children[selectIndex!]
// 3.获取子元素左距离
const itemLeft = (selectEL as HTMLElement)?.offsetLeft
// 4.获取子元素本身的宽度
const itemWidth = (selectEL as HTMLElement)?.clientWidth
// 5.获取dots本身的宽度
const dotsWidth = dotsRef.current?.clientWidth!
// 5.计算滚动的的距离
// let distance = itemLeft + itemWidth * 0.5 - dotsWidth
let distance = itemLeft + itemWidth - dotsWidth
// 6.计算可滚动的总距离
const dotsScroll = dotsRef.current?.scrollWidth!
const totalDistance = dotsScroll - dotsWidth
// 7.边界判断
// 左边边界判断
if (distance < 0) distance = 0
// 右边边界判断
if (distance > totalDistance) distance = totalDistance
// console.log(distance)
dotsRef.current!.style.transform = `translate(${-distance}px)`
}, [selectIndex])
return (
<div className="flex transition duration-200 ease-in-out " ref={dotsRef}>
{children}
</div>
)
}
export default Indicator
// 设置一个方便调试的name 可以不写 默认为组件名称
Indicator.displayName = 'Indicator'
在 room-item 组件中结合 antd 的轮播图组件时使用
...
import { Rating } from '@mui/material'
import { Carousel } from 'antd'
import { CarouselRef } from 'antd/es/carousel'
import Indicator from '../indicator'
import style from './index.module.scss'
export interface IProps {
itemData: EntireHomeItem | HomeItem
width?: '20%' | '25%' | '33.33%' // 展示的宽度
itemClick?: (item: EntireHomeItem) => void
}
const RoomItem: FC<IProps> = function (props) {
const { itemData, width = '25%', itemClick } = props
// 定义dots的索引
const [selectIndex, setSelectIndex] = useState(0)
// 点击箭头切换图片
// 1.获取轮播图的Ref
const carouselRef = useRef<CarouselRef>(null)
function controlClickHandle(isNext: boolean) {
isNext ? carouselRef.current?.next() : carouselRef.current?.prev()
// 1.如果点击下一个 index+1 上一个-1
let newIndex = isNext ? selectIndex + 1 : selectIndex - 1
// 2.如果小于0 index设为最后一个
const len = itemData.picture_urls.length
if (newIndex < 0) newIndex = len - 1
// 3.如果大于最后一个 index设为第一个
if (newIndex > len - 1) newIndex = 0
setSelectIndex(newIndex)
}
function itemClickHandle() {
itemClick && itemClick(itemData as EntireHomeItem)
}
return (
<div className={style.swiper}>
{/* 滚动箭头 */}
<div className={style.control}>
<div className={style.left} onClick={(e) => controlClickHandle(false)}>
<IconArrowLeft width={20} height={20} />
</div>
<div className={style.right} onClick={(e) => controlClickHandle(true)}>
<IconArrowRight width={20} height={20} />
</div>
</div>
{/* 轮播图 */}
<Carousel dots={false} ref={carouselRef}>
{itemData.picture_urls?.map((item) => {
return (
<div key={item}>
<img
className=" bottom-0 h-[200px] w-[100%] object-cover rounded-md "
src={item}
/>
</div>
)
})}
</Carousel>
{/* 使用dots组件 */}
<div className=" absolute z-[999] bottom-2 left-0 right-0 mx-auto w-[40%] overflow-hidden ">
<Indicator selectIndex={selectIndex}>
{itemData.picture_urls?.map((item, index) => {
return (
// w为14.28展示7个点 20为5个 看你怎么用了
<div
className="flex flex-shrink-0 justify-center items-center w-[14.28%]"
key={item}
>
<span
className="w-[6px] h-[6px] bg-white rounded-full"
style={
selectIndex == index
? {
width: '8px',
height: '8px',
background: '#00848A'
}
: {}
}
></span>
</div>
)
})}
</Indicator>
</div>
</div>
...
)
}
export default RoomItem
// 设置一个方便调试的name 可以不写 默认为组件名称
RoomItem.displayName = 'RoomItem'
room-item 箭头的样式
// 轮播图样式
.swiper {
@apply cursor-pointer;
&:hover {
.control {
display: flex;
}
}
.control {
@apply absolute hidden text-white z-10 left-0 right-0 top-0 bottom-0 justify-between;
.left {
@apply flex justify-center items-center w-[50px] bg-gradient-to-l from-transparent to-[rgba(0,0,0,0.25)];
}
.right {
@apply flex justify-center items-center w-[50px] bg-gradient-to-r from-transparent to-[rgba(0,0,0,0.25)];
}
}
}