一次 CSS 动画异常的分析
发现问题
偶然发现一个问题,就好奇看了下,没看懂。这个需求已经上线,目前线上表现正常。所以就更加的好奇和莫名其妙。
具体问题是:页面中有动画,动画大概是:
animation: moveRight 1s ease-out forwards;
@keyframes moveRight {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(100px, 0, 0);
}
}
动画 keyframes 有多个,根据数据的不同,来进行相应的位移动画。
但是动画突然位置不对。因为是开发环境,于是我首先检查了一下数据,数据果然不对。不过和动画相关的数据似乎没问题,也就是元素的 class 是没错的。
最简试验
因为对动画也不是很精通,所以实在有点怀疑自己写的动画并不是最合理的,然后在某种情况(比如数据或者兼容)下就出现问题了。于是在项目之外又尝试写了一个简版的动画效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>transition</title>
<style>
* {
margin: 0;
padding: 0;
list-style: none;
}
body {
position: relative;
margin: 20px;
}
.list {
border: 1px solid #ccc;
height: 240px;
clear: both;
}
.item {
float: left;
border: 1px solid red;
box-sizing: border-box;
margin: 0 -26px;
width: 332px;
height: 240px;
line-height: 100px;
text-align: center;
}
.star {
position: absolute;
left: 0;
top: 240px;
width: 100px;
height: 100px;
border: 1px solid red;
}
.star-1 {
left: 0;
top: 240px;
animation: moveRight1 1s ease-out forwards;
}
.star-2 {
left: 100px;
top: 340px;
animation: moveRight2 1s ease-out forwards;
}
@keyframes moveRight1 {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(100px, 0, 0);
}
}
@keyframes moveRight2 {
from {
transform: translate3d(100px, 0, 0);
}
to {
transform: translate3d(280px, 0, 0);
}
}
</style>
</head>
<body>
<ul class="list">
<li class="item item1">box1</li>
<li class="item item2">box2</li>
<li class="item item3">box3</li>
<li class="item item4">box4</li>
<li class="item item5">box5</li>
</ul>
<div class="star"></div>
</body>
</html>
结果一切正常,看起来动画没问题。
animate.css 冲突?
后来突然想起来,最近项目中同事新增了 animate.css,是不是冲突导致的?因为是突然出现的问题,所以应该是页面本身不需要修改才对;问题是有其他的地方关联导致的。很有可能是类名的冲突。
于是删除了 main.ts 引入的 animate CSS 文件,但是问题依然存在。难道是还有其他的影响,卸载依赖,依然不行。
尝试删除依赖缓存,重新 install,依然不行。
再次分析问题
问题是动画的 translate3D 偏移不对,如果我修改它能不能生效呢。
换成 translateX,无效。
修改偏移量能够生效,但是问题是为什么突然不对呢,想要改成正确的偏移量,这个修改也没有规律。
和之前对比以及依赖缓存?
但是既然之前的版本有正确的,切换到之前的版本再次查看效果,是可以的,但是这里要清除依赖缓存,重新安装依赖。
想到之前因为 package lock 文件冲突,进行了一次删除锁文件。以及对 Vite 的升级,清除依赖的缓存操作。这些操作之后,项目的依赖其实进行了一次全面的升级。
然后果然发现一个插件的版本变化:
"postcss-px-to-viewport-8-plugin": "^1.2.2",
1.2.2 -> 1.2.5
版本由 1.2.2 变成了 1.2.5
解决问题
发现版本变化,尝试恢复版本,果然就修复了问题。
也立即明白了问题所在,由于项目中使用 pxtoviewport 进行单位转换进行适配,而插件在升级后,由于某种原因,动画的单位未能进行转换。
探迹根源
但是源码的更新,到底哪里影响了?
1.2.5 的提交 github.com/lkxian888/p… 似乎只是一些格式化的修改,无关紧要
1.2.3 的更新: github.com/lkxian888/p… 看起来是关于 mediaQuery 的修改。好像也没关系。
而且看 github.com/lkxian888/p… 1.2.3 是正常的,说明只是 1.2.5 的修改导致的。
但实在未看出问题。
看原始仓库,也找到了类似的问题: github.com/evrone/post…
所以不是版本的问题,而是配置的问题。
postcss 插件原理
开始看 postcss-px-to-viewport-8-plugin
的源码实现,其实是根据 postcss-px-to-viewport
进行修改的,对于它解决的问题,我们都很熟悉。
PostCSS 用于转换 CSS 代码。它本身并不会对 CSS 做任何处理,而是通过插件来实现各种功能,比如自动添加浏览器前缀、转换单位、优化代码等。postcss-px-to-viewport
插件的作用是将 CSS 中的 px
单位转换为 vw
(视口宽度单位)或其他指定单位。
PostCSS 的原理:
- 加载配置:PostCSS 读取插件配置,并加载其中配置的插件和选项。
- 解析 CSS:PostCSS 解析输入的 CSS 文件,生成一棵 AST。
- 调用插件:PostCSS 按顺序调用每个插件,对 AST 进行处理。
postcss-px-to-viewport
插件会遍历所有的规则和声明,找到并转换符合条件的px
单位。 - 生成 CSS:PostCSS 将处理后的 AST 转换回 CSS 代码。
插件的关键步骤:
css.walkRules
:遍历 CSS 规则。rule.walkDecls
:遍历每个规则中的声明。validateParams
:检查规则的父节点参数,确定是否处理该规则。createPxReplace
:创建用于替换px
单位的函数。decl.value.replace
:执行单位转换。
其中 validateParams
就是和 配置项 mediaQuery 相关,插件根据 medieQuery 的配置决定对 CSS 进行单位转换,其中的条件是 rule.parent.params,就是规则存在 parent 的情况。
这里配置项虽然叫 mediaQuery,但是 @keyframes 的规则也一样受影响。而且默认值为 false,也就是默认不会进行转换。
mediaQuery 是否合理?
根据以上分析,为什么更新导致问题之所以不好排查,在于参数的误导性。当然也是因为对 PostCSS 的原理不了解,不然大概也能猜到。
我们知道 atrule 还有很多类型,所以是否能增加配置参数 keyframes?这样可以对 keyframes 单独控制。代码修改添加中。
总结
开发中,对于前端依赖的处理一定要小心,可以关注以下几点:
- 依赖锁文件的冲突处理
- 依赖缓存的问题
- 每一次的依赖升级要小心
- 关注依赖的版本变化
- 尝试回退版本
- 查看版本的更新内容
对于问题,也不要满足于解决,要搞清楚问题的根源,直到自己满意为止,这样既能提高自己的能力,也能真正的避免类似的问题。
而问题永远不会突然出现,做好自测,一定跟最近的变化有关。
参考
转载自:https://juejin.cn/post/7376889083211857961