圆圆动画

o3swap-circle-animation.gif

这是一个昨天面试遇到的题,回来后花了共 9 个小时左右完成了,在线预览地址 https://io-oi.me/o3swap-circle-animation/,实现方式是纯 HTML+CSS+JavaScript,直接点击中间的大圆就可以跳转到 GitHub 源码仓库。

整个实现过程还是非常有趣的,从最开始的页面布局到最后的动画实现,特别是对 CSS 的 transform/animation/transition Property 有了更多的学习。最有趣的还是小圆环绕中圆的实现居然使用了高中学习的三角函数,当时自己脑中想了想,然后拿了笔在纸上画了画,最后 Google 重新学习了三角函数,耗时颇长但写成代码看到预期后,十分有趣!

在将图片都写进 HTML 过程中,我观察发现拿来的图片资源的命名是数字递增基本有序的,于是就把这种顺序规律模式映射到了 HTML 代码,进而也映射影响到了 CSS 和 JavaScript 代码,成为了三个文件内代码书写的基本顺序规范,也可以说成为了开发时我心中的代码律。


首先观察原网页找线索,打开开发者工具,发现了 ng-* 开头的标签(指令),猜测使用了 Angular,发现了一大堆 SVG 代码,猜测是使用了动画库;然后浏览页面发现 GitHub 地址,于是直接找到网站的源码仓库,找到对应代码观察;然后克隆仓库到本地,拿到静态图片资源;最后开始自己实现。

然后自己实现页面布局,首先是页面也即骨架也即结构化 HTML,由外到内逐渐层层嵌套,外层基架完成后我最开始写的是大圆,然后把四个被小圆环绕的中圆分别作为一组与大圆并列,最后把所有图片都写进 HTML[1];然后是布局也即外形也即样式 CSS,最先根据 HTML 的结构为相应标签添加 class Attribute,然后在 CSS 文件里面使用选择器将选择相应标签并写规则集,我最先实现的是水平垂直居中的大圆以及大图,然后是四个中圆,最后是环绕的小圆;然后是逻辑抽象也即 JavaScript 也即一些 HTML+CSS 无法实现或无法简洁实现的功能,比如四组小圆的环绕和动画的无限轮播;最后是可访问性的考虑,做了窄屏的适配。

小圆环绕的原理是,首先使用绝对位置 position: absolute 将小圆的变换轴固定在以中圆的圆心为坐标原点的直角坐标系,然后根据小圆圆心到坐标原点的连线长度[2],及该连线以 x 轴正方向顺时针旋转的圆心角,通过三角函数计算出小圆圆心的位置坐标 (x, y),最后传递给对应小圆规则集的 transformtranslate() 函数即可。[3]

最后自己实现各种动画,这一部分也是通过 CSS 来实现。整个动画可以理解为四组小圆环绕的中圆依次被激活和中间大圆的不断水波。整个动画可以分解为三种效果:小圆环绕的旋转动画、中圆的呼吸动画,大圆周围的水波动画,这三种效果的具体属性是:位置整体顺时针旋转 360° 且图片渐现渐隐、图片突现渐隐且大小呼吸变化且边框突现渐隐、5 圆波纹且半径逐渐变大且渐隐,这三种效果的时间关系是:呼吸动画是旋转动画及水波动画的 1/2。具体实现见源码,值得一提的是我使用 transition 实现图片渐现渐隐,因为这个子动画周期是独立的。另外,动画激活的原理是通过该组元素们的父级元素[4]class 属性列表的增删实现状态的改变,然后在 CSS 中通过选择器为这两种不同状态使用不同规则集。我实现这三种动画的顺序与上述叙述一致,最后实现的水波动画。

无限轮播的原理是,通过函数的递归调用,首先函数外声明 4 组的 class 名组成的 Array,然后函数外声明 index 以记录当前 Item 的索引值,然后该函数内递增或重置 index 以遍历 Array,然后选择该元素并对其 class 属性列表增删,最后调用自己,实现无限轮播。当然,由于这个函数的执行是非常快的,所以需要通过 setTimeout() 实现动画人类可见及其完成所需的时间等待。


需要说明的是,整个开发过程并不是按以上顺序串行运行,而是并行关联的多维尝试,比如 HTML 的结构化可能需要结合 CSS 考虑,我在 Google 时还将一些参考过的链接添加到 README.md。[5]

整个实现通过纯 HTML+CSS+JavaScript,依循 Web 层级自下而上方向。HTML 和 CSS 是重要的,它们能让网站的可访问性增加且拥有更为与众不同的设计,这两点可以更好地提升企业的尊重和形象。

CSS 很强大有趣,动画很有趣,期待继续学习实践更多的实例并分享给大家,共同学习!喜欢 Web 前端,渴望去尝试最新技术,渴望去学习 CS 理论,想要写出规范标准简洁简单高效的代码,希望让 Web 特别是中文网页变得更美更快体验更好!


  1. 特别感谢 GitHub Copilot,自动完成了很多重复工作,来自另一个宇宙的高维「JavaScript」。 ↩︎

  2. 即小圆环绕所在圆的半径。 ↩︎

  3. 可继续向上抽象简洁,比如根据小圆的数量直接计算每个小圆的角度,这样无需自己手动计算,但为了简单和偷懒我暂时就到这了——属于我的极简。 ↩︎

  4. 因为 CSS(Cascading Style Sheets)是级联样式表。 ↩︎

  5. 整个开发后悔的是没有在新建仓库后立即 git init,这样就能将以上全部每一过程以 commit history 方式版本记录下来。P.S. 已顺利拿到 Offer。 ↩︎