mouse-selection
Version: 
A mouse frame selection plugin using JavaScript
349 lines (348 loc) • 13.9 kB
JavaScript
"use strict";
exports.__esModule = true;
function isDOMType(value) {
    return isDOM(value);
}
function isDocument(value) {
    return (value === null || value === void 0 ? void 0 : value.nodeName) === "#document";
}
/**
 * 缩放rect
 */
function scaleRect(rect, scale) {
    if (scale === 1) {
        return rect;
    }
    return {
        left: rect.left * scale,
        top: rect.top * scale,
        width: rect.width * scale,
        height: rect.height * scale,
        right: rect.right * scale,
        bottom: rect.bottom * scale
    };
}
var rectangleElementInlineStyle = "position: absolute;pointer-events: none;border: 1px solid rgb(45, 140, 240);background: rgba(45, 140, 240, 0.2);";
var getInitCustomRect = function () { return ({
    left: 0,
    top: 0,
    width: 0,
    height: 0,
    right: 0,
    bottom: 0
}); };
var MouseSelection = /** @class */ (function () {
    function MouseSelection(domOrConfig, config) {
        var _this = this;
        this.domRect = getInitCustomRect();
        this.selectionPagePositionRect = getInitCustomRect();
        this.selectionDOMPositionRect = getInitCustomRect();
        // 用于标记鼠标点下时的坐标
        this.startX = 0;
        this.startY = 0;
        // 是否有缩放
        this.scale = 1.0;
        // 当前是否在框选
        this.moving = false;
        // 矩形框选元素类名
        this.RectangleElementClassName = "frame-selection-rectangle-element";
        /**
         * @description mousedown事件回调
         * @param event 鼠标事件对象
         */
        this._selectStart = function (event) {
            var _a, _b, _c, _d;
            var nodeList = document.querySelectorAll(_this.config.stopSelector);
            var isStopNode = findNode(event.target, Array.from(nodeList));
            if (_this.config.stopSelector && isStopNode) {
                return;
            }
            if ((_a = _this.config) === null || _a === void 0 ? void 0 : _a.stopPropagation) {
                event.stopPropagation();
            }
            // 如果不是鼠标左键按下不操作
            if (event.button !== 0) {
                return;
            }
            // 如果设置了disabled钩子函数,并且返回值为true,不操作
            if (((_b = _this.config) === null || _b === void 0 ? void 0 : _b.disabled) && ((_c = _this.config) === null || _c === void 0 ? void 0 : _c.disabled())) {
                return;
            }
            _this.rectangleElement = _this._createRectangleElement();
            _this.moving = true;
            // 设置所作用的DOM的定位及尺寸信息
            _this.domRect = _this._getDOMRect(_this.targetDom);
            // 鼠标点下时距离作用DOM的偏移,需要考虑滚动,还需要考虑缩放
            var x = (event.pageX + _this.wrapDOM.scrollLeft - window.pageXOffset) /
                _this.scale;
            var y = (event.pageY + _this.wrapDOM.scrollTop - window.pageYOffset) / _this.scale;
            // 显示矩形框选元素
            _this._setRectangleElementStyle("display", "block");
            // 设置起始点坐标
            _this._setStartPosition(x - 2, y - 2);
            // 更新矩形框选元素
            _this.selectionPagePositionRect = _this.getSelectionPagePosition(x, y);
            _this.selectionDOMPositionRect = _this.getSelectionDOMPosition(_this.selectionPagePositionRect);
            _this._updateRectangleElementStyle(_this.selectionDOMPositionRect);
            var callback = (_d = _this.config) === null || _d === void 0 ? void 0 : _d.onMousedown;
            callback && callback(event);
            document.addEventListener("mouseup", _this._selectEnd);
            document.addEventListener("mousemove", _this._selecting);
        };
        /**
         * @description mousemove事件回调
         * @param event 鼠标事件对象
         */
        this._selecting = function (event) {
            var _a;
            if (!_this.moving) {
                return;
            }
            // 鼠标当前距离作用DOM的偏移,需要考虑滚动, 还需要考虑缩放
            var x = (event.pageX + _this.wrapDOM.scrollLeft - window.pageXOffset) /
                _this.scale;
            var y = (event.pageY + _this.wrapDOM.scrollTop - window.pageYOffset) / _this.scale;
            _this.selectionPagePositionRect = _this.getSelectionPagePosition(x, y);
            var refitedMouseEvent = event;
            _this.selectionDOMPositionRect = _this.getSelectionDOMPosition(_this.selectionPagePositionRect);
            refitedMouseEvent.selectionDOMRect = JSON.parse(JSON.stringify(_this.selectionDOMPositionRect));
            _this._updateRectangleElementStyle(_this.selectionDOMPositionRect);
            var callback = (_a = _this
                .config) === null || _a === void 0 ? void 0 : _a.onMousemove;
            callback && callback(refitedMouseEvent);
        };
        /**
         * @description mouseup事件回调
         * @param event 鼠标事件对象
         */
        this._selectEnd = function (event) {
            var _a;
            document.removeEventListener("mousemove", _this._selecting);
            document.removeEventListener("mouseup", _this._selectEnd);
            _this._setRectangleElementStyle("display", "none");
            _this.moving = false;
            var callback = (_a = _this.config) === null || _a === void 0 ? void 0 : _a.onMouseup;
            callback && callback(event);
        };
        var dom = document;
        this.config = config;
        if (isDOMType(domOrConfig)) {
            dom = domOrConfig;
        }
        else if (!!domOrConfig) {
            this.config = domOrConfig;
        }
        this.targetDom = dom;
        if (isDocument(this.targetDom)) {
            this.wrapDOM = document.body;
        }
        else {
            this.wrapDOM = this.targetDom;
        }
        this.scale = this.config.scale || 1.0; // 默认无缩放
        this._setWrapDomPositionStyle();
        this._addMousedownListener(this.targetDom);
    }
    /**
     * @description 获取框选元素以作用DOM为准的偏移和尺寸信息
     * @param left 距离页面左侧距离
     * @param top 距离页面顶部距离
     * @param width 宽度
     * @param height 高度
     */
    MouseSelection.prototype.getSelectionPagePosition = function (x, y) {
        var domRect = this.domRect;
        x = x - 2;
        y = y - 2;
        var left = Math.max(domRect.left, Math.min(this.startX, x));
        var top = Math.max(domRect.top, Math.min(this.startY, y));
        var width = Math.max(this.startX, Math.min(x, this.wrapDOM.scrollWidth + domRect.left - 2)) - left;
        var height = Math.max(this.startY, Math.min(y, this.wrapDOM.scrollHeight + domRect.top - 2)) - top;
        return {
            left: left,
            top: top,
            width: width,
            height: height,
            right: left + width,
            bottom: top + height
        };
    };
    /**
     * @description 获取矩形框选元素以传入的DOM为准的偏移和尺寸信息
     * @param selectionPagePositionRect getSelectionPagePosition返回的值
     */
    MouseSelection.prototype.getSelectionDOMPosition = function (selectionPagePositionRect) {
        var left = selectionPagePositionRect.left, top = selectionPagePositionRect.top, width = selectionPagePositionRect.width, height = selectionPagePositionRect.height, right = selectionPagePositionRect.right, bottom = selectionPagePositionRect.bottom;
        var _a = this.domRect, DOMLeft = _a.left, DOMTop = _a.top;
        return {
            left: left - DOMLeft,
            top: top - DOMTop,
            width: width,
            height: height,
            right: right - DOMLeft,
            bottom: bottom - DOMTop
        };
    };
    /**
     * @description 工具方法,传入一个包含left/top/width/height字段的对象,返回这个参数描述的矩形是否与框选矩形相交
     * @param positionSizeMap {left,top,width,height} 要判断的
     */
    MouseSelection.prototype.isInTheSelection = function (_a) {
        var left = _a.left, top = _a.top, width = _a.width, height = _a.height;
        var _b = this.selectionDOMPositionRect, x = _b.left, y = _b.top, w = _b.width, h = _b.height;
        return left + width > x && x + w > left && top + height > y && y + h > top;
    };
    /**
     * @description 注销方法
     */
    MouseSelection.prototype.destroy = function () {
        this.rectangleElement && this.wrapDOM.removeChild(this.rectangleElement);
        this._removeMousedownListener(this.targetDom);
        this.rectangleElement = null;
        this.targetDom = null;
        this.domRect = null;
        this.selectionPagePositionRect = null;
        this.selectionDOMPositionRect = null;
        this.startX = null;
        this.startY = null;
        this.moving = null;
        this.wrapDOM = null;
    };
    /**
     * @description 如果未明确设置notSetWrapPosition为true,则给作用容器加position: relative属性
     */
    MouseSelection.prototype._setWrapDomPositionStyle = function () {
        var _a;
        if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.notSetWrapPosition) {
            return;
        }
        var position = getComputedStyle(this.wrapDOM).position;
        if (position === "static") {
            this.wrapDOM.style.position = "relative";
        }
    };
    /**
     * @description 在document.body中创建矩形框选元素
     *              不管事件绑定到哪个DOM,矩形框选元素都添加到document.body
     * @returns 矩形框选元素
     */
    MouseSelection.prototype._createRectangleElement = function () {
        var _this = this;
        var _a, _b;
        var ele = Array.from(this.wrapDOM.children).find(function (node) {
            return Array.from(node.classList).includes(_this.RectangleElementClassName);
        });
        if (ele) {
            this.wrapDOM.removeChild(ele);
        }
        ele = document.createElement("div");
        var customClassName = (_a = this.config) === null || _a === void 0 ? void 0 : _a.className;
        ele.className =
            this.RectangleElementClassName +
                (customClassName ? " " + customClassName : "");
        ele.style.cssText =
            rectangleElementInlineStyle +
                ("z-index: " + (((_b = this.config) === null || _b === void 0 ? void 0 : _b.zIndex) || 99999999));
        this.wrapDOM.appendChild(ele);
        return ele;
    };
    /**
     * @description 设置鼠标按下时起始坐标
     * @param x
     * @param y
     */
    MouseSelection.prototype._setStartPosition = function (x, y) {
        this.startX = x;
        this.startY = y;
    };
    /**
     * @description 绑定mousedown事件
     * @param dom 要绑定事件的dom
     */
    MouseSelection.prototype._addMousedownListener = function (dom) {
        dom.addEventListener("mousedown", this._selectStart);
    };
    /**
     * @description 解绑mousedown事件
     * @param dom 要解绑事件的dom
     */
    MouseSelection.prototype._removeMousedownListener = function (dom) {
        dom === null || dom === void 0 ? void 0 : dom.removeEventListener("mousedown", this._selectStart);
    };
    /**
     * @description 获取DOM的Rect信息,如果是document,只返回6个值
     * @param dom 要获取Rect信息的dom
     */
    MouseSelection.prototype._getDOMRect = function (dom) {
        var domRect = isDocument(dom)
            ? {
                left: 0,
                top: 0,
                width: window.innerWidth,
                height: window.innerHeight,
                right: window.innerWidth,
                bottom: window.innerHeight
            }
            : dom.getBoundingClientRect();
        return scaleRect(domRect, 1 / this.scale);
    };
    /**
     * @description 设置矩形框选元素样式
     * @param props CSS属性名
     * @param value CSS属性值
     */
    MouseSelection.prototype._setRectangleElementStyle = function (props, value) {
        this.rectangleElement.style[props] = value;
    };
    /**
     * @description 更新矩形框选元素样式
     * @param currentX 当前鼠标event.pageX值
     * @param currentY 当前鼠标event.pageY值
     */
    MouseSelection.prototype._updateRectangleElementStyle = function (rect) {
        var left = rect.left, top = rect.top, width = rect.width, height = rect.height;
        this._setRectangleElementStyle("left", left + "px");
        this._setRectangleElementStyle("top", top + "px");
        this._setRectangleElementStyle("width", width + "px");
        this._setRectangleElementStyle("height", height + "px");
    };
    return MouseSelection;
}());
/**
 * @description 判断一个值是否是DOM对象
 * @param object 要判断的值
 * @returns {boolean}
 */
function isDOM(object) {
    if (!object || typeof object !== "object") {
        return false;
    }
    if (typeof HTMLElement === "function") {
        return object instanceof HTMLElement || object instanceof HTMLDocument;
    }
    else {
        return (object &&
            typeof object === "object" &&
            object.nodeType &&
            typeof object.nodeName === "string");
    }
}
/**
 * @description 判断当前点击的元素是否是给定的元素,或其父级是否是给定的元素
 * @param target 当前鼠标点中的节点
 * @param dom 要找的节点
 */
function findNode(target, nodeList) {
    if (nodeList.some(function (node) { return target === node; })) {
        return true;
    }
    else {
        if (target.parentNode) {
            return findNode(target.parentNode, nodeList);
        }
        else {
            return false;
        }
    }
}
exports["default"] = MouseSelection;