You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React项目风扇动画求助:实现启停变速旋转效果

实现带平滑加速/减速的风扇旋转动画

以下是修复后的代码,可实现点击开启时平滑加速到恒定转速,点击关闭时平滑减速停止,同时解决卡顿问题:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fan</title>
    <style>
        .rotor {
            --rotation-duration: 100s; /* 初始慢速(近似静止) */
            animation: rotate var(--rotation-duration) linear infinite;
            transform-origin: center;
            will-change: transform; /* 提前告知浏览器优化动画性能 */
            transition: --rotation-duration 1s ease-in-out; /* 转速变化的过渡动画 */
        }

        .rotor.rotating {
            --rotation-duration: 0.5s; /* 最高速旋转周期 */
        }

        @keyframes rotate {
            from {
                transform: rotate(0deg);
            }
            to {
                transform: rotate(360deg);
            }
        }
    </style>
</head>
<body>
    <div>
        <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
        <g clip-path="url(#clip0_1_2)">
        <mask id="mask0_1_2" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="512">
        <path class="rotor" d="M506.679 204.398C505.262 197.512 502.307 192.599 496.887 188.122C453.569 152.322 393.494 154.729 316.661 195.34C311.647 190.326 306.22 186.088 300.137 182.438C353.091 134.931 401.362 114.986 444.946 122.601C457.796 124.846 466.695 110.122 458.733 99.7904C441.061 76.8547 420.948 58.181 396.768 42.2548C390.895 38.3877 385.333 37.0048 378.334 37.6699C322.39 42.9874 281.612 87.168 255.999 170.213C248.908 170.213 242.073 171.053 235.193 172.774C239.045 101.738 259.073 53.5023 295.278 28.0675C305.952 20.5691 301.832 3.86563 288.897 2.18738C260.182 -1.53431 232.757 -0.517742 204.397 5.32065C197.512 6.73789 192.598 9.69258 188.121 15.1131C152.322 58.4317 154.728 118.506 195.339 195.34C190.325 200.354 186.087 205.781 182.437 211.863C134.93 158.909 114.986 110.638 122.601 67.0542C124.846 54.204 110.122 45.3056 99.79 53.2664C76.8543 70.9384 58.1807 91.0511 42.2546 115.231C38.3875 121.104 37.0046 126.666 37.6697 133.666C42.9872 189.61 87.1676 230.388 170.212 256.001C170.212 263.093 171.052 269.927 172.773 276.807C101.737 272.955 53.502 252.927 28.0673 216.722C20.5691 206.048 3.86561 210.168 2.18737 223.103C-1.5343 251.818 -0.51774 279.243 5.32063 307.603C6.73786 314.489 9.69253 319.402 15.1131 323.879C58.4314 359.679 118.506 357.273 195.339 316.661C200.353 321.676 205.78 325.914 211.863 329.563C158.909 377.071 110.638 397.015 67.0539 389.4C54.2038 387.155 45.3054 401.879 53.2673 412.211C70.9393 435.146 91.0519 453.82 115.232 469.746C121.105 473.613 126.667 474.996 133.666 474.331C189.61 469.014 230.388 424.833 256.001 341.788C263.092 341.788 269.927 340.948 276.807 339.226C272.955 410.262 252.927 458.498 216.722 483.933C206.048 491.431 210.168 508.134 223.103 509.813C251.818 513.534 279.243 512.518 307.603 506.679C314.488 505.262 319.402 502.307 323.879 496.887C359.678 453.568 357.272 393.494 316.661 316.66C321.675 311.646 325.913 306.219 329.563 300.137C377.07 353.091 397.014 401.362 389.399 444.946C387.154 457.796 401.878 466.694 412.21 458.732C435.146 441.06 453.819 420.948 469.745 396.768C473.612 390.895 474.995 385.332 474.33 378.333C469.013 322.389 424.832 281.61 341.788 255.998C341.788 248.906 340.948 242.072 339.227 235.192C410.263 239.044 458.498 259.072 483.933 295.277C491.431 305.951 508.134 301.831 509.813 288.896C513.534 260.182 512.518 232.759 506.679 204.398ZM256.001 327.491C216.518 327.491 184.51 295.483 184.51 256.001C184.51 216.518 216.518 184.51 256.001 184.51C295.483 184.51 327.491 216.518 327.491 256.001C327.491 295.483 295.483 327.491 256.001 327.491Z" fill="black"/>
        </mask>
        <g mask="url(#mask0_1_2)">
        <rect width="512" height="512.002" fill="url(#paint0_linear_1_2)"/>
        </g>
        </g>
        <defs>
        <linearGradient id="paint0_linear_1_2" x1="256.683" y1="502.445" x2="512.001" y2="256.685" gradientUnits="userSpaceOnUse">
        <stop stop-color="#D9D9D9"/>
        <stop offset="1" stop-color="#9F9F9F"/>
        </linearGradient>
        <clipPath id="clip0_1_2">
        <rect width="512" height="512" fill="white"/>
        </clipPath>
        </defs>
        </svg>
    </div>
    <div>
        <button class="turn-on">Turn on</button>
        <button class="turn-off">Turn off</button>
    </div>
    <script>
        const turnOn = document.querySelector(".turn-on");
        const turnOff = document.querySelector(".turn-off");
        const rotor = document.querySelector(".rotor");

        turnOn.addEventListener("click", () => {
            rotor.classList.add("rotating");
        });

        turnOff.addEventListener("click", () => {
            rotor.classList.remove("rotating");
        });
    </script>
</body>
</html>

关键改进点说明

  1. 平滑转速过渡:通过CSS变量--rotation-duration控制旋转动画的周期,给变量添加transition属性,实现周期变化时的平滑过渡,对应转速的加速/减速效果。
  2. 性能优化:添加will-change: transform提示浏览器提前优化动画性能,同时transform-origin: center确保旋转中心正确,避免不必要的重绘重排,解决卡顿问题。
  3. 状态控制逻辑:点击开启时添加rotating类,将动画周期从100s(近似静止)切换到0.5s(高速);点击关闭时移除类,周期恢复到100s,完成减速停止。

React适配方案

在React中只需将CSS变量的控制逻辑转化为状态管理即可:

import { useState } from 'react';

function Fan() {
  const [isRotating, setIsRotating] = useState(false);

  return (
    <div>
      <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
        {/* 省略SVG内部代码,保持原rotor类不变 */}
        <path 
          className="rotor" 
          style={{ '--rotation-duration': isRotating ? '0.5s' : '100s' }}
          d="..." // 原路径数据
          fill="black"
        />
      </svg>
      <button onClick={() => setIsRotating(true)}>Turn on</button>
      <button onClick={() => setIsRotating(false)}>Turn off</button>
    </div>
  );
}

内容的提问来源于stack exchange,提问作者Jurakin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 13:30:55