slide smoothly 滑动组件

431 阅读1分钟

业务上需要实现一个拖动调整大小的功能,所以首先想到的就是监听轨道的mouse事件,来调整组件的高度,类似slider的拖拽滑动。

效果就是,能拖动,但是不顺滑,拖一段就卡,跟常用的slider的顺滑体验相差太大,所以做了以下改进

  1. 如果把mouse event监听放在thumb上,当鼠标划得快,划出thumb范围的时候,将会导致不能捕获到move事件,所以会出现卡顿。因此需要监听body 的mouse 事件
  2. 将整个可滑动区域划分为100份,按照百分比来设置thumb的位置,百分比只取整数,减少update UI的次数
  3. 在支持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>
    }