美团中的左右二级联动简单效果实现:
import React, { Component, createRef } from 'react'
import "./index.less";
export default class index extends Component {
// 声明控制下标的状态 和开关的状态
state = { Index: 0, flag: true }
// 渲染数据
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
// 保存右部子盒子每次递增的高度 用于计算scroll
array: number[] = []
// 保存子盒子的高度
hi: number = 0
// 保存右部子盒子每次递增的高度
height: number = 0
// 左部大盒子
left = createRef<HTMLDivElement>();
// 右部大盒子
right = createRef<HTMLDivElement>();
// 利用方法对左部大盒子元素进行返回值 方便我们调用该元素
getleft = () => (this.left.current as HTMLDivElement)
// 利用方法对右部大盒子元素进行返回 方便我们调用该元素
getright = () => (this.right.current as HTMLDivElement)
// React生命周期 在组件挂载完毕后执行
componentDidMount() {
// 保存右部子级盒子的高度
this.hi = (this.getright().children[0] as HTMLDivElement).offsetHeight
// 默认往我们的数组中添加一个0;解决我们点击第一个元素时会滑动的问题
this.array.push(this.height);
// 对右部的所有子级盒子进行遍历
this.getright().childNodes.forEach(item => {
// 对每次的子级盒子的高度进行递增, 1+2 2+3 3+4 4+5 数字为下标元素
this.height += (item as HTMLDivElement).offsetHeight
// 将每次递增后的数值添加进我们提前声明好的数值数组中
this.array.push(this.height);
})
}
start(index: number) {
// 通过Reactapi this.setState 对小标进行更新,并关闭开关 用于阻止scroll事件后续的代码执行
this.setState({ Index: index, flag: false });
// 用当前点击元素的下标 称与 右部自己盒子的高度 来计算出右部大盒子scrollTop滚动多少距离
this.getright().scrollTop = this.hi * index
// 三元运算 让左部发生运动 让选中元素保持在中间位置
this.getleft().scrollTop = index >= 4 ? (index - 3) * 100 : 0
}
scroll = () => {
// 判断当前状态里的开关是否打开 没有打开 直接return 阻止后续的事件逻辑执行
if (this.state.flag === false) return
// 对我们保存递增高度的数组进行遍历判断
this.array.forEach((item, index) => {
// 判断右部盒子scrollTop滚动距离是否大于 我们所保存的每一个递增的高度值 大于就将其对应下标进行状态更新
if (this.getright().scrollTop >= item - 80) {
this.setState({ Index: index });
// 三元运算 让左部盒子发生运动 让选中元素保持在中间位置
this.getleft().scrollTop = index >= 4 ? (index - 3) * 100 : 0;
}
});
}
// 触摸事件 当我们触摸之后打开开关灯 让scroll事件的后续代码可以执行
Fnend = () => { this.setState({ flag: true }) }
render() {
// 结构赋值 可以快速的保存设置状态里的值
var { Index } = this.state
return (
<div className='LeftRight-LingAge'>
<div className="Lingage-left" ref={this.left}>
{this.arr.map((item, index) => <div style={{ background: Index === index ? '#09c' : '' }} onTouchStart={this.start.bind(this, index)} key={item}>
{item}
</div>)}
</div>
<div className="Lingage-right" ref={this.right} onScroll={this.scroll} onTouchStart={this.Fnend.bind(this)}>
{this.arr.map((item, index) => <div key={item}>
{item}
</div>)}
</div>
</div>
)
}
}
- index.less
.LeftRight-LingAge {
width: 100vw;
height: 100vh;
background: skyblue;
display: flex;
overflow: hidden;
.Lingage-left::-webkit-scrollbar {
display: none;
}
.Lingage-right::-webkit-scrollbar {
display: none;
}
.Lingage-left {
overflow-y: scroll;
scroll-behavior: smooth;
width: 120px;
height: 100%;
background-color: pink;
div {
width: 100%;
height: 100px;
font-size: 18px;
text-align: center;
line-height: 100px;
font-size: 30px;
}
}
.Lingage-right {
overflow-y: scroll;
scroll-behavior: smooth;
width: 280px;
height: 100vh;
>div {
text-align: center;
font-size: 40px;
width: 100%;
height: 110%;
background-color: goldenrod;
}
>div:nth-child(2n) {
background-color: #999;
}
}
}
如有高见可在评论区发言