naviix
Version:
Spatial navigation. Arrow key navigation.
403 lines (392 loc) • 10 kB
JavaScript
/* 这是 1.0.0 版本的 naviix。 */
;
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;
sortedY.slice(0, sOrderY).forEach(function (s2) {
var dis = getMinDownDis(s.loc, s2.loc);
if (dis < minDownDis) {
gotDown = true;
downS = s2;
minDownDis = dis;
}
});
// next up element
var minUpDis = Infinity;
var upS = wrap;
var gotUp = false;
sortedY.slice(sOrderY + 1).forEach(function (s2) {
var dis = getMinUpDis(s.loc, s2.loc);
if (dis < minUpDis) {
gotUp = true;
upS = s2;
minUpDis = dis;
}
});
// next left element
var minLDis = Infinity;
var lS = wrap;
var gotLeft = false;
sortedX.slice(0, sOrderX).forEach(function (s2) {
var dis = getMinLeftDis(s.loc, s2.loc);
if (dis < minLDis) {
gotLeft = true;
lS = s2;
minLDis = dis;
}
});
// next right element
var minRDis = Infinity;
var rS = wrap;
var gotRight = false;
sortedX.slice(sOrderX + 1).forEach(function (s2) {
var dis = getMinRightDis(s.loc, s2.loc);
if (dis < minRDis) {
gotRight = true;
rS = s2;
minRDis = dis;
}
});
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;
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
dis = Math.pow(y - t2 - y2 - t2a, 2);
if (y2 + t2a > y - t2) dis = -dis;
}
}
return dis;
}
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;
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;
}
}
return dis;
}
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;
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;
}
}
return dis;
}
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;
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;
}
}
return dis;
}
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);
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
} : 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
} : 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
} : 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;
}
}
module.exports = naviix;
//# sourceMappingURL=naviix.js.map