业务上需要实现一个拖动调整大小的功能,所以首先想到的就是监听轨道的mouse事件,来调整组件的高度,类似slider的拖拽滑动。
效果就是,能拖动,但是不顺滑,拖一段就卡,跟常用的slider的顺滑体验相差太大,所以做了以下改进
- 如果把mouse event监听放在thumb上,当鼠标划得快,划出thumb范围的时候,将会导致不能捕获到move事件,所以会出现卡顿。因此需要监听body 的mouse 事件
- 将整个可滑动区域划分为100份,按照百分比来设置thumb的位置,百分比只取整数,减少update UI的次数
- 在支持touch 事件的设备上监听touch事件,在支持mouse事件的设备上监听mouse事件
滑动部分的代码如下
addMouseListener = () => {
document.body.addEventListener('mousemove', this.onMouseMove);
document.body.addEventListener('mouseup', this.onMouseUp);
};
removeMouseListener = () => {
document.body.removeEventListener('mousemove', this.onMouseMove);
document.body.removeEventListener('mouseup', this.onMouseUp);
};
addTouchListener = () => {
document.body.addEventListener('touchmove', this.onTouchMove);
document.body.addEventListener('touchend', this.onTouchEnd);
};
removeTouchListener = () => {
document.body.removeEventListener('touchmove', this.onTouchMove);
document.body.removeEventListener('touchend', this.onTouchEnd);
};
onStart = () => {
this.dragging = true;
};
onEnd = () => {
this.dragging = false;
};
onMove = (clientY: number) => {
if (!this.content) return;
const { percent } = this.state;
const offset = clientY - this.content.clientTop;
const newPercent = Math.floor((offset / this.content.clientHeight) * 100);
if (newPercent === percent || newPercent < 20 || newPercent > 80) return;
this.setState({ percent: newPercent });
};
onMouseDown = () => {
this.addMouseListener();
this.onStart();
};
onMouseUp = () => {
this.removeMouseListener();
this.onEnd();
};
onMouseMove = (e: MouseEvent) => {
if (!this.dragging) return;
// 拖拽块的offset补偿,确保鼠标一直在块中央
this.onMove(e.clientY + 45);
};
onTouchStart = () => {
this.addTouchListener();
this.onStart();
};
onTouchMove = (e: TouchEvent) => {
if (e.targetTouches.length > 1 || !this.dragging) return;
const clientY = e.targetTouches[0].clientY;
this.onMove(clientY + 45);
};
onTouchEnd = () => {
this.removeTouchListener();
this.onEnd();
};
render() {
const {percent} = this.state
return <div style={{ height: `${percent}%` }}>
<span
onMouseDown={this.onMouseDown}
onTouchStart={this.onTouchStart}
/>
</div>
}