Vou implementar uma mudança de tema semelhante para meu site como parte do meu estudo. Consegui implementar uma mudança suave de tema na forma de um círculo. Mas não entendo como fazer a mudança de tema na forma de uma faixa caótica, se você tiver as habilidades ou conhecimento, por favor me diga. Anexei um código compactado para um lançamento compacto da mudança que obtive.
const container = document.querySelector(".cards");
for (let i = 1; i <= 6; i++) {
const card = document.createElement("div");
card.className = "card";
const title = document.createElement("h2");
title.textContent = `Title${i}`;
const paragraph = document.createElement("p");
paragraph.textContent = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
card.appendChild(title);
card.appendChild(paragraph);
container.appendChild(card);
}
document.addEventListener("DOMContentLoaded", () => {
const btn = document.querySelector(".toggle-button");
const isDarkMode = () => document.documentElement.getAttribute("data-theme") === "dark";
const setDarkMode = (enabled) => {
document.documentElement.setAttribute("data-theme", enabled ? "dark" : "light");
};
btn.addEventListener("click", async () => {
if (!document.startViewTransition || window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
return setDarkMode(!isDarkMode());
}
await document.startViewTransition(() => {
setDarkMode(!isDarkMode());
}).ready;
const {top, left, width, height} = btn.getBoundingClientRect();
const right = window.innerWidth - left;
const bottom = window.innerHeight - top;
const maxRadius = Math.hypot(Math.max(left, right), Math.max(top, bottom));
document.documentElement.animate(
{
clipPath: [
`circle(0px at ${left + width / 2}px ${top + height / 2}px)`,
`circle(${maxRadius}px at ${left + width / 2}px ${top + height / 2}px)`,
],
},
{
duration: 1500,
easing: "ease-in",
pseudoElement: "::view-transition-new(root)",
}
);
});
setDarkMode(isDarkMode());
});
:root {
color-scheme: dark;
--theme-text: white;
--theme-bg: black;
--bg-card: #29292d;
--border-card: #3b3b41;
}
[data-theme="light"] {
color-scheme: light;
--theme-text: black;
--theme-bg: white;
--bg-card: #dedfe1;
--border-card: #cacbcc;
}
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
body {
background: var(--theme-bg);
}
.header {
display: flex;
justify-content: end;
margin-bottom: 10px;
}
.cards {
display: grid;
gap: 16px;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.card {
border: 1px solid var(--border-card);
color: var(--theme-text);
background: var(--bg-card);
border-radius: 14px;
padding: 6px 10px;
}
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="header">
<button class="toggle-button">Change theme</button>
</div>
<div class="cards"></div>
</body>
</html>
O clip-path é limitado a formas e polígonos simples, não tenho certeza se entendi o que você quer dizer com "faixas caóticas", mas parece que você pode conseguir isso usando stroke-dasharray/stroke-dashoffset do svg. Talvez tente usar svg mask em vez de clip-path.
Editar:
Fiz uma pequena demonstração para sua demonstração, meu svg não é perfeito, mas presumo que você ajustará seu próprio svg. Fiz svg com animação svg (você pode aprender mais aqui https://www.w3schools.com/graphics/svg_animation.asp ) e o converti para data-url usando https://yoksel.github.io/url-encoder/ .
Meu svg:
Alterações no css:
Nunca trabalhei com a API ViewTranstion e não consegui encontrar nenhuma outra opção para forçar alterações em pseudoelementos além de usar animação. Você também precisará gerar um novo id para cada data-url svg, caso contrário, a animação pode disparar apenas uma vez. Alterações no script: