|
3 | 3 | // (elements' position evaluated at run-time) |
4 | 4 | // |
5 | 5 |
|
6 | | -const getPoint = function (dir, pos) { |
7 | | - if (dir === 'left') return { x: pos.left, y: pos.top + ((pos.bottom - pos.top) / 2) } // left middle |
8 | | - else if (dir === 'right') return { x: pos.right, y: pos.top + ((pos.bottom - pos.top) / 2) } // right middle |
9 | | - else if (dir === 'top') return { x: pos.left + ((pos.right - pos.left) / 2), y: pos.top } // center top |
10 | | - else if (dir === 'bottom') return { x: pos.left + ((pos.right - pos.left) / 2), y: pos.bottom } // center bottom |
| 6 | +/** |
| 7 | + * Get element position |
| 8 | + * @param el |
| 9 | + * @returns {ClientRect|null} |
| 10 | + */ |
| 11 | +const getElRect = function (el) { |
| 12 | + let rect = el.getBoundingClientRect() |
| 13 | + if (rect.width === 0 && rect.height === 0) return null // element is not visible |
| 14 | + return rect |
| 15 | +} |
| 16 | + |
| 17 | +/** |
| 18 | + * Get center point of a rectangle. The points are used to compare distance between elements. |
| 19 | + * up |
| 20 | + * ┌────*────┐ |
| 21 | + * │ │ |
| 22 | + * left * * right |
| 23 | + * │ │ |
| 24 | + * └────*────┘ |
| 25 | + * down |
| 26 | + * |
| 27 | + * @param side |
| 28 | + * @param rect {ClientRect} |
| 29 | + * @returns {{x, y}} |
| 30 | + */ |
| 31 | +const getCenterPoint = function (side, rect) { |
| 32 | + if (side === 'left') return { x: rect.left, y: rect.top + ((rect.bottom - rect.top) / 2) } |
| 33 | + else if (side === 'right') return { x: rect.right, y: rect.top + ((rect.bottom - rect.top) / 2) } |
| 34 | + else if (side === 'up') return { x: rect.left + ((rect.right - rect.left) / 2), y: rect.top } |
| 35 | + else if (side === 'down') return { x: rect.left + ((rect.right - rect.left) / 2), y: rect.bottom } |
11 | 36 | } |
12 | 37 |
|
| 38 | +/** |
| 39 | + * Calculate distance between two points |
| 40 | + * @param pos1 {{x, y}} |
| 41 | + * @param pos2 {{x, y}} |
| 42 | + * @returns {number} |
| 43 | + */ |
13 | 44 | const getDistance = function (pos1, pos2) { |
14 | 45 | let a = pos1.x - pos2.x |
15 | 46 | let b = pos1.y - pos2.y |
16 | 47 |
|
17 | 48 | return Math.sqrt(a * a + b * b) |
18 | 49 | } |
19 | 50 |
|
| 51 | +const invertKey = {left: 'right', right: 'left', up: 'down', down: 'up'} |
| 52 | + |
20 | 53 | /** |
21 | 54 | * |
22 | 55 | */ |
23 | | -export function navDynamic (key, navTree, focusedNode) { |
| 56 | +export function navDynamic (key, navTree, deepestFocusedNode) { |
24 | 57 | if (key !== 'left' && key !== 'right' && key !== 'up' && key !== 'down') return false |
25 | 58 |
|
26 | | - let focusedEl |
| 59 | + let srcEl |
27 | 60 | if (navTree.focusedNode !== null) { |
28 | | - focusedEl = navTree.nodes[navTree.focusedNode].el |
29 | | - } else if (focusedNode) { |
30 | | - focusedEl = focusedNode.el |
| 61 | + srcEl = navTree.nodes[navTree.focusedNode].el |
| 62 | + } else if (deepestFocusedNode) { |
| 63 | + srcEl = deepestFocusedNode.el |
31 | 64 | } |
32 | 65 |
|
33 | | - let srcPoint = {left: 'left', right: 'right', up: 'top', down: 'bottom'} |
34 | | - let dstPoint = {left: 'right', right: 'left', up: 'bottom', down: 'top'} |
35 | | - |
36 | | - let point = focusedEl ? getPoint(srcPoint[key], focusedEl.getBoundingClientRect()) : {x: 0, y: 0} |
| 66 | + // get src element position |
| 67 | + let srcRect |
| 68 | + let srcPoint |
| 69 | + if (srcEl && (srcRect = getElRect(srcEl))) { |
| 70 | + srcPoint = getCenterPoint(key, srcRect) |
| 71 | + } else { // no element, or not visible |
| 72 | + srcPoint = {x: 0, y: 0} |
| 73 | + } |
37 | 74 |
|
38 | 75 | let minDistance = null |
39 | 76 | let minDistanceNode |
40 | 77 |
|
41 | | - // Loop over children nodes and find a node that is at minimum distance from previously focused element |
| 78 | + // Loop over child nodes to find a node that is at minimum distance from previously focused element |
42 | 79 | navTree.nodesId.forEach(id => { |
43 | | - let node = navTree.nodes[id] |
44 | | - let el = node.el |
45 | | - if (el !== focusedEl) { |
46 | | - let elPoint = getPoint(dstPoint[key], el.getBoundingClientRect()) |
47 | | - |
48 | | - if ((key === 'left' && elPoint.x > point.x) || |
49 | | - (key === 'right' && elPoint.x < point.x) || |
50 | | - (key === 'up' && elPoint.y > point.y) || |
51 | | - (key === 'down' && elPoint.y < point.y)) return |
52 | | - |
53 | | - let distance = getDistance(point, elPoint) |
54 | | - |
55 | | - if (minDistance === null || minDistance > distance) { |
56 | | - minDistance = distance |
57 | | - minDistanceNode = node |
58 | | - } |
| 80 | + let dstNode = navTree.nodes[id] |
| 81 | + let dstEl = dstNode.el |
| 82 | + if (dstEl === srcEl) return |
| 83 | + |
| 84 | + // get dst element position |
| 85 | + let dstRect = getElRect(dstEl) |
| 86 | + if (!dstRect) return // element is not visible, skip |
| 87 | + let dstPoint = getCenterPoint(invertKey[key], dstRect) |
| 88 | + |
| 89 | + // skip if element is beyond the scope |
| 90 | + if ((key === 'left' && dstPoint.x > srcPoint.x) || |
| 91 | + (key === 'right' && dstPoint.x < srcPoint.x) || |
| 92 | + (key === 'up' && dstPoint.y > srcPoint.y) || |
| 93 | + (key === 'down' && dstPoint.y < srcPoint.y)) return |
| 94 | + |
| 95 | + let distance = getDistance(srcPoint, dstPoint) |
| 96 | + |
| 97 | + if (minDistance === null || minDistance > distance) { |
| 98 | + minDistance = distance |
| 99 | + minDistanceNode = dstNode |
59 | 100 | } |
60 | 101 | }) |
61 | 102 |
|
|
0 commit comments