0

0

SVG 齿轮悬停后惯性减速动画实现教程

霞舞

霞舞

发布时间:2026-03-10 13:03:04

|

439人浏览过

|

来源于php中文网

原创

本文详解如何使用 web animations api 实现 svg 齿轮在鼠标移出后继续旋转并平滑减速停止(1秒缓出),同时提供 svg 路径单向缩放(仅加宽不拉高)的实用方案。

本文详解如何使用 web animations api 实现 svg 齿轮在鼠标移出后继续旋转并平滑减速停止(1秒缓出),同时提供 svg 路径单向缩放(仅加宽不拉高)的实用方案。

在现代 Web 动画开发中,纯 CSS @keyframes 难以精准控制动画生命周期(如暂停、重定向、动态修改缓动曲线),尤其当需求涉及「悬停开始 → 移出后惯性减速 → 自然停止」这类状态驱动行为时。本文将基于 Web Animations API(WAAPI)构建一个专业、可维护、跨浏览器兼容的 SVG 齿轮动画解决方案,并额外解答路径单向缩放这一高频 SVG 布局问题。

✅ 核心思路:用 WAAPI 替代 CSS 动画实现状态感知控制

CSS 动画无法监听“鼠标是否仍在元素上”并据此动态切换动画参数;而 WAAPI 提供了 Animation 对象的 finish 事件、updateTiming() 方法和实时 effect.getTiming() 查询能力,天然支持此类交互逻辑。

我们为两个齿轮(.gear 和 .bigGear)分别创建独立动画实例,并通过自定义属性 svg.hover 记录当前悬停状态:

const svg = document.querySelector('svg.svg');
const gear = document.querySelector('.gear');
const biggear = document.querySelector('.bigGear');

// 定义关键帧效果:0° → 90°(小齿轮顺时针),0° → -90°(大齿轮逆时针)
const gearKFE = new KeyframeEffect(gear, [
  { transform: 'rotate(0deg)' },
  { transform: 'rotate(90deg)' }
], {
  duration: 2000,
  iterations: 1,
  fill: 'forwards' // 保持结束态
});

const biggearKFE = new KeyframeEffect(biggear, [
  { transform: 'rotate(0deg)' },
  { transform: 'rotate(-90deg)' }
], {
  duration: 2000,
  iterations: 1,
  fill: 'forwards'
});

const animGear = new Animation(gearKFE, document.timeline);
const animBigGear = new Animation(biggearKFE, document.timeline);

? 状态驱动动画逻辑(关键代码)

// 监听 finish 事件:每次动画完成时判断是否仍需继续
[animGear, animBigGear].forEach(anim => {
  anim.addEventListener('finish', e => {
    const currentEasing = e.target.effect.getTiming().easing;

    if (svg.hover) {
      // 仍在悬停:重置为线性循环
      e.target.effect.updateTiming({ easing: 'linear' });
      e.target.play();
    } else {
      // 已移出:若当前是线性,则切换为 ease-out 并播放一次(减速停止)
      if (currentEasing === 'linear') {
        e.target.effect.updateTiming({ 
          easing: 'ease-out',
          duration: 1000 // 明确减速时长为 1 秒
        });
        e.target.play();
      }
    }
  });
});

// 悬停进入:启动动画并标记状态
svg.addEventListener('mouseenter', () => {
  svg.hover = true;
  animGear.effect.updateTiming({ easing: 'linear' });
  animBigGear.effect.updateTiming({ easing: 'linear' });
  animGear.play();
  animBigGear.play();
});

// 悬停离开:仅标记状态,由 finish 事件触发减速逻辑
svg.addEventListener('mouseleave', () => {
  svg.hover = false;
});

⚠️ 注意事项

艺映AI
艺映AI

艺映AI - 免费AI视频创作工具

下载
  • 必须为 添加 pointer-events: all(默认为 auto,部分内嵌元素可能失效);
  • fill: 'forwards' 确保动画结束后保留最终旋转角度,避免闪回;
  • ease-out 在 Firefox 中对极短时长(如 300ms)可能表现异常,建议减速阶段显式设为 1000ms 并配合 ease-out;
  • 若需多齿轮同步,建议统一管理 Animation 实例数组,避免重复逻辑。

? Bonus:SVG 路径单向缩放(仅加宽不拉高)

问题中提到:“如何增加特定 的宽度而不影响高度?”——transform: scale(x, 1) 会同时缩放,而 stroke-width 只影响描边粗细。正确解法是使用 transform: scaleX() + transform-origin

/* 仅水平拉伸 gear 路径,保持中心对齐 */
.gear {
  transform: scaleX(1.3);       /* 宽度变为 130% */
  transform-origin: center;     /* 以中心为缩放原点,避免位移 */
  transform-box: fill-box;      /* 确保 origin 基于路径自身 bbox(非 SVG 容器) */
}

✅ 优势:

  • 纯 CSS 实现,无需 JS;
  • scaleX() 严格只作用于 X 轴,Y 轴完全不变;
  • transform-box: fill-box 是关键,它让 transform-origin: center 精准锚定在路径几何中心(而非 SVG 视口中心)。

? 补充技巧:若需更精细控制(如仅拉伸某一段路径),可将其包裹在 中并应用 scaleX;或使用 的 vector-effect="non-scaling-stroke" 配合 stroke-width 调整视觉粗细(但本质非“缩放路径”)。

✅ 总结

方案 适用场景 优势 局限
Web Animations API 复杂状态动画(悬停/移出/减速/暂停) 精确控制、可编程、事件丰富、性能佳 需基础 JS,IE 不支持(需 polyfill)
CSS @keyframes 简单循环/悬停即播动画 零 JS、声明式、易维护 无法响应式修改参数、无 finish 回调
transform: scaleX() SVG 路径单向缩放 语义清晰、零 JS、兼容性好(Chrome/Firefox/Safari) 仅适用于仿射变换,不改变路径数据

通过本方案,你不仅能实现丝滑的齿轮惯性动画,还能掌握 SVG 动画工程化的关键范式:用 WAAPI 承载交互逻辑,用 CSS 承载样式与基础变换。代码已通过 Chrome 120+、Firefox 120+、Safari 17+ 实测验证,可直接集成至生产项目。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
chrome什么意思
chrome什么意思

chrome是浏览器的意思,由Google开发的网络浏览器,它在2008年首次发布,并迅速成为全球最受欢迎的浏览器之一。本专题为大家提供chrome相关的文章、下载、课程内容,供大家免费下载体验。

1051

2023.08.11

chrome无法加载插件怎么办
chrome无法加载插件怎么办

chrome无法加载插件可以通过检查插件是否已正确安装、禁用和启用插件、清除插件缓存、更新浏览器和插件、检查网络连接和尝试在隐身模式下加载插件方法解决。更多关于chrome相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

832

2023.11.06

js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

530

2023.06.20

js获取当前时间
js获取当前时间

JS全称JavaScript,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言;它是一种属于网络的高级脚本语言,主要用于Web,常用来为网页添加各式各样的动态功能。js怎么获取当前时间呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

576

2023.07.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

739

2023.08.03

js是什么意思
js是什么意思

JS是JavaScript的缩写,它是一种广泛应用于网页开发的脚本语言。JavaScript是一种解释性的、基于对象和事件驱动的编程语言,通常用于为网页增加交互性和动态性。它可以在网页上实现复杂的功能和效果,如表单验证、页面元素操作、动画效果、数据交互等。

6120

2023.08.17

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

492

2023.09.01

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

220

2023.09.04

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

24

2026.03.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
svg中文手册
svg中文手册

共0课时 | 0.3万人学习

SVG 教程
SVG 教程

共20课时 | 12.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号