实现鼠标长按某一区域可上下改变盒子高度大小

28 篇文章 1 订阅

组件

import {ResizeSensor} from 'css-element-queries';

function ContainerFoot(props) {
    const {isCover, onPosition} = props;
    const [max, setMax] = useState(600);
    const {position, separatorProps} = useResizable({axis: 'y', initial: 312, min: 208, max, reverse: true});

    useEffect(() => {
        const el = document.querySelector(`.${style.container}`);
        const resizeSensor = new ResizeSensor(el, size => {
            setMax(size.height - 240);
        });
        return () => resizeSensor?.detach();
    }, []);

    useEffect(() => {
        // onPosition(position);
    }, [position]);

    return (
        <div className={style.container_foot} style={{height: position}}>
            <div className={style.container_foot_separator} {...separatorProps}></div>
            {isCover ? <ContainerFootDisableCover /> : <ContainerFootContent />}
        </div>
    );
}


样式

.container_foot_separator {
    width: 100%;
    height: 4px;
    background: #191924;
    cursor: row-resize;
}


.container_foot {
    flex: none;
    background: #2b2e3e;
    height: 312px;
    // margin-top: 4px;
    position: relative;
}

js

import {useCallback, useRef, useState} from 'react';

const KEYS_LEFT = ['ArrowLeft', 'Left'];
const KEYS_RIGHT = ['ArrowRight', 'Right'];
const KEYS_UP = ['ArrowUp', 'Up'];
const KEYS_DOWN = ['ArrowDown', 'Down'];
const KEYS_AXIS_X = [...KEYS_LEFT, ...KEYS_RIGHT];
const KEYS_AXIS_Y = [...KEYS_UP, ...KEYS_DOWN];
const KEYS_POSITIVE = [...KEYS_RIGHT, ...KEYS_DOWN];

const useResizable = ({
    axis,
    disabled = false,
    initial = 0,
    min = 0,
    max = Infinity,
    reverse,
    step = 10,
    shiftStep = 50,
    onResizeStart,
    onResizeEnd,
    containerRef
}) => {
    const initialPosition = Math.min(Math.max(initial, min), max);
    const isResizing = useRef(false);
    const [isDragging, setIsDragging] = useState(false);
    const [position, setPosition] = useState(initialPosition);
    const positionRef = useRef(initialPosition);
    const [endPosition, setEndPosition] = useState(initialPosition);

    const handlePointermove = useCallback(
        e => {
            if (!isResizing.current) return;

            if (disabled) return;

            e.stopPropagation();
            e.preventDefault();

            const currentPosition = (() => {
                if (axis === 'x') {
                    if (containerRef?.current) {
                        const containerNode = containerRef.current;
                        const {left, width} = containerNode.getBoundingClientRect();
                        return reverse ? left + width - e.clientX : e.clientX - left;
                    }
                    return reverse ? document.body.offsetWidth - e.clientX : e.clientX;
                }
                if (containerRef?.current) {
                    const containerNode = containerRef.current;
                    const {top, height} = containerNode.getBoundingClientRect();
                    return reverse ? top + height - e.clientY : e.clientY - top;
                }
                return reverse ? document.body.offsetHeight - e.clientY : e.clientY;
            })();

            if (min < currentPosition && currentPosition < max) {
                setPosition(currentPosition);
                positionRef.current = currentPosition;
            }
        },
        [axis, disabled, max, min, reverse, containerRef]
    );

    const handlePointerup = useCallback(
        e => {
            if (disabled) return;

            e.stopPropagation();
            isResizing.current = false;
            setIsDragging(false);
            setEndPosition(positionRef.current);
            document.removeEventListener('pointermove', handlePointermove);
            document.removeEventListener('pointerup', handlePointerup);
            if (onResizeEnd) onResizeEnd({position: positionRef.current});
        },
        [disabled, handlePointermove, onResizeEnd]
    );

    const handlePointerdown = useCallback(
        e => {
            if (disabled) return;

            e.stopPropagation();
            isResizing.current = true;
            setIsDragging(true);
            document.addEventListener('pointermove', handlePointermove);
            document.addEventListener('pointerup', handlePointerup);
            if (onResizeStart) onResizeStart({position: positionRef.current});
        },
        [disabled, handlePointermove, handlePointerup, onResizeStart]
    );

    const handleKeyDown = useCallback(
        e => {
            if (disabled) return;

            if (e.key === 'Enter') {
                setPosition(initial);
                positionRef.current = initial;
                return;
            }
            if ((axis === 'x' && !KEYS_AXIS_X.includes(e.key)) || (axis === 'y' && !KEYS_AXIS_Y.includes(e.key))) {
                return;
            }

            if (onResizeStart) onResizeStart({position: positionRef.current});

            const changeStep = e.shiftKey ? shiftStep : step;
            const reversed = reverse ? -1 : 1;
            const dir = KEYS_POSITIVE.includes(e.key) ? reversed : -1 * reversed;

            const newPosition = position + changeStep * dir;
            if (newPosition < min) {
                setPosition(min);
                positionRef.current = min;
            } else if (newPosition > max) {
                setPosition(max);
                positionRef.current = max;
            } else {
                setPosition(newPosition);
                positionRef.current = newPosition;
            }

            if (onResizeEnd) onResizeEnd({position: positionRef.current});
        },
        [disabled, axis, onResizeStart, shiftStep, step, reverse, position, min, max, onResizeEnd, initial]
    );

    const handleDoubleClick = useCallback(() => {
        if (disabled) return;
        setPosition(initial);
        positionRef.current = initial;
    }, [disabled, initial]);

    return {
        position,
        endPosition,
        isDragging,
        separatorProps: {
            onPointerDown: handlePointerdown,
            onKeyDown: handleKeyDown,
            onDoubleClick: handleDoubleClick
        },
        setPosition,
        splitterProps: {
            onPointerDown: handlePointerdown,
            onKeyDown: handleKeyDown,
            onDoubleClick: handleDoubleClick
        }
    };
};

export default useResizable;

// export type SeparatorProps = React.ComponentPropsWithoutRef<'hr'>;
/**
 * @deprecated Use SeparatorProps instead
 */
// export type SplitterProps = SeparatorProps;

// export type Resizable = {
/**
 * border position
 */
//   position: number;
/**
 * position at end of drag
 */
//   endPosition: number;
/**
 * whether the border is dragging
 */
//   isDragging: boolean;
/**
 * props for drag bar
 */
//   separatorProps: SeparatorProps;
/**
 * set border position
 */
//   setPosition: React.Dispatch<React.SetStateAction<number>>;
/**
 * @deprecated Use separatorProps instead
 */
//   splitterProps: SplitterProps;
// };

// export type ResizeCallbackArgs = {
/**
 * position at the time of callback
 */
//   position: number;
// };

// export type UseResizableProps = {
/**
 * direction of resizing
 */
//   axis: 'x' | 'y';
/**
 * ref of the container element
 */
//   containerRef?: React.RefObject<HTMLElement>;
/**
 * if true, cannot resize
 */
//   disabled?: boolean;
/**
 * initial border position
 */
//   initial?: number;
/**
 * minimum border position
 */
//   min?: number;
/**
 * maximum border position
 */
//   max?: number;
/**
 * calculate border position from other side
 */
//   reverse?: boolean;
/**
 * resizing step with keyboard
 */
//   step?: number;
//   shiftStep?: number;
/**
 * callback when border position changes start
 */
//   onResizeStart?: (args: ResizeCallbackArgs) => void;
/**
 * callback when border position changes end
 */
//   onResizeEnd?: (args: ResizeCallbackArgs) => void;
// };

// export type ResizableProps = UseResizableProps & {
/**
 * callback children
 */
//   children: (props: Resizable) => JSX.Element;
// };




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值