te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>javascript - How to properly implement an animated theme change? - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to properly implement an animated theme change? - Stack Overflow

programmeradmin5浏览0评论

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
Add a comment  | 

1 Answer 1

Reset to default 0

Clip-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)",
        }
    );
});
发布评论

评论列表(0)

  1. 暂无评论