一、需求
实现的功能为:最大黑色框内为可视区,其余黑框为需展示的列表,列表超出部分隐藏。
红色圆圈为左右切换按钮,当页面初始加载时,左侧红色按钮隐藏,右侧红色按钮根据列表宽度及可视区宽度判断是否应该显示:当列表宽度超出可视区范围时,显示右侧红色按钮,且点击之后可以切换下一个列表内容,同时左侧红色按钮也相应显示;当列表宽度小于可视区范围时,两侧按钮均不展示(如果有点击某个按钮可以添加列表项的需求时,当整体列表项宽度大于可视窗宽度,按钮也相应的显示)。
二、思路分析
- 在页面初始加载时需获取可视窗宽度(clientWidth)(对应生命周期componentDidMount)
- 获取展示列表宽度(假设列表名称定义为list,则对应列表宽度为list.width)
- 页面布局时设置每个列表项宽度为固定值(假定为300px)
- 每个列表项之间的margin(假定为15px)
- 要使得整个列表可以移动,则需要操作DOM,并设置css样式,有两个样式可以实现列表的移动:1.tranform,设置translateX的值不断变化,则可以实现列表项的移动2.绝对定位,设置left或right的值不断变化。 在这里我使用的方法为使用transform实现列表的移动。
- 因为要判断切换按钮是否显示,所以需要明显显示的边界条件
- 左侧按钮:页面初始加载时一定不显示(初始时列表未移动)
- 右侧按钮:页面初始加载时,根据列表宽度与可视区范围时判断右侧按钮是否显示(当有添加按钮时,根据列表长度的变化判断按钮是否显示)
三、代码实现
整体我只写页面HTML,React逻辑部分,css部分自己可以根据自己需求进行布局,注意整个list可视区部分需设置overflow:hidden
1.定义列表项
const list = [
{ key: '1', month:"列表项1"},
{ key: '2', month:"列表项2"},
{ key: '3', month:"列表项3"},
{ key: '4', month:"列表项4"},
{ key: '5', month:"列表项5"},
]
2.html部分
//这里img使用绝对定位定位在整个list上方
{ leftIconVisible && <img src={""} onClick={()=>onIconClick("left")} /> }
<div
className={style.list}
id="list"
style={{ transform: `translateX(${translateX}px)` }}
>
{list.map((item) => {
const { key, name } = item
return (
<div
key={key}
className={style.listItem}
>
{name}
</div>
)
})}
</div>
{ rightIconVisible && <img src={""} onClick={()=>onIconClick("right")} /> }
3.react逻辑部分
const [listWidth, setListWidth] = useState(0)
const [translateX, setTranslateX] = useState(0)
//使用useMemo计算左右按钮是否隐藏
const [leftIconVisible, rightIconVisible] = useMemo(() => {
const listItemWidth = list.length * 315
const isLeftIconVisible = translateX >= 0
const isRightIconVisible =
listItemWidth < listWidth ||
listItemWidth + translateX <= listWidth
return [isLeftIconVisible, isRightIconVisible]
}, [list, translateX, listWidth])
useEffect(() => {
const list = document.getElementById('list') as HTMLElement
setlistWidth(list.clientWidth)
}, [])
const onIconClick = (direaction: string) => {
const translateStep = 315
let newTranslateX: number = translateX
const translateDistance: number =
translateStep * (direaction === 'left' ? 1 : -1)
// 点击箭头
if (translateDistance > 0) {
if (leftIconVisible) return
else {
newTranslateX += translateDistance
}
} else if (translateDistance < 0) {
if (rightIconVisible) return
else {
newTranslateX += translateDistance
}
}
setTranslateX(newTranslateX)
}