I'm going to implement a similar theme change for my site as part of my study.I managed to implement a smooth change of theme in the form of a circle. But I do not understand how to make the theme change in the form of a chaotic strip, if you have the skills or knowledge, please tell me. I have attached a compressed code for a compact launch of the shift that I got.
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>
I'm going to implement a similar theme change for my site as part of my study.I managed to implement a smooth change of theme in the form of a circle. But I do not understand how to make the theme change in the form of a chaotic strip, if you have the skills or knowledge, please tell me. I have attached a compressed code for a compact launch of the shift that I got.
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>
Share
Improve this question
asked Feb 18 at 0:40
LionLiveLionLive
213 bronze badges
1 Answer
Reset to default 0Clip-path is limited to simple shapes and polygons, I'm not sure that I get what you mean by "chaotic strips" but it sounds like you could achieve this by using svg's stroke-dasharray/stroke-dashoffset. Maybe try using svg mask instead of clip-path.
Edit:
I made a small demonstration to your demo, my svg is not perfect but i assume you will tweak your own svg. I made svg with svg animation (you can learn more here https://www.w3schools/graphics/svg_animation.asp) and converted it to data-url using https://yoksel.github.io/url-encoder/.
My svg:
<svg id="svg" viewBox="0 0 82.56 36.82" width="100%" height="100%" preserveAspectRatio="none">
<path fill="none" stroke="black" stroke-dasharray="200" stroke-dashoffset="-200" stroke-width="0%" d="M0,36.82L35.09,1.73c3.42-3.42,9.28-1,9.28,3.84v18.13c0,4.84,5.85,7.27,9.28,3.84L79.67,1.52c.86-.86,1.86-1.35,2.89-1.52">
<animate
attributeName="stroke-dashoffset"
begin="0s"
dur="1s"
from="-200"
to="0"
repeatCount="1"
fill="freeze"
/>
<animate
attributeName="stroke-width"
begin="0s"
dur="1s"
from="0%"
to="50%"
repeatCount="1"
fill="freeze"
/>
</path>
</svg>
Changes to css:
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
--mask:none;
mix-blend-mode: normal;
}
::view-transition-new(root) {
mask-image: var(--mask);
mask-size: 100% 100%;
mask-repeat: no-repeat;
-webkit-mask-image: var(--mask);
-webkit-mask-size: 100% 100%;
-webkit-mask-repeat: no-repeat;
}
I have never worked with ViewTranstion API and couldn't find any other option to force changes on pseudoelements than to use animation. You also will need to generate new id for each data-url svg otherwise animation might fire only once. Changes to script:
btn.addEventListener("click", async () => {
if (!document.startViewTransition || window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
return setDarkMode(!isDarkMode());
}
await document.startViewTransition(() => {
setDarkMode(!isDarkMode());
}).ready;
const makeId = () => {
const chars = 'abcdefghijklmnoupqrstuvwxyzABCDEFGHIJKLMNOUPQRSTUVWXYZ1234567890'.split('');
let retStr = '';
for(let i = 0; i<10; i++) {
retStr+=`${chars[Math.floor(Math.random()*chars.length)]}`;
}
return retStr;
}
let id = makeId();
document.documentElement.animate(
{
"--mask": [
`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3./2000/svg' id='${id}' viewBox='0 0 82.56 36.82' width='100%25' height='100%25' preserveAspectRatio='none'%3E%3Cpath fill='none' stroke='black' stroke-dasharray='200' stroke-dashoffset='-200' stroke-width='0%25' d='M0,36.82L35.09,1.73c3.42-3.42,9.28-1,9.28,3.84v18.13c0,4.84,5.85,7.27,9.28,3.84L79.67,1.52c.86-.86,1.86-1.35,2.89-1.52'%3E%3Canimate attributeName='stroke-dashoffset' begin='0s' dur='1s' from='-200' to='0' repeatCount='1'%0Afill='freeze' /%3E%3Canimate attributeName='stroke-width' begin='0s' dur='1s' from='0%25' to='50%25' repeatCount='1' fill='freeze' /%3E%3C/path%3E%3C/svg%3E")`,
`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3./2000/svg' id='${id}' viewBox='0 0 82.56 36.82' width='100%25' height='100%25' preserveAspectRatio='none'%3E%3Cpath fill='none' stroke='black' stroke-dasharray='200' stroke-dashoffset='-200' stroke-width='0%25' d='M0,36.82L35.09,1.73c3.42-3.42,9.28-1,9.28,3.84v18.13c0,4.84,5.85,7.27,9.28,3.84L79.67,1.52c.86-.86,1.86-1.35,2.89-1.52'%3E%3Canimate attributeName='stroke-dashoffset' begin='0s' dur='1s' from='-200' to='0' repeatCount='1'%0Afill='freeze' /%3E%3Canimate attributeName='stroke-width' begin='0s' dur='1s' from='0%25' to='50%25' repeatCount='1' fill='freeze' /%3E%3C/path%3E%3C/svg%3E")`,
],
},
{
duration: 1000,
easing: "ease-in",
pseudoElement: "::view-transition-new(root)",
}
);
});