`
```<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/darkreader@4.9.58/darkreader.min.js"></script>
</head>
<body>
<h1>Hello World</h1>
<!-- 切换按钮 -->
<button onclick="toggleDarkMode(event)">切换主题</button>
<script>
// Dark Reader 配置
const customFetch = (url) => {
return fetch(url, {
mode: 'no-cors',
credentials: 'include'
});
};
DarkReader.setFetchMethod(customFetch);
const darkReaderConfig = {
brightness: 100,
contrast: 100,
sepia: 10
};
// 初始化主题状态
let isDarkMode = JSON.parse(localStorage.getItem('darkMode')) || false;
// 初始化主题
if (isDarkMode) {
DarkReader.enable(darkReaderConfig);
}
// 主题切换逻辑
function toggleTheme(currentIsDarkMode) {
if (currentIsDarkMode) {
DarkReader.disable();
localStorage.setItem('darkMode', false);
} else {
DarkReader.enable(darkReaderConfig);
localStorage.setItem('darkMode', true);
}
}
function toggleDarkMode(event) {
const currentIsDarkMode = JSON.parse(localStorage.getItem('darkMode')) || false;
// 使用View Transitions API
if (!document.startViewTransition) {
// 如果不支持View Transitions API,则使用普通切换
toggleTheme(currentIsDarkMode);
return;
}
// 获取点击位置
const x = event.clientX;
const y = event.clientY;
// 计算扩散半径
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
// 开始过渡动画
const transition = document.startViewTransition(() => {
toggleTheme(currentIsDarkMode);
});
// 设置动画
transition.ready.then(() => {
if (currentIsDarkMode) {
// 黑夜 → 白天:黑色收缩效果
// 让old元素(黑色)在上层,从全屏收缩到点击位置
document.documentElement.style.setProperty('--old-z-index', '2');
document.documentElement.style.setProperty('--new-z-index', '1');
document.documentElement.animate(
{
clipPath: [
`circle(${endRadius}px at ${x}px ${y}px)`,
`circle(0px at ${x}px ${y}px)`
]
},
{
duration: 450,
easing: 'ease-in-out',
pseudoElement: '::view-transition-old(root)'
}
);
} else {
// 白天 → 黑夜:黑色扩散效果
// 让new元素(黑色)在上层,从点击位置扩散到全屏
document.documentElement.style.setProperty('--old-z-index', '1');
document.documentElement.style.setProperty('--new-z-index', '2');
document.documentElement.animate(
{
clipPath: [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`
]
},
{
duration: 450,
easing: 'ease-in-out',
pseudoElement: '::view-transition-new(root)'
}
);
}
});
}
</script>
<style>
/* 添加View Transitions相关样式 */
::view-transition-new(root),
::view-transition-old(root) {
animation: none;
mix-blend-mode: normal;
}
/* 确保主题切换时内容不会闪烁 */
::view-transition-old(root) {
z-index: var(--old-z-index, 1);
}
::view-transition-new(root) {
z-index: var(--new-z-index, 2);
}
/* 防止过渡期间的内容跳动 */
::view-transition-group(root) {
isolation: auto;
}
/* 确保动画平滑进行 */
html {
view-transition-name: root;
}
</style>
</body>
</html>