@vtbag/utensil-drawer
Version:
Pull out just what you need to craft seamless transitions. The Utensil Drawer holds reusable functions to help you build websites with view transitions. It is a bit sparse right now, but like the one in your kitchen, it is bound to fill up over time.
58 lines (57 loc) • 2.77 kB
JavaScript
/* Sets the view transition name in the style attribute of selected elements, see setGivenViewTransitionNames.
`selector´is an arbitrary CSS selector for the current document
*/
export function setSelectedViewTransitionNames(selector, prefix, force = false) {
let selected;
let stripped = selector.replace(/:in-viewport\s*$/, '');
try {
selected = document.querySelectorAll(stripped);
}
catch (e) {
console.error(e.message.replace(/.*:/, '[vtbag] Error selecting elements for view transition names:'));
return;
}
let elements = [...selected];
if (selector.length !== stripped.length) {
elements = elements.filter((e) => {
const rect = e.getBoundingClientRect();
const overlapX = Math.max(0, Math.min(rect.right, window.innerWidth) - Math.max(rect.left, 0));
const overlapY = Math.max(0, Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0));
console.log(rect, overlapX, overlapY);
return overlapX > 0 && overlapY > 0;
});
}
setGivenViewTransitionNames(elements, prefix, force);
}
/* Sets the view transition name in the style attribute of the given elements.
If `elements` has exactly one entry, `prefix` is used as a name.
Otherwise the `elements` are named with the `prefix` with an appended index starting from 0.
If `force` is true, the names are always set, otherwise only if they are not already set.
If the prefix string ends with a '~', the character is replaced by a '-' and the names are assigned in random order.
*/
export function setGivenViewTransitionNames(elements, prefix, force = false) {
if (prefix[prefix.length - 1] === '~') {
prefix = prefix.slice(0, -1) + '-';
shuffle(elements);
}
prefix = CSS.escape(prefix);
elements.forEach((element, idx, array) => {
const name = `${prefix}${array.length > 1 && prefix !== '' && prefix !== 'none' && prefix !== 'auto' && prefix !== 'match-element' ? idx : ''}`;
(force && (element.style.viewTransitionName = name)) ||
(element.style.viewTransitionName ||= name);
});
}
export function setOldPageViewTransitionNames(selector, prefix) {
addEventListener('pageswap', () => setSelectedViewTransitionNames(selector, prefix));
}
// The pagereveal event fires very early. Thus scripts using this code should load inside the <head>
export function setNewPageViewTransitionNames(selector, prefix) {
addEventListener('pagereveal', () => setSelectedViewTransitionNames(selector, prefix));
}
function shuffle(array) {
for (let i = array.length - 1; i > 0; --i) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}