本文将介绍一种基于 CSS 变量技巧,通过合理使用 CSS 变量,实现 CSS 动画 @keyframes 的复用。
CSS 变量大家应该都比较熟悉了,已经不能算是新知识了,快速过一遍。
CSS 变量(CSS Variable),在之前也叫做 CSS 自定义属性,其使用方式如下:
// 声明一个变量:
:root{
--bgColor: #000;
}
这里我们借助了上面
#12、结构性伪类中的:root{ }伪类,在全局:root{ }伪类中定义了一个 CSS 变量,取名为--bgColor。
定义完了之后则是使用,假设我要设置一个 div 的背景色为黑色:
.main{
background:var(--bgColor);
}
这里,我们在需要使用之前定义变量的地方,通过 var(定义的变量名) 来调用。
OK,回归我们的正题。巧用 CSS 变量,实现动画函数复用。
假设,我们现在有多个元素,需要实现一个位移动画,从位置 A 位移到 位置 B,位置 A 相同,但是位置 B 不一样,像是这样:
正常而言,由于终点不一样,我们可能需要实现 3 个不一样的 @keyframes,像是这样:
<ul>
<li></li>
<li></li>
<li></li>
</ul>
li:nth-child(1) {
animation: move1 2s linear;
}
li:nth-child(2) {
animation: move2 2s linear;
}
li:nth-child(3) {
animation: move3 2s linear;
}
@keyframes move1 {
60%,
100% {
transform: translate(150px);
}
}
@keyframes move2 {
60%,
100% {
transform: translate(120px);
}
}
@keyframes move3 {
60%,
100% {
transform: translate(200px);
}
}
这个代码有问题吗?没有。
但是,我们可以利用 CSS 变量,让它变得更为简洁,我们改造一下 @keyframes 代码,将固定的位移值,变成一个变量:
@keyframes move {
60%,
100% {
transform: translate(var(--dis));
}
}
由于 CSS 变量是存在作用域的,我们可以通过 CSS 变量的方式,给每一个 li 定义一个不同的 --dis 变量,像是这样:
li:nth-child(1) {
--dis: 150px;
}
li:nth-child(2) {
--dis: 120px;
}
li:nth-child(3) {
--dis: 200px;
}
这样,虽然动画的结束点不一样,但是我们利用 CSS 变量,复用了同一个 @keyframes 函数:
除了通过在 <style> 内传入不同的自定义变量,我们还可以通过内联 style 属性传入自定义变量。
我们再改造一下我们的 @keyframes:
@keyframes move {
60%,
100% {
transform: translate(var(--end));
background: var(--color);
}
}
这一次,我们不需要通过 :nth-child() 去修改每一个 li 的 CSS,而是通过 HTML 元素的内联 style 属性,像是这样:
<ul>
<li style="--end: 150px; --color: red;"></li>
<li style="--end: 200px; --color: blue;"></li>
<li style="--end: 120px; --color: green;"></li>
</ul>
是的,每个 li 元素的 @keyframes 可以读取到每个 li 的 style 里面定义的不一样的 CSS 变量。
这样,我们就可以得到如下效果:
完整的代码,可以戳这里:CodePen Demo -- 巧用 CSS 变量,实现动画函数复用
下面我们实战演练一下,上一点难度。
在很久之前,我们实现过这样一个动画效果:
这个动画效果的实现方式在于:
rotateZ(360deg) 的匀速动画rotateZ(-360deg) 的匀速动画rotateX(40deg) 的动画由于父容器和子容器同时相反向旋转,所以子元素看上去其实和没有旋转是一样的。但是由于又添加了一个 rotateX(40deg) 动画,因此看上去就会有这样一种 3D 效果。
在之前,我们的代码是这样的:
<div class="reverseRotate">
<div class="rotate">
</div>
</div>
.rotate {
animation: rotate 5s linear infinite;
}
.reverseRotate {
animation: reverseRotate 5s linear infinite;
}
@keyframes rotate {
0% {
transform: rotateX(0deg) rotateZ(0deg);
}
50% {
transform: rotateX(40deg) rotateZ(180deg);
}
100% {
transform: rotateX(0deg) rotateZ(360deg);
}
}
@keyframes reverseRotate {
0% {
transform: rotateZ(0deg);
}
100% {
transform: rotateZ(-360deg);
}
}
可以看到,我们这里实现了两个动画效果:
@keyframes rotate {} 父容器的旋转动画@keyframes reverseRotate {} 子容器的旋转动画其实,这里,运用今天的技巧,我们可以把两个动画合成为一个,利用 CSS 自定义变量进行控制。改造后更简洁的 CSS 代码如下:
.rotate {
--degZ: 360deg;
--degZMiddle: 180deg;
--degX: 30deg;
animation: rotate 5s linear infinite;
}
.reverseRotate {
--degZ: -360deg;
--degZMiddle: -180deg;
--degX: 0;
animation: rotate 5s linear infinite;
}
@keyframes rotate {
0% {
transform: rotateX(0deg) rotateZ(0deg);
}
50% {
transform: rotateX(var(--degX)) rotateZ(var(--degZMiddle));
}
100% {
transform: rotateX(0deg) rotateZ(var(--degZ));
}
}
是的,我们可以得到同样的效果!
完整的代码,你可以戳这里:CodePen DEMO -- Css动画正反旋转相消
下面,我们再来尝试一个有意思的动画效果,图片旋转配合容器旋转。
在上述的基础上,如果我们把子元素,改成图片,整个效果就会有意思不少,我们稍微改变一点点代码:
<div class="reverseRotate">
<img class="rotate" src="https://picsum.photos/1000/1000?random=5" alt="">
</div>
.rotate,
.reverseRotate {
width: 60vh;
height: 60vh;
}
.reverseRotate {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 3px solid #999;
overflow: hidden;
}
.rotate {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
height: 100%;
}
.rotate {
--degZ: 360deg;
animation: rotate 5s linear infinite;
}
.reverseRotate {
--degZ: -360deg;
animation: rotate 5s linear infinite;
}
@keyframes rotate {
0% {
transform: translate(-50%, -50%) rotateZ(0deg);
}
100% {
transform: translate(-50%, -50%) rotateZ(var(--degZ));
}
}
这里,我们做了什么事情呢?
这样,我们就能看到,虽然内外两层容器同时在进行相反方向的旋转 360° 动画,但是内部的图片其实是静止不动的!
效果如下:
由于,内部图片的大小为父容器的 100%,所以在旋转过程中,父容器会有明显的无法包裹住整个图片的情况。
这个很好解决,我们只需要把图片大小调整大一点:
// ... 其它代码不变
.rotate {
width: 150%;
height: 150%;
}
.rotate {
--degZ: 360deg;
animation: rotate 5s linear infinite;
}
正常而言,对于正方形容器,内部图片设置到 141% 即可满足父容器旋转过程,可以一直包裹住图片的效果。那么,我们就能得到这样一种效果:
完整的代码,你可以戳这里:CodePen Demo -- Css动画正反旋转相消
当然,上述当只有一个容器的时候,整个动画效果还不够震撼。
如果我们可以把这个效果融合进整个布局的动画之中,整个效果又会完全不一样。
在 Rotating gallery with CSS scroll-driven animations 这篇文章中,作者提供了一种非常巧妙的思路,将 Grid 布局动画与上述动画效果巧妙的结合了起来。
首先,我们利用 Grid 布局,实现这样一个简单的网格布局结构:
<div class="container">
<div class="A">
<img src="https://picsum.photos/600/600?random=1" alt=""></div>
<div class="B">
<img src="https://picsum.photos/600/600?random=2" alt=""></div>
<div class="C">
<img src="https://picsum.photos/600/600?random=3" alt=""></div>
<div class="D">
<img src="https://picsum.photos/600/600?random=4" alt=""></div>
<div class="E">
<img src="https://picsum.photos/600/600?random=5" alt=""></div>
</div>
.container {
width: 60vmin;
height: 60vmin;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
gap: 4px;
grid-template-areas:
"E B B"
"E A C"
"D D C";
}
.container > div {
border: 3px solid #431312;
border-radius: 5px;
}
.A {
grid-area: A;
}
.B {
grid-area: B;
}
.C {
grid-area: C;
}
.D {
grid-area: D;
}
.E {
grid-area: E;
}
效果如下:
接下来,我们要做的,就是结合上面的知识点,容器滚动起来,图片反向滚动起来,配合一些 tranfrom 变换。
有了上面的铺垫,下面的新增的代码就非常好理解了:
.container > div img {
--scale: 1;
--rotation: -360deg;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 260%;
height: 260%;
object-fit: cover;
object-position: center;
}
.container,
.container > div img {
animation: 10s scale-up both ease-in-out infinite alternate;
}
@keyframes scale-up {
0% {
transform: translate(-50%, -50%) scale(var(--scale)) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) scale(1) rotate(var(--rotation));
}
}
这样,我们就得到了一个高级感拉满的网格旋转动画:
注意,这里我们依旧是通过 CSS 自定义变量,在不同元素间,复用了同一个动画 @keyframes 函数。
完整的代码,你可以戳这里:CodePen Demo -- Grid 图片旋转动画 & 使用 CSS 变量复用动画函数
好了,本文到此结束,希望本文对你有所帮助 ?
更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。
如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。
我在MiniTest::Spec和Capybara中使用以下规范:find_field('Email').must_have_css('[autofocus]')检查名为“电子邮件”的字段是否具有autofocus属性。doc说如下:has_css?(path,options={})ChecksifagivenCSSselectorisonthepageorcurrentnode.据我了解,字段“Email”是一个节点,因此调用must_have_css绝对有效!我做错了什么? 最佳答案 通过JonasNicklas得到了答案:No
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。
我有一个div,它根据表单是否正确提交而改变。我想知道是否可以检查类的特定元素?开始元素看起来像这样。如果输入不正确,添加错误类。 最佳答案 试试这个:browser.div(:id=>"myerrortest").class_name更多信息:http://watir.github.com/watir-webdriver/doc/Watir/HTMLElement.html#class_name-instance_method另一种选择是只查看具有您期望的类的div是否存在browser.div((:id=>"myerrortes