工作室介绍,开工(
This commit is contained in:
+106
-5
@@ -1,5 +1,30 @@
|
||||
const themeToggle = document.querySelector('.theme-toggle');
|
||||
const themeToggles = document.querySelectorAll('.theme-toggle');
|
||||
const html = document.documentElement;
|
||||
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
const supportsViewTransition = typeof document.startViewTransition === 'function';
|
||||
const viewTransitionStyleId = 'itstudio-view-transition-style';
|
||||
let isThemeTransitioning = false;
|
||||
|
||||
function ensureViewTransitionStyles() {
|
||||
if (!supportsViewTransition || document.getElementById(viewTransitionStyleId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.id = viewTransitionStyleId;
|
||||
style.textContent = `
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
animation: none;
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
::view-transition-group(root) {
|
||||
animation: none;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
function updateLogoColor(theme) {
|
||||
const logoText = document.querySelector('#logo-text-cn');
|
||||
@@ -21,19 +46,95 @@ function getPreferredTheme() {
|
||||
function setTheme(theme) {
|
||||
html.setAttribute('data-theme', theme);
|
||||
localStorage.setItem('theme', theme);
|
||||
themeToggles.forEach((toggle) => {
|
||||
toggle.setAttribute('aria-pressed', theme === 'dark' ? 'true' : 'false');
|
||||
});
|
||||
updateLogoColor(theme);
|
||||
}
|
||||
|
||||
const initialTheme = getPreferredTheme();
|
||||
setTheme(initialTheme);
|
||||
ensureViewTransitionStyles();
|
||||
|
||||
if (themeToggle) {
|
||||
themeToggle.addEventListener('click', () => {
|
||||
function getToggleCenter(toggle) {
|
||||
if (!toggle) {
|
||||
return {
|
||||
x: window.innerWidth / 2,
|
||||
y: window.innerHeight / 2,
|
||||
};
|
||||
}
|
||||
|
||||
const rect = toggle.getBoundingClientRect();
|
||||
return {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
function getTransitionOrigin(event, toggle) {
|
||||
const hasPointerPoint = Number.isFinite(event.clientX) && Number.isFinite(event.clientY) && (event.clientX !== 0 || event.clientY !== 0);
|
||||
if (hasPointerPoint) {
|
||||
return {
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
};
|
||||
}
|
||||
|
||||
return getToggleCenter(toggle);
|
||||
}
|
||||
|
||||
function getRevealRadius(x, y) {
|
||||
const maxX = Math.max(x, window.innerWidth - x);
|
||||
const maxY = Math.max(y, window.innerHeight - y);
|
||||
return Math.hypot(maxX, maxY);
|
||||
}
|
||||
|
||||
function switchThemeWithScatter(theme, origin) {
|
||||
if (isThemeTransitioning || prefersReducedMotion.matches || !supportsViewTransition) {
|
||||
setTheme(theme);
|
||||
return;
|
||||
}
|
||||
|
||||
isThemeTransitioning = true;
|
||||
const x = origin.x;
|
||||
const y = origin.y;
|
||||
const endRadius = getRevealRadius(x, y);
|
||||
const clipFrom = `circle(0px at ${x}px ${y}px)`;
|
||||
const clipTo = `circle(${endRadius}px at ${x}px ${y}px)`;
|
||||
|
||||
const transition = document.startViewTransition(() => {
|
||||
setTheme(theme);
|
||||
});
|
||||
|
||||
transition.ready
|
||||
.then(() => {
|
||||
document.documentElement.animate(
|
||||
{
|
||||
clipPath: [clipFrom, clipTo],
|
||||
},
|
||||
{
|
||||
duration: 650,
|
||||
easing: 'cubic-bezier(0.22, 1, 0.36, 1)',
|
||||
pseudoElement: '::view-transition-new(root)',
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
transition.finished.finally(() => {
|
||||
isThemeTransitioning = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
themeToggles.forEach((themeToggle) => {
|
||||
themeToggle.addEventListener('click', (event) => {
|
||||
const currentTheme = html.getAttribute('data-theme');
|
||||
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||
setTheme(newTheme);
|
||||
const origin = getTransitionOrigin(event, themeToggle);
|
||||
switchThemeWithScatter(newTheme, origin);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||
if (!localStorage.getItem('theme')) {
|
||||
|
||||
Reference in New Issue
Block a user