UNPKG

naviix

Version:

Spatial navigation. Arrow key navigation.

478 lines (466 loc) 12 kB
/* 这是 1.0.2 版本的 naviix。 */ 'use strict'; function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function naviix(squares) { var formattedSquares = formatIpt(squares); var _getX = getX(formattedSquares), rawX = _getX.x, firstInWrap = _getX.firstInWrap; var x = genUserX(rawX, firstInWrap); return x; } function getX(squares, notRoot) { var mergedX = new Map(); var firstInWrap = new Map(); if (!notRoot && squares.length > 1) { // 位于 root,且区域数量大于 1 var wraps = squares.map(function (info) { return info.wrap; }); squares.forEach(function (s) { firstInWrap.set(s.wrap.id, s.locs[0]); }); var _getXBySimple = getXBySimple(wraps), x = _getXBySimple.x; mergedX = x; } squares.forEach(function (_ref) { var locs = _ref.locs, subs = _ref.subs, wrap = _ref.wrap; var subWraps = (subs || []).map(function (s) { return s.wrap; }); (subs || []).map(function (s) { firstInWrap.set(s.wrap.id, s.locs[0]); }); var newLocs = locs.concat(subWraps); var _getXBySimple2 = getXBySimple(newLocs, wrap, subWraps), x = _getXBySimple2.x; var _getX2 = getX(subs || [], true), subsX = _getX2.x, _firstInWrap = _getX2.firstInWrap; mergedX = new Map(Array.from(mergedX).concat(Array.from(x)).concat(Array.from(subsX))); firstInWrap = new Map(Array.from(firstInWrap).concat(Array.from(_firstInWrap))); }); return { x: mergedX, firstInWrap: firstInWrap }; } function getXBySimple(squares, wrap, subWraps) { var sortedX = [].concat(squares); sortedX.sort(function (s1, s2) { return s1.loc[0] - s2.loc[0]; }); var sortedY = [].concat(squares); sortedY.sort(function (s1, s2) { return s1.loc[1] - s2.loc[1]; }); var dirMap = new Map(); var t = -Infinity; var b = Infinity; var l = Infinity; var r = -Infinity; squares.forEach(function (s) { var _s$loc = s.loc, x = _s$loc[0], y = _s$loc[1], t1 = _s$loc[2], t2 = _s$loc[3]; var sOrderY = sortedY.findIndex(function (e) { return e.id === s.id; }); var sOrderX = sortedX.findIndex(function (e) { return e.id === s.id; }); // next down element var minDownDis = Infinity; var downS = wrap; // 默认指向包裹层 var gotDown = false; for (var i = sOrderY - 1; i > -1; --i) { // 从距离元素最近的位置寻找 var s2 = sortedY[i]; var _getMinDownDis = getMinDownDis(s.loc, s2.loc), dis = _getMinDownDis.dis, isProj = _getMinDownDis.isProj; if (dis < minDownDis) { gotDown = true; downS = s2; minDownDis = dis; if (isProj) break; // 投影距离是最短距离,无需后续比较 } } // next up element var minUpDis = Infinity; var upS = wrap; var gotUp = false; for (var _i = sOrderY + 1; _i < sortedY.length; ++_i) { var _s = sortedY[_i]; var _getMinUpDis = getMinUpDis(s.loc, _s.loc), _dis = _getMinUpDis.dis, _isProj = _getMinUpDis.isProj; if (_dis < minUpDis) { gotUp = true; upS = _s; minUpDis = _dis; if (_isProj) break; } } // next left element var minLDis = Infinity; var lS = wrap; var gotLeft = false; for (var _i2 = sOrderX - 1; _i2 > -1; --_i2) { var _s2 = sortedX[_i2]; var _getMinLeftDis = getMinLeftDis(s.loc, _s2.loc), _dis2 = _getMinLeftDis.dis, _isProj2 = _getMinLeftDis.isProj; if (_dis2 < minLDis) { gotLeft = true; lS = _s2; minLDis = _dis2; if (_isProj2) break; } } // next right element var minRDis = Infinity; var rS = wrap; var gotRight = false; for (var _i3 = sOrderX + 1; _i3 < sortedX.length; ++_i3) { var _s3 = sortedX[_i3]; var _getMinRightDis = getMinRightDis(s.loc, _s3.loc), _dis3 = _getMinRightDis.dis, _isProj3 = _getMinRightDis.isProj; if (_dis3 < minRDis) { gotRight = true; rS = _s3; minRDis = _dis3; if (_isProj3) break; } } dirMap.set(s.id, { up: upS, down: downS, left: lS, right: rS, nextWrap: { // 指向元素是否为包裹层 up: !gotUp, down: !gotDown, right: !gotRight, left: !gotLeft }, nextSubWrap: { // 是否内部的包裹层 up: subWraps.includes(upS), down: subWraps.includes(downS), right: subWraps.includes(rS), left: subWraps.includes(lS) } }); // 更新边界 t = y + t2 > t ? y + t2 : t; b = y - t2 < b ? y - t2 : b; l = x - t2 < l ? x - t1 : l; r = x + t2 > r ? x + t1 : r; }); return { x: dirMap, t: t, b: b, l: l, r: r }; } function getMinDownDis(s, s2) { var x = s[0], y = s[1], t1 = s[2], t2 = s[3]; var x2 = s2[0], y2 = s2[1], t1a = s2[2], t2a = s2[3]; var dis = Infinity; var isProj = false; if (y > y2) { // is below if (x2 - t1a > x + t1) { // is right corner if (x2 - t1a - x - t1 < y - t2 - y2 - t2a) { dis = getDistance(x + t1, y - t2, x2 - t1a, y2 + t2a); } } else if (x2 + t1a < x - t1) { // is left corner if (x - t1 - x2 - t1a < y - t2 - y2 - t2a) { dis = getDistance(x + t1, y - t2, x2 + t1a, y2 + t2a); } } else { // is project isProj = true; dis = Math.pow(y - t2 - y2 - t2a, 2); if (y2 + t2a > y - t2) dis = -dis; } } return { dis: dis, isProj: isProj }; } function getMinUpDis(s, s2) { var x = s[0], y = s[1], t1 = s[2], t2 = s[3]; var x2 = s2[0], y2 = s2[1], t1a = s2[2], t2a = s2[3]; var dis = Infinity; var isProj = false; if (y < y2) { // is above if (x2 - t1a > x + t1) { // is right corner if (x2 - t1a - x - t1 < y2 - t2a - y - t2) { dis = getDistance(x + t1, y + t2, x2 - t1a, y2 - t2a); } } else if (x2 + t1a < x - t1) { // is left corner if (x - t1 - x2 - t1a < y2 - t2a - y - t2) { dis = getDistance(x - t1, y + t2, x2 + t1a, y2 - t2a); } } else { // is project dis = Math.pow(y2 - t2a - y - t2, 2); if (y2 - t2a < y + t2) dis = -dis; isProj = true; } } return { dis: dis, isProj: isProj }; } function getMinLeftDis(s, s2) { var x = s[0], y = s[1], t1 = s[2], t2 = s[3]; var x2 = s2[0], y2 = s2[1], t1a = s2[2], t2a = s2[3]; var dis = Infinity; var isProj = false; if (x > x2) { // is left if (y2 - t2a > y + t2) { // is top corner if (y2 - t2a - y - t2 < x - t1 - x2 - t1a) { // closer x dis = getDistance(x - t1, y + t2, x2 + t1a, y2 - t2a); } } else if (y2 + t2a < y - t2) { // is bottom corder if (y - t2 - y2 - t2a < x - t1 - x2 - t1a) { // closer x dis = getDistance(x - t1, y - t2, x2 + t1a, y2 + t2a); } } else { // is project dis = Math.pow(x - t1 - x2 - t1a, 2); if (x2 + t1a > x - t1) dis = -dis; isProj = true; } } return { dis: dis, isProj: isProj }; } function getMinRightDis(s, s2) { var x = s[0], y = s[1], t1 = s[2], t2 = s[3]; var x2 = s2[0], y2 = s2[1], t1a = s2[2], t2a = s2[3]; var dis = Infinity; var isProj = false; if (x2 > x) { // is right if (y2 - t2a > y + t2) { // is top corner if (y2 - t2a - y - t2 < x2 - t1a - x - t1) { // closer x dis = getDistance(x + t1, y + t2, x2 - t1a, y2 - t2a); } } else if (y2 + t2a < y - t2) { // is bottom corder if (y - t2 - y2 - t2a < x2 - t1a - x - t1) { dis = getDistance(x + t1, y - t2, x2 - t1a, y2 + t2a); } } else { dis = Math.pow(x2 - t1a - x - t1, 2); if (x2 - t1a < x + t1) // overlap dis = -dis; isProj = true; } } return { dis: dis, isProj: isProj }; } function getDistance(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; return dx * dx + dy * dy; // without sqrt } function formatIpt(input) { if (input == null) return; var aryIpt = Array.isArray(input); var simpleIpt = aryIpt && (Array.isArray(input[0]) ? typeof input[0][0] === "number" : input[0].loc != null || istanceofHtmlElement(input[0])); var objIpt = !aryIpt; var normalIpt = !simpleIpt && !objIpt; var formatted = null; if (simpleIpt) { formatted = [{ locs: input.map(function (s) { return Array.isArray(s) ? { id: s, loc: s } : getElementObjLoc(s); }) }]; } else if (objIpt) { var wrap = input.wrap, subs = input.subs, locs = input.locs; formatted = [{ locs: formatIpt(locs)[0].locs }]; if (subs != null) { Object.assign(formatted[0], { subs: formatIpt(input.subs) }); } if (wrap != null) { Object.assign(formatted[0], { wrap: Array.isArray(wrap) ? { loc: wrap, id: wrap } : getElementObjLoc(wrap) }); } } else if (normalIpt) { formatted = input.map(function (item) { var locs = item.locs, wrap = item.wrap, subs = item.subs; var res = { locs: formatIpt(locs)[0].locs, wrap: Array.isArray(wrap) ? { loc: wrap, id: wrap } : getElementObjLoc(wrap) }; if (subs) { Object.assign(res, { subs: formatIpt(subs) }); } return res; }); } return formatted; } function genUserX(rawX, firstInWrap) { var x = new Map(); rawX.forEach(function (val, key) { var up = val.up, right = val.right, down = val.down, left = val.left, nextWrap = val.nextWrap, nextSubWrap = val.nextSubWrap; var upWrap = nextWrap.up, rWrap = nextWrap.right, downWrap = nextWrap.down, lWrap = nextWrap.left; var upSubW = nextSubWrap.up, rSubW = nextSubWrap.right, dSubWrap = nextSubWrap.down, lSubW = nextSubWrap.left; var userUp = genUserDir("up", up, upWrap, upSubW); var userRight = genUserDir("right", right, rWrap, rSubW); var userDown = genUserDir("down", down, downWrap, dSubWrap); var userLeft = genUserDir("left", left, lWrap, lSubW); x.set(key, { up: userUp, right: userRight, down: userDown, left: userLeft }); }); return x; function genUserDir(dirStr, rawDir, wrap, subW) { var userDir = rawDir; if (rawDir && wrap) { // 是否指向包裹层 var wrapTarget = rawX.get(rawDir.id); var nextSubWrap = wrapTarget.nextSubWrap; userDir = wrapTarget[dirStr]; if (nextSubWrap[dirStr]) { userDir = firstInWrap.get(wrapTarget[dirStr].id); } } else if (subW) { // 是否指向子包裹层 userDir = firstInWrap.get(rawDir.id); } return userDir; } } function getElementObjLoc(o) { if (istanceofHtmlElement(o)) return getElementObjLoc2(o); if (istanceofHtmlElement(o.loc)) { return _extends({}, o, { loc: getElementAryLoc(o.loc) }); } return o; } function getElementObjLoc2(e) { return { id: e, loc: getElementAryLoc(e) }; } function getElementAryLoc(e) { var t = e.getBoundingClientRect(); var x = t.x, y = t.y, width = t.width, height = t.height; var halfW = width / 2; var halfH = height / 2; return [x + halfW, -y - halfH, halfW, halfH]; } function istanceofHtmlElement(o) { return o instanceof HTMLElement; } module.exports = naviix; //# sourceMappingURL=naviix.js.map