This commit is contained in:
2026-04-24 22:31:23 +09:00
parent bc2d46096a
commit ebbf92e8f6
+170 -157
View File
@@ -1,3 +1,144 @@
/* View Transition */
(() => {
if (!document.startViewTransition) return;
function preloadAssets(newDoc) {
const curStyleHrefs = new Set(
[...document.head.querySelectorAll('link[rel="stylesheet"]')].map(l => l.href)
);
const curScriptSrcs = new Set(
[...document.head.querySelectorAll('script[src]')].map(s => s.src)
);
const tasks = [];
[...newDoc.head.querySelectorAll('link[rel="stylesheet"]')]
.filter(l => !curStyleHrefs.has(new URL(l.href, location.href).href))
.forEach(link => tasks.push(new Promise(resolve => {
const l = link.cloneNode(true);
l.addEventListener('load', resolve, { once: true });
l.addEventListener('error', resolve, { once: true });
document.head.appendChild(l);
})));
[...newDoc.head.querySelectorAll('script[src]')]
.filter(s => !curScriptSrcs.has(new URL(s.src, location.href).href))
.forEach(script => tasks.push(new Promise(resolve => {
const s = document.createElement('script');
[...script.attributes].forEach(a => s.setAttribute(a.name, a.value));
s.addEventListener('load', resolve, { once: true });
s.addEventListener('error', resolve, { once: true });
document.head.appendChild(s);
})));
return Promise.all(tasks);
}
function updateHead(newDoc) {
const head = document.head;
const newHead = newDoc.head;
const t = newHead.querySelector('title');
if (t) document.title = t.textContent;
const META_KEEP = new Set(['charset', 'viewport', 'color-scheme', 'theme-color']);
head.querySelectorAll('meta').forEach(m => {
const key = m.getAttribute('name') || m.getAttribute('property');
if (!key || META_KEEP.has(key)) return;
m.remove();
});
const insertRef = head.querySelector(
'link[rel="preconnect"], link[rel="stylesheet"], link[rel="manifest"], link[rel="icon"], script'
);
newHead.querySelectorAll('meta[name], meta[property]').forEach(m => {
const key = m.getAttribute('name') || m.getAttribute('property');
if (!META_KEEP.has(key)) head.insertBefore(m.cloneNode(true), insertRef);
});
const nc = newHead.querySelector('link[rel="canonical"]');
const cc = head.querySelector('link[rel="canonical"]');
if (nc && cc) cc.href = nc.href;
const newStyleHrefs = new Set(
[...newHead.querySelectorAll('link[rel="stylesheet"]')]
.map(l => new URL(l.href, location.href).href)
);
head.querySelectorAll('link[rel="stylesheet"]').forEach(l => {
if (!newStyleHrefs.has(l.href)) l.remove();
});
const newScriptSrcs = new Set(
[...newHead.querySelectorAll('script[src]')]
.map(s => new URL(s.src, location.href).href)
);
head.querySelectorAll('script[src]').forEach(s => {
if (!newScriptSrcs.has(s.src)) s.remove();
});
head.querySelectorAll('style').forEach(s => s.remove());
newHead.querySelectorAll('style').forEach(s => head.appendChild(s.cloneNode(true)));
}
document.addEventListener('click', (event) => {
const link = event.target.closest('a');
if (!link || link.hasAttribute('download')) return;
const url = new URL(link.href, location.href);
if (url.origin !== location.origin) return;
if (link.target || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
if (url.hash && url.pathname === location.pathname) {
event.preventDefault();
const target = document.querySelector(url.hash);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
history.pushState(null, '', url.hash);
}
return;
}
event.preventDefault();
document.startViewTransition(async () => {
const response = await fetch(url.href, {
headers: { 'X-Requested-With': 'view-transition' }
});
const html = await response.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
if (typeof window.__cursorCleanup === 'function') {
window.__cursorCleanup();
window.__cursorCleanup = null;
}
await preloadAssets(doc);
updateHead(doc);
for (const tag of ['header', 'main', 'footer']) {
const newEl = doc.querySelector(tag);
const curEl = document.querySelector(tag);
if (!newEl || !curEl) { location.href = url.href; return; }
curEl.innerHTML = newEl.innerHTML;
curEl.querySelectorAll('script').forEach(old => {
const s = document.createElement('script');
[...old.attributes].forEach(a => s.setAttribute(a.name, a.value));
s.textContent = old.textContent;
old.replaceWith(s);
});
}
history.pushState(null, '', url.href);
if (typeof window.__cursorReinit === 'function') {
window.__cursorReinit();
}
});
});
window.addEventListener('popstate', () => location.reload());
})();
/* Cursor */
(() => { (() => {
const textSelectors = 'p, h1, h2, h3, h4, h5, h6, span, li, label, td, th, pre, .code'; const textSelectors = 'p, h1, h2, h3, h4, h5, h6, span, li, label, td, th, pre, .code';
const linkSelectors = 'a, button, [role="button"], input[type="submit"], input[type="button"]'; const linkSelectors = 'a, button, [role="button"], input[type="submit"], input[type="button"]';
@@ -15,7 +156,7 @@
let isMouseDown = false; let isMouseDown = false;
const TOUCH_MOUSE_GUARD_MS = 800; const TOUCH_MOUSE_GUARD_MS = 800;
window.__scriptCleanup = () => { window.__cursorCleanup = () => {
if (ac) ac.abort(); if (ac) ac.abort();
if (rafId) { cancelAnimationFrame(rafId); rafId = null; } if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
document.documentElement.style.cursor = ''; document.documentElement.style.cursor = '';
@@ -128,165 +269,37 @@
}, { passive: true, signal: sig }); }, { passive: true, signal: sig });
} }
if (document.readyState === 'loading') { function reinit() {
document.addEventListener('DOMContentLoaded', init, { signal: sig }); cursor = document.getElementById('cursor');
} else { if (!cursor) return;
currentLinkEl = null;
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
cursor.classList.remove('on-link', 'on-text');
cursor.style.transform = 'translate(-50%, -50%)';
cursor.style.left = mouseX + 'px';
cursor.style.top = mouseY + 'px';
cursor.style.width = '';
cursor.style.height = '';
const el = document.elementFromPoint(mouseX, mouseY);
const newLinkEl = el ? el.closest(linkSelectors) : null;
if (newLinkEl) {
currentLinkEl = newLinkEl;
rafId = requestAnimationFrame(trackLink);
} else if (el && el.closest(textSelectors)) {
cursor.classList.add('on-text');
}
init(); init();
} }
function preloadAssets(newDoc) { window.__cursorReinit = reinit;
const curStyleHrefs = new Set( window.__cursorGetState = () => ({ mouseX, mouseY, currentLinkEl, rafId, trackLink, linkSelectors, textSelectors });
[...document.head.querySelectorAll('link[rel="stylesheet"]')].map(l => l.href)
);
const curScriptSrcs = new Set(
[...document.head.querySelectorAll('script[src]')].map(s => s.src)
);
const tasks = []; if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init, { once: true });
[...newDoc.head.querySelectorAll('link[rel="stylesheet"]')] } else {
.filter(l => !curStyleHrefs.has(new URL(l.href, location.href).href)) init();
.forEach(link => tasks.push(new Promise(resolve => {
const l = link.cloneNode(true);
l.addEventListener('load', resolve, { once: true });
l.addEventListener('error', resolve, { once: true });
document.head.appendChild(l);
})));
[...newDoc.head.querySelectorAll('script[src]')]
.filter(s => !curScriptSrcs.has(new URL(s.src, location.href).href))
.forEach(script => tasks.push(new Promise(resolve => {
const s = document.createElement('script');
[...script.attributes].forEach(a => s.setAttribute(a.name, a.value));
s.addEventListener('load', resolve, { once: true });
s.addEventListener('error', resolve, { once: true });
document.head.appendChild(s);
})));
return Promise.all(tasks);
}
function updateHead(newDoc) {
const head = document.head;
const newHead = newDoc.head;
const t = newHead.querySelector('title');
if (t) document.title = t.textContent;
const META_KEEP = new Set(['charset', 'viewport', 'color-scheme', 'theme-color']);
head.querySelectorAll('meta').forEach(m => {
const key = m.getAttribute('name') || m.getAttribute('property');
if (!key || META_KEEP.has(key)) return;
m.remove();
});
const insertRef = head.querySelector(
'link[rel="preconnect"], link[rel="stylesheet"], link[rel="manifest"], link[rel="icon"], script'
);
newHead.querySelectorAll('meta[name], meta[property]').forEach(m => {
const key = m.getAttribute('name') || m.getAttribute('property');
if (!META_KEEP.has(key)) head.insertBefore(m.cloneNode(true), insertRef);
});
const nc = newHead.querySelector('link[rel="canonical"]');
const cc = head.querySelector('link[rel="canonical"]');
if (nc && cc) cc.href = nc.href;
const newStyleHrefs = new Set(
[...newHead.querySelectorAll('link[rel="stylesheet"]')]
.map(l => new URL(l.href, location.href).href)
);
head.querySelectorAll('link[rel="stylesheet"]').forEach(l => {
if (!newStyleHrefs.has(l.href)) l.remove();
});
const newScriptSrcs = new Set(
[...newHead.querySelectorAll('script[src]')]
.map(s => new URL(s.src, location.href).href)
);
head.querySelectorAll('script[src]').forEach(s => {
if (!newScriptSrcs.has(s.src)) s.remove();
});
head.querySelectorAll('style').forEach(s => s.remove());
newHead.querySelectorAll('style').forEach(s => head.appendChild(s.cloneNode(true)));
}
if (document.startViewTransition) {
document.addEventListener('click', (event) => {
const link = event.target.closest('a');
if (!link || link.hasAttribute('download')) return;
const url = new URL(link.href, location.href);
if (url.origin !== location.origin) return;
if (link.target || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
if (url.hash && url.pathname === location.pathname) {
event.preventDefault();
const target = document.querySelector(url.hash);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
history.pushState(null, '', url.hash);
}
return;
}
event.preventDefault();
document.startViewTransition(async () => {
const response = await fetch(url.href, {
headers: { 'X-Requested-With': 'view-transition' }
});
const html = await response.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
if (typeof window.__scriptCleanup === 'function') {
window.__scriptCleanup();
window.__scriptCleanup = null;
}
await preloadAssets(doc);
updateHead(doc);
for (const tag of ['header', 'main', 'footer']) {
const newEl = doc.querySelector(tag);
const curEl = document.querySelector(tag);
if (!newEl || !curEl) { location.href = url.href; return; }
curEl.innerHTML = newEl.innerHTML;
curEl.querySelectorAll('script').forEach(old => {
const s = document.createElement('script');
[...old.attributes].forEach(a => s.setAttribute(a.name, a.value));
s.textContent = old.textContent;
old.replaceWith(s);
});
}
history.pushState(null, '', url.href);
if (cursor) {
currentLinkEl = null;
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
cursor.classList.remove('on-link', 'on-text');
cursor.style.transform = 'translate(-50%, -50%)';
cursor.style.left = mouseX + 'px';
cursor.style.top = mouseY + 'px';
cursor.style.width = '';
cursor.style.height = '';
const el = document.elementFromPoint(mouseX, mouseY);
const newLinkEl = el ? el.closest(linkSelectors) : null;
if (newLinkEl) {
currentLinkEl = newLinkEl;
rafId = requestAnimationFrame(trackLink);
} else if (el && el.closest(textSelectors)) {
cursor.classList.add('on-text');
}
}
init();
});
});
window.addEventListener('popstate', () => location.reload());
} }
})(); })();