UNPKG

mind.svg.js

Version:

Display and operate MindMap using SVG in browser

724 lines (668 loc) 21.5 kB
/** * 定义常用名称空间常量 */ export const NS = { SVG: "http://www.w3.org/2000/svg", XLINK: "http://www.w3.org/1999/xlink" }; /** * 将名称转化为属性数据名称 * @param {*} _name */ function namedData(_name) { return `data-${_name}`; } /** * 检查参数是不是一个DOM节点 * @param {*} _node */ export function isNode(_node) { return (_node instanceof window.Node || _node instanceof window.HTMLElement); } /** * 获取CSS的数值部分 * @param {*} _value */ function cssNumber(_value) { return Number((typeof _value === "string" ? _value : String(_value)).replace(/\D+/, "")); } /** * 获取CSS的单位部分 * @param {*} _value */ function cssUnit(_value) { return (typeof _value === "string" ? _value : String(_value)).replace(/\d+/, ""); } /** * 封装Node操作的类 */ export class ENodeClass { constructor (_tagOrNode, _ns) { const node = (this.node = isNode(_tagOrNode) ? _tagOrNode : (_ns ? document.createElementNS(_ns, _tagOrNode) : document.createElement(_tagOrNode))); this.matches = (node.matches || node.matchesSelector || node.msMatchesSelector || node.mozMatchesSelector || node.webkitMatchesSelector || node.oMatchesSelector).bind(node); this.dispatchEvent = node.dispatchEvent.bind(node); } /** * 将一个现成节点和ENode封装关联 * @param {*} _node */ static attach(_node) { return isNode(_node) ? new ENodeClass(_node) : (isENode(_node) ? _node : (_node ? ENodeClass.attach(document.querySelector(_node)) : undefined)); } /** * 获取ID */ get id() { const value = this.node.getAttribute("id"); return value || ""; } /** * 设置ID */ set id(_value) { this.node.setAttribute("id", _value); } /** * 获取标签名 */ get tagName() { return this.node.tagName.toLowerCase(); } /** * 获取风格类 */ get class() { const value = this.node.getAttribute("class"); return value || ""; } /** * 设置风格类 */ set class(_value) { this.node.setAttribute("class", _value); } /** * 检查是否是SVG的成员元素 */ get isSVG() { return this.node instanceof SVGElement; } /** * 获取文本内容 */ get text() { return this.node.textContent || ""; } /** * 设置文本内容 */ set text(_value) { if (this.isSVG) { this.node.innerHTML = ""; this.node.appendChild(document.createTextNode(_value)); } else { this.node.innerText = _value; } } /** * 元素的y坐标 */ get top() { if (this.isSVG) { return this.node.getBBox().y; } else { return this.node.offsetTop; } } /** * 元素的x坐标 */ get left() { if (this.isSVG) { return this.node.getBBox().x; } else { return this.node.offsetLeft; } } /** * 元素的宽度信息 */ get width() { if (this.isSVG) { return this.node.getBBox().width; } else { return this.node.offsetWidth; } } /** * 元素的高度信息 */ get height() { if (this.isSVG) { return this.node.getBBox().height; } else { return this.node.offsetHeight; } } /** * 获取当前节点相对于自身已定位坐标系的矩形 */ get rect() { const node = this.node; return this.isSVG ? node.getBBox() : { x: node.offsetLeft, y: node.offsetTop, width: node.offsetWidth, height: node.offsetHeight }; } /** * 获取元素内部的矩形空间 */ get clientRect() { const node = this.node; return this.isSVG ? node.getBBox() : { x: node.clientLeft, y: node.clientTop, width: node.clientWidth, height: node.clientHeight }; } /** * 获取当前节点相对于全局的空间矩形 */ get globalRect() { return this.node.getBoundingClientRect(); } /** * 获取当前节点相对于父元素的空间矩形 */ get offsetRect() { const node = this.node; const box = this.isSVG ? node.getBoundingClientRect() : { x: node.offsetLeft, y: node.offsetTop, width: node.offsetWidth, height: node.offsetHeight }; return box; } /** * 获取相对于另一个节点的当前节点空间矩形 * @param {*} _node 如果不给该参数,则直接给出当前节点针对自身的空间矩形 */ getRelativeRect(_node) { const box = this.globalRect; const relNode = ENodeClass.attach(_node); if (relNode) { if (relNode.isSVG) { const matrix = relNode.node.getScreenCTM().inverse(); let retMatrix = matrix.translate(box.x, box.y); box.x = retMatrix.e; box.y = retMatrix.f; if (this.isSVG) { const originRect = this.rect; box.width = originRect.width; box.height = originRect.height; } } else { const relGlobalRect = relNode.globalRect; box.x -= relGlobalRect.x - relNode.node.clientLeft; box.y -= relGlobalRect.y - relNode.node.clientTop; } } return box; } /** * 设置纯HTML元素的STYLE中的大小 * @param {*} _width 宽度 * @param {*} _height 高度 */ setSizeInStyle(_width, _height) { const css = window.getComputedStyle ? window.getComputedStyle(this.node) : undefined; if (css) { _width -= cssNumber(css.paddingLeft) + cssNumber(css.paddingRight) + cssNumber(css.borderLeftWidth) + cssNumber(css.borderRightWidth); _height -= cssNumber(css.paddingTop) + cssNumber(css.paddingBottom) + cssNumber(css.borderTopWidth) + cssNumber(css.borderBottomWidth); } this.node.style.width = `${_width}px`; this.node.style.height = `${_height}px`; } /** * 将屏幕上的坐标转化为相对元素的坐标 * @param {*} _x * @param {*} _y */ translatePoint(_x, _y) { if (this.isSVG) { const matrix = this.node.getScreenCTM().inverse(); let {e:x, f:y} = matrix.translate(_x, _y); return {x, y}; } else { const rect = this.globalRect; return { x: _x - rect.x, y: _y - rect.y }; } } /** * 获取或设置属性,传入null为参数值,则说明删除属性 * @param {*} _idOrMap 属性名,或者属性名和属性值构成的对象,或者属性名构成的数组 * @param {*} _value 属性值,如果第一参数为属性名,且不传入该参数,则获取属性; * 如果第一参数为属性名列表,则或略该参数直接批量获取属性; * 如果第一参数为属性名和属性值构成的对象,则忽略该参数 */ attr(_idOrMap, _value) { const node = this.node; if (typeof _idOrMap === "string") { // 只操作单个属性的处理 if (_value === undefined) { return node.getAttribute(_idOrMap); } else if (_value === null) { node.removeAttribute(_idOrMap); } else { node.setAttribute(_idOrMap, _value); } } else if (_idOrMap instanceof Array) { // 批量获取属性 const valueList = {}; for (let item of _idOrMap) { valueList[item] = node.getAttribute(item); } return valueList; } else { // 传入的是一个映射表 for (let name in _idOrMap) { const value = _idOrMap[name]; (value === null) ? node.removeAttribute(name) : node.setAttribute(name, value); } } return this; } /** * 获取或设置风格,传入null为参数值,则说明删除风格 * @param {*} _idOrMap 风格名,或者风格名和风格值构成的对象,或者风格名构成的数组 * @param {*} _value 风格值,如果第一参数为风格名,且不传入该参数,则获取风格; * 如果第一参数为风格名列表,则或略该参数直接批量获取风格; * 如果第一参数为风格名和风格值构成的对象,则忽略该参数 */ style(_idOrMap, _value) { const styleList = this.node.style; if (typeof _idOrMap === "string") { // 只操作单个风格的处理 if (_value === undefined) { return styleList[_idOrMap]; } else { styleList[_idOrMap] = _value; } } else if (_idOrMap instanceof Array) { // 批量获取属性 const valueList = {}; for (let item of _idOrMap) { valueList[item] = styleList[item]; } return valueList; } else { // 传入的是一个映射表 for (let name in _idOrMap) { const value = _idOrMap[name]; styleList[name] = value; } } return this; } /** * 获取或设置扩展数据,传入null为参数值,则说明删除扩展数据 * @param {*} _idOrMap 数据名,或者数据名和数据值构成的对象,或者数据名构成的数组 * @param {*} _value 数据值,如果第一参数为数据名,且不传入该参数,则获取数据; * 如果第一参数为数据名列表,则或略该参数直接批量获取数据; * 如果第一参数为数据名和数据值构成的对象,则忽略该参数 */ data(_idOrMap, _value) { const node = this.node; if (typeof _idOrMap === "string") { // 只操作单个属性的处理 if (_value === undefined) { return JSON.parse(node.getAttribute(namedData(_idOrMap))); } else if (_value === null) { node.removeAttribute(namedData(_idOrMap)); } else { node.setAttribute(namedData(_idOrMap), JSON.stringify(_value)); } } else if (_idOrMap instanceof Array) { // 批量获取属性 const valueList = {}; for (let item of _idOrMap) { valueList[item] = JSON.parse(node.getAttribute(namedData(item))); } return valueList; } else { // 传入的是一个映射表 for (let name in _idOrMap) { const value = _idOrMap[name]; (value === null) ? node.removeAttribute(namedData(name)) : node.setAttribute(namedData(name), JSON.stringify(value)); } } return this; } /** * 检查对象是否具有某个风格类 * @param {*} _name */ hasClass(_name) { return this.matches(`.${_name}`); } /** * 添加对象某个风格类 * @param {*} _name */ addClass(_name) { let classList = this.node.classList; if (classList) { classList.add(_name); } else { const classStr = this.attr("class"); classList = classStr ? classStr.split(/\s+/) : []; if (classList.indexOf(_name) < 0) { this.attr("class", `${classStr} ${_name}`); } } return this; } /** * 移除对象某个风格类 * @param {*} _name */ removeClass(_name) { let classList = this.node.classList; if (classList) { classList.remove(_name); } else { const classStr = this.attr("class"); classList = classStr ? classStr.split(/\s+/) : []; const idx = classList.indexOf(_name); if (idx >= 0) { classList[idx] = ""; this.attr("class", classList.join(" ")); } } return this; } /** * 获取风格类型列表 */ get classList() { const classStr = this.attr("class"); return classStr ? classStr.split(/\s+/) : []; } /** * 查找父节点 * @param {*} _selector */ parent(_selector) { let parentENode; let element = this.node; while ((element = element.parentElement)) { parentENode = ENodeClass.attach(element); if (!_selector) { break; } else if (parentENode.matches(_selector)) { break; } } return parentENode; } /** * 查找根节点 */ get root() { return this.isSVG ? ENodeClass.attach(this.node.ownerSVGElement || this.parent("body")) : this.parent("body"); } /** * 获取符合选择描述的第一个后代 * @param {*} _selector */ firstDescendant(_selector) { _selector = _selector || "*"; const node = this.node.querySelector(_selector); return node ? ENodeClass.attach(node) : undefined; } /** * 获取所有满足选择描述的后代数组 * @param {*} _selector */ descendant(_selector) { _selector = _selector || "*"; const elmList = this.node.querySelectorAll(_selector); const list = []; for (let item of elmList) { const node = ENodeClass.attach(item); if (node.matches(_selector)) { list.push(node); } } return list; } /** * 获取所有满足选择描述的后代的迭代器 * @param {*} _selector */ * getDescandant(_selector) { _selector = _selector || "*"; const elmList = this.node.querySelectorAll(_selector); for (let item of elmList) { const node = ENodeClass.attach(item); if (node.matches(_selector)) { yield node; } } } /** * 获取符合第一个选择描述的子代 * @param {*} _selector */ firstChild(_selector) { if (_selector) { let childNode; for (let item of this.node.children) { let node = ENodeClass.attach(item); if (node.matches(_selector)) { childNode = node; break; } } return childNode; } else { return ENodeClass.attach(this.node.firstElementChild); } } /** * 获取满足选择描述的子代数组 * @param {*} _selector */ children(_selector) { const elmList = this.node.children; const list = []; for (let item of elmList) { const node = ENodeClass.attach(item); if ((!_selector) || node.matches(_selector)) { list.push(node); } } return list; } /** * 获取满足选择描述的子代迭代器 * @param {*} _selector */ * getChildren(_selector) { const elmList = this.node.children; for (let item of elmList) { const node = ENodeClass.attach(item); if ((!_selector) || node.matches(_selector)) { yield node; } } } /** * 加入为一个父元素的最后一个子项 * @param {*} _parent */ appendTo(_parent) { const parentNode = isNode(_parent) ? _parent : (isENode(_parent) ? _parent.node : null); if (parentNode) { this.node.remove(); parentNode.appendChild(this.node); } return this; } /** * 加入为一个父元素的第一个子项 * @param {*} _parent */ insertToTop(_parent) { const parentNode = isNode(_parent) ? _parent : (isENode(_parent) ? _parent.node : null); if (parentNode) { this.node.remove(); parentNode.insertBefore(this.node, parentNode.firstElementChild); } return this; } /** * 插入到某个元素的后面去 * @param {*} _sibling */ insertAfter(_sibling) { const siblingNode = ENodeClass.attach(_sibling); if (siblingNode) { const parentNode = siblingNode.parent(); if (parentNode) { this.node.remove(); const nextSibling = siblingNode.node.nextSibling; nextSibling ? parentNode.node.insertBefore(this.node, nextSibling) : parentNode.node.appendChild(this.node); } } return this; } /** * 插入到某个元素的前面去 * @param {*} _sibling */ insertBefore(_sibling) { const siblingNode = ENodeClass.attach(_sibling); if (siblingNode) { const parentNode = siblingNode.parent(); if (parentNode) { this.node.remove(); parentNode.node.insertBefore(this.node, siblingNode.node); } } return this; } /** * 移除当前元素 */ remove() { this.node.remove(); return this; } /** * 转换点的坐标 * @param {*} _point * @param {*} _toOuter */ convertPoint(_point, _toOuter) { let x = _point ? (_point.x || 0) : 0; let y = _point ? (_point.y || 0) : 0; if (_toOuter) { x += this.node.offsetLeft; y += this.node.offsetTop; } else { x -= this.node.offsetLeft; y -= this.node.offsetTop; } return {x, y}; } /** * 添加事件监听 * @param {*} _event * @param {*} _fn * @param {*} _opt */ on(_event, _fn, _opt) { this.node.addEventListener(_event, _fn, _opt); return this; } /** * 移除事件监听 * @param {*} _event * @param {*} _fn * @param {*} _opt */ off(_event, _fn, _opt) { this.node.removeEventListener(_event, _fn, _opt); return this; } /** * 判断两个节点对象是否相同 * @param {*} _other */ isSame(_other) { return this.node.isSameNode(isENode(_other) ? _other.node : _other); } /** * 创建一个子元素,并追加到最后 * @param {*} _tag * @param {*} _ns */ createChild(_tag, _ns) { const child = new ENodeClass(_tag, _ns); child && child.appendTo(this); return child; } /** * 创建一个SVG的子元素,并追加到最后 * @param {*} _tag */ createSVGChild(_tag) { return this.isSVG ? this.createChild(_tag, NS.SVG) : SVG().appendTo(this); } /** * 删除所有成员 */ clearChildren() { this.node.innerHTML = ""; return this; } } /** * 获取或创建一个ENode对象,如果不传入参数则返回ENodeClass类 * @param {*} _node * @param {*} _ns */ export function ENode(_node, _ns) { return (arguments.length <= 0) ? ENodeClass : new ENodeClass(_node, _ns); } /** * 创建一个SVG的ENode对象 */ export function SVG() { const svg = new ENodeClass("svg", NS.SVG); return svg.attr({ xmlns: NS.SVG, "xmlns:xlink": NS.XLINK, version: "1.1" }); } /** * 检查参数是不是一个ENode封装 * @param {*} _node */ export function isENode(_node) { return _node instanceof ENodeClass; }