Skip to content

Instantly share code, notes, and snippets.

@guxuerui
Created March 19, 2025 10:40
Show Gist options
  • Save guxuerui/637616d5a24d7e7c08f3d4242a4a7e28 to your computer and use it in GitHub Desktop.
Save guxuerui/637616d5a24d7e7c08f3d4242a4a7e28 to your computer and use it in GitHub Desktop.
在Svelte项目中,使用Css View Transition API实现切换主题颜色及动画效果
<script lang="ts">
import { onMount, tick } from "svelte";
$: currentTheme = "";
function setTheme() {
if (currentTheme === "light" || currentTheme === "auto") {
localStorage.setItem("color-schema", "dark");
document.documentElement.classList.add("dark");
} else {
localStorage.setItem("color-schema", "light");
document.documentElement.classList.remove("dark");
}
}
function toggleTheme(event?: MouseEvent) {
const isAppearanceTransition =
// @ts-expect-error experimental API
document?.startViewTransition &&
!window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!isAppearanceTransition) {
setTheme();
loadTheme();
return;
}
const x = event.clientX;
const y = event.clientY;
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
);
// @ts-expect-error: Transition API
const transition = document.startViewTransition(async () => {
setTheme();
loadTheme();
await tick();
});
transition.ready.then(() => {
console.log("currentTheme: ", currentTheme);
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
];
document.documentElement.animate(
{
clipPath:
currentTheme === "dark" ? [...clipPath].reverse() : clipPath,
},
{
duration: 500,
easing: "ease-out",
pseudoElement:
currentTheme === "dark"
? "::view-transition-old(root)"
: "::view-transition-new(root)",
}
);
});
}
function loadTheme() {
currentTheme = localStorage.getItem("color-schema") || "auto";
}
onMount(() => {
loadTheme();
});
</script>
<button
class="border-0 bg-transparent icon-btn px-0 !outline-none c-gray-800 hover:c-black dark:text-white"
on:click={toggleTheme}
>
{#if currentTheme === "light" || currentTheme === "auto"}
<div class="scale-180" i-carbon-sun />
{:else}
<div class="scale-180" i-carbon-moon />
{/if}
</button>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment