diff --git a/.changeset/gold-oranges-switch.md b/.changeset/gold-oranges-switch.md new file mode 100644 index 0000000..650b1e5 --- /dev/null +++ b/.changeset/gold-oranges-switch.md @@ -0,0 +1,5 @@ +--- +'@vtbag/turn-signal': minor +--- + +link-types: assign view transition types directly to the links that trigger navigation! diff --git a/README.md b/README.md index b5cd2bc..bf2c401 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,31 @@ The @vtbag website can be found at https://vtbag.dev/ ## !!! News !!! -Transition types and direction attributes are now also determined on the old page! +The Turn-Signal learned a new trick! With the new `link-types` script, you can assign view transition types directly to the links that trigger navigation. This means you can create different view transition animations for the same page, based on the link that takes you there! Simply use `data-vtbag-link-types` attributes to set transition types and use them in you CSS to guard your animation rules. + +But that's not all: `link-types` lets you specify different transition types for history back navigation too! + +Check out the [image viewer tech demo](https://vtbag.dev/viewwe-demo/) for a hands-on example and the docs at [vtbag.dev/tools/turn-signal](https://vtbag.dev/tools/turn-signal#link-types). + +Happy animating! 🎉 + -For details see the [CHANGELOG](https://github.com/vtbag/turn-signal/blob/main/CHANGELOG.md) and the [documentation](https://vtbag.dev/tools/turn-signal/). + +### What happened before? + +> Transition types and direction attributes are now also determined on the old page! + +For details see the [CHANGELOG](https://github.com/vtbag/turn-signal/blob/main/CHANGELOG.md) and the [documentation](https://vtbag.dev/tools/turn-signal/). + ## What is it? -Turn-Signal is a lightweight script that enhances cross-document view transitions by detecting the direction of browser navigation. It enables developers to create smooth, responsive transitions that adjust based on forward or backward navigation, delivering a more intuitive user experience. +Turn-Signal is a lightweight script that enhances *browser-native* *cross-document* **view transitions** by detecting the **direction** of browser **navigation**. It enables developers to create smooth, responsive transitions that adjust based on forward or backward navigation, delivering a more intuitive user experience. When your pages slide to the left on forward navigation, let them slide to the right when the users goes back in the browser's history. -The script automatically detects the traversal direction and sets `backward`, `neither` or `forward` view transition types accordingly. You can also instruct the script to set data attributes on the `` element. +The script automatically detects the traversal direction and sets `backward`, `same` or `forward` view transition types accordingly. You can also instruct the script to set data attributes on the `` element. If your site has the concept of a _previous_ and _next_ page, the Turn-Signal can automatically generate directional transitions for you. diff --git a/bin/bundle b/bin/bundle index 6880bef..759a37e 100755 --- a/bin/bundle +++ b/bin/bundle @@ -6,6 +6,6 @@ else OPTS="--minify" (cd lib && rm *.js.map) >> /dev/null 2>&1 fi -npx esbuild src/index.ts src/forced-traversal.ts --bundle $OPTS --target=ESnext --outdir=lib +npx esbuild src/index.ts src/forced-traversal.ts src/link-types.ts --bundle $OPTS --target=ESnext --outdir=lib tsc diff --git a/package.json b/package.json index 6c10d9a..9d6b8a7 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,14 @@ "lib/index.js", "lib/index.d.ts", "lib/forced-traversal.js", - "lib/forced-traversal.d.ts" + "lib/forced-traversal.d.ts", + "lib/link-types.js", + "lib/link-types.d.ts" ], "exports": { ".": "./lib/index.js", - "./forced-traversal": "./lib/forced-traversal.js" + "./forced-traversal": "./lib/forced-traversal.js", + "./link-types": "./lib/link-types.js" }, "scripts": { "dev": "bin/bundle dev", diff --git a/src/dom-view-transisions-level2.d.ts b/src/dom-view-transisions-level2.d.ts index 3a37e1d..1fcbf4e 100644 --- a/src/dom-view-transisions-level2.d.ts +++ b/src/dom-view-transisions-level2.d.ts @@ -20,8 +20,8 @@ declare global { } interface NavigationActivation { - entry: NavigationEntry; - from: NavigationEntry; + entry: NavigationHistoryEntry; + from: NavigationHistoryEntry; navigationType: string; } interface AnimationEffect { diff --git a/src/index.ts b/src/index.ts index 704e49b..3d67460 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,8 @@ const currentScript = document.currentScript; function direction(e: PageSwapEvent | PageRevealEvent) { - const activation = e.type === 'pageswap' ? (e as PageSwapEvent).activation : navigation?.activation; + const activation = + e.type === 'pageswap' ? (e as PageSwapEvent).activation : navigation?.activation; if (e.viewTransition && activation) { const pages = allPages(); let hereIdx = 1, @@ -11,9 +12,9 @@ function direction(e: PageSwapEvent | PageRevealEvent) { fromIdx = activation.from?.index; } else { if (pages.length) { - const index = (url: string) => pages.indexOf(new URL(url).pathname); - hereIdx = index(activation.entry?.url); - fromIdx = index(activation.from?.url); + const index = (url: string) => pages.indexOf(new URL(url, location.href).pathname); + hereIdx = index(activation.entry?.url ?? ''); + fromIdx = index(activation.from?.url ?? ''); if (hereIdx === -1 || fromIdx === -1) { hereIdx = 1; fromIdx = 0; @@ -75,4 +76,3 @@ function allPages() { 'onpagereveal' in window && addEventListener('pagereveal', direction); 'onpageswap' in window && addEventListener('pageswap', direction); - diff --git a/src/link-types.ts b/src/link-types.ts new file mode 100644 index 0000000..bae50b9 --- /dev/null +++ b/src/link-types.ts @@ -0,0 +1,45 @@ +import { direction, activation } from './navigation'; + +let anchor: Element | null = null; + +addEventListener('DOMContentLoaded', () => { + document.addEventListener('click', (event) => { + const target = event.composedPath()[0]; + anchor = target instanceof Element ? target.closest('a, area') : null; + anchor = anchor?.hasAttribute('data-vtbag-link-types') ? anchor : null; + }); +}); + +addEventListener('pageswap', (event) => { + if (!event.viewTransition) return; + const lastAnchor = anchor; + anchor = null; + let types = history.state?.vtbagTypes ?? ''; + if (activation(event).navigationType === 'traverse' && direction(event) === 'backward') return; + if (lastAnchor) { + types = lastAnchor.getAttribute('data-vtbag-link-types') ?? ''; + console.log('s-types :>> ', types); + setViewTransitionTypes(types); + } + sessionStorage?.setItem('vtbag-link-types', types.split(/\s*\/\s*/, 1)[0]); + sessionStorage + ?.getItem('vtbag-link-types') + ?.split(/\s+/) + .forEach((type) => event.viewTransition?.types?.add(type)); + console.log('x', ...event.viewTransition?.types); +}); + +addEventListener('pagereveal', (event) => { + if (!event.viewTransition) return; + let types = sessionStorage?.getItem('vtbag-link-types') ?? ''; + if (activation().navigationType === 'traverse' && direction() === 'backward') { + types = history.state?.vtbagTypes ?? ''; + types.includes('/') && (types = types.split(/\s*\/\s*/, 2)[1]); + } + types.split(/\s+/).forEach((type) => event.viewTransition?.types?.add(type)); + console.log('r-types :>> ', [...event.viewTransition?.types]); +}); + +export function setViewTransitionTypes(types: string) { + history.replaceState({ ...history.state, vtbagTypes: types }, ''); +} diff --git a/src/navigation.ts b/src/navigation.ts new file mode 100644 index 0000000..3d2507e --- /dev/null +++ b/src/navigation.ts @@ -0,0 +1,11 @@ +export const activation = (e?: PageSwapEvent | PageRevealEvent) => + e && 'activation' in e ? (e as PageSwapEvent).activation : window.navigation.activation; + +export function direction(e?: PageSwapEvent | PageRevealEvent) { + const act = activation(e); + return act?.from.index > act?.entry.index + ? 'backward' + : act.entry.index === act.from.index + ? 'same' + : 'forward'; +}