Last active
May 13, 2021 21:16
-
-
Save audunolsen/9225aa14681a50f61c25a5049b0eb06c to your computer and use it in GitHub Desktop.
Returns a detailed set of data regarding an elements position relative to the viewport
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable */ | |
/* | |
type Directions { | |
top: boolean; | |
left: boolean; | |
bottom: boolean; | |
right: boolean; | |
horizontal: boolean; | |
vertical: boolean; | |
all: boolean; | |
} | |
type Percents { | |
horizontal: number; | |
vertical: number; | |
total: number; | |
} | |
type Return { | |
fully?: Directions | |
hidden?: Directions | |
intersects?: Directions | |
percents { | |
visible: Percents | |
occupied: Percents | |
} | |
} | |
*/ | |
export default function visibility(target/* Element | DOMRect */, { relative /* Element | DOMRect */ } = {}) { | |
// TODO: relative parent | |
let rect; | |
if (target instanceof DOMRect) rect = target; | |
if (target instanceof Element) rect = target.getBoundingClientRect(); | |
const outerArea = { width: innerWidth, height: innerHeight }; | |
let fully = { | |
top: rect.top >= 0, | |
left: rect.left >= 0, | |
bottom: rect.bottom <= outerArea.height, | |
right: rect.right <= outerArea.width | |
}; | |
fully.horizontal = fully.left && fully.right; | |
fully.vertical = fully.top && fully.bottom; | |
fully.all = fully.horizontal && fully.vertical; | |
let hidden = { | |
top: rect.top + rect.height <= 0, | |
left: rect.left + rect.width <= 0, | |
bottom: rect.bottom - rect.height >= outerArea.height, | |
right: rect.right - rect.width >= outerArea.width | |
}; | |
hidden.horizontal = hidden.left || hidden.right; | |
hidden.vertical = hidden.top || hidden.bottom; | |
hidden.all = hidden.horizontal && hidden.vertical; | |
let intersects = Object.fromEntries(['top', 'left', 'bottom', 'right'].map(dir => [ | |
dir, !fully[dir] && !hidden[dir] | |
])); | |
intersects.horizontal = intersects.left || intersects.right; | |
intersects.vertical = intersects.top || intersects.bottom; | |
intersects.all = intersects.horizontal && intersects.vertical; | |
const percents = { | |
occupied: {}, | |
visible: {} | |
}; | |
const axes = { | |
horizontal: ['left', 'right', 'width'], | |
vertical: ['top', 'bottom', 'height'] | |
}; | |
for (const [axis, [start, end, length]] of Object.entries(axes)) { | |
if (hidden[axis]) percents.visible[axis] = 0; | |
if (fully[axis]) percents.visible[axis] = 1; | |
if (intersects[start] ^ intersects[end]) { | |
percents.visible[axis] = intersects[start] | |
? (rect[start] + rect[length]) / rect[length] | |
: (outerArea[length] - rect[start]) / rect[length]; | |
percents.occupied[axis] = intersects[start] | |
? (rect[start] + rect[length]) / outerArea[length] | |
: (outerArea[length] - rect[start]) / outerArea[length]; | |
} | |
if (intersects[start] && intersects[end]) { | |
percents.occupied[axis] = 1; | |
percents.visible[axis] = outerArea[length] / rect[length]; | |
} | |
if (fully[start] && fully[end]) percents.occupied[axis] = rect[length] / outerArea[length]; | |
} | |
const | |
visibleWidth = rect.width * percents.visible.horizontal, | |
visibleHeight = rect.height * percents.visible.vertical, | |
visibleArea = visibleWidth * visibleHeight; | |
percents.occupied.total = visibleArea / (outerArea.width * outerArea.height); | |
percents.visible.total = visibleArea / (rect.height * rect.width); | |
const falsy = e => !!e === false; | |
if (Object.values(fully).every(falsy)) fully = false; | |
if (Object.values(hidden).every(falsy)) hidden = false; | |
if (Object.values(intersects).every(falsy)) intersects = false; | |
return { fully, hidden, intersects, ...percents }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
13.05.21 - In progress refactoring. Now returns a lot more useful data and is far more succinctly written