给单元素艺术添加动画
序言:学习工具的最好的方法就是尝试新技术,本文通过“单元素艺术”介绍了 CSS 变量的使用以及给单元素添加动画的几种方法。通过学习作者编写的复杂的“单元素”示例,相信你对 CSS 变量以及 CSS 动画会有一个深刻的认识。
翻译:nzbin
如果你深入挖掘你的工具,你可以使用最基本的 HTML 做一些令人称奇的东西。我对 Lynn Fisher 以及其他人的“单元素艺术”(“Single Div Art”)印象深刻,所谓“单元素艺术”就是你可以使用一个单独的通用的 <div>
来创建漂亮的仙人掌、阿拉莫博物馆或者熊猫。
See the Pen #dailycssimages 01: Bear Cub by Lynn Fisher (@lynnandtonic) on CodePen.
很难解释这些艺术作品是怎么做的,实际上它们使用了背景渐变、阴影、文字阴影,并且只有一个 div 元素以及 ::before
和 ::after
伪类。在继续阅读本文之前,可以先看看 Lynn Fisher 的这篇文章 她为什么以及如何制作单元素艺术 。
很少使用单个 div
元素做动画。假如你可以变换 div
或者其中的伪类元素,那样就没问题(比如 Lynn Fisher 制作的 BB-8 机器人)。但是你无法直接改变你所创建的 div
中私有元素的 opacity
或者 transform
,因为它们并不是真正的 DOM 元素。
对于工具的学习,我很愿意尝试一些不同的、有趣的方法,否则你可能永远也学不会。因为单个 div
元素的限制,它并不适合实际的生产工作,但是可以作为锻炼技能的一次练习或挑战。本着这种精神,我们可以使用这种技术来探索自定义属性(CSS 变量)的工作原理,甚至给我们提供一种在 div
中实现动画的方法。为了说明这一点,我们将使用多个动画方法来分解下面的示例:
See the Pen Single Div Accordion (Animated with CSS Variables) by Dan Wilson (@danwilson) on CodePen.
这个手风琴(“accordion”指的是乐器,不是 UI 组件)有三个主要部分,键盘 (div
),风箱 (挤压的部分, div::before
)以及按键 (div::after
)。因为手风琴很自然地分成这些部分,我们可以在 CSS 关键帧动画中给每个部分设置 transform
。风箱的运动需要设置不同的 scaleX
值而另外两个部分则设置跟随风箱运动的 translateX
值。这样,一个简易的手风琴就诞生了。
See the Pen Single Div Accordion Breakdown: Transforms by Dan Wilson (@danwilson) on CodePen.
使用 CSS 的自定义属性组织 <div>
针对这三个大的部分添加动画要比针对其中的每一小部分更直接。给 div
内的独立的部分分组并命名非常有帮助,而自定义属性提供了原生的方式。你可以定义 -white-key
和 --black-key
以代替冗长的线性渐变断点来实现钢琴键盘。为了代替编写大量的渐变层,你可以设置一个 --tea-cup
,然后在其中设置 --tea-bag
以及 --tea-bag-position
。
手风琴的左侧样式如下:
background:
var(--shine),
var(--shine),
var(--button-key1, var(--button)),
var(--button-key2, var(--button)),
var(--button-key3, var(--button)),
var(--black-keys),
var(--white-keys),
var(--keyboard-base);
这些变量值可能有很多行(甚至一百多行),但是从概念上讲,变量使键盘层代码清晰自然。
See the Pen Single Div Accordion Breakdown: Keyboard by Dan Wilson (@danwilson) on CodePen.
虽然也可以使用 Sass 或者 Less,但是在将来自定义属性允许我们修改这些值。现在我们可以考虑修改 --button-key2
或者手风琴的装饰线 --shine
来添加动画。解决这个问题的方法有很多。
使用 CSS 关键帧给属性添加动画
第一种方法是在 CSS 的关键帧动画中改变你想要变化的部分的属性值。如果你想改变 background 中的某些部分(比如说,我们想把“shine”线条的颜色从红色改为蓝色),你可以替换 background
属性中的这些值。在前面的示例代码基础上修改如下:
div { /* using background definition from earlier */ --shine: linear-gradient(to right, transparent 29.5%, red 29.5%, red 70.5%, transparent 70.5%); --shine-blue: linear-gradient(to right, transparent 29.5%, blue 29.5%, blue 70.5%, transparent 70.5%); animation: modify-shine 2000ms infinite alternate ease-in-out; } @keyframes modify-shine { 100% { background: var(--shine-blue), /*these two replace the original --shine*/ var(--shine-blue), /* the rest of the background remains unchanged */ var(--button-key1, var(--button)), var(--button-key2, var(--button)), var(--button-key3, var(--button)), var(--black-keys), var(--white-keys), var(--keyboard-base); } }
这给了我们很多启示,尤其是给 background
添加动画 ( text-shadow
和 box-shadow
也一样)。在这个例子中会有一个从红色到蓝色的变化。
如果你的属性很长就会很难维护,但是自定义属性可以通过提取不变的部分来帮助我们减少重复的工作。我们可以进一步将不需要添加动画的部分提取出来放到新的变量中,从而产生多层次的变量:
div { --static-component: var(--button-key1, var(--button)), var(--button-key2, var(--button)), var(--button-key3, var(--button)), var(--black-keys), var(--white-keys), var(--keyboard-base); background: var(--shine), var(--shine), var(--static-component); } @keyframes modify-shine { 100% { background: var(--shine-blue), var(--shine-blue), var(--static-component); } }
手风琴中的三个音符就是通过给 text-shadow
添加动画的方法实现的。
See the Pen Single Div Accordion Breakdown: Music Notes by Dan Wilson (@danwilson) on CodePen.
在 CSS 关键帧中给自定义属性添加动画
改变状态的一个方法就是直接在 keyframes
中改变自定义属性。
@keyframes { 0% { --button1-color: var(--color-primary); } 100% { --button1-color: var(--color-secondary); } }
自定义属性无法预定义,只有使用 var(…)
才有用,因此改变值的特殊状态 flip its value at 50% 。 这是所有无动画的 CSS 属性的默认操作,所以这些值之间不会产生过渡效果。
因为我已经提到的特殊状态,这在大多数的浏览器中都不可用。目前,只有 Chrome 和 Opera 支持。
如果跨浏览器支持的话,这是改变状态最快的方法。如果你正在使用 Chrome 和 Opera 浏览,可以使用此方法给手风琴的左侧按键及右侧按钮添加动画。 举一个简单例子,这里有一个“像素艺术”的示例,使用这种方法,眼睛和眉毛将会在兼容的浏览器中移动。在其它浏览器中只显示一个静态图像。这一方法的代码量最少,但是兼容性最差。
See the Pen Pixel Art Animated with Custom Properties by Dan Wilson (@danwilson) on CodePen.
通过 JavaScript 给自定义属性添加动画
var div = document.querySelector('div'); var active = false; setInterval(function() { active = !active; div.style.setProperty('--white-key-1', 'var(--white-key-color-' + (active ? 'active' : 'default') +')') }, 1000);
对应的 CSS 如下:
div { --white-key-1: var(--white-key-color-default); --white-key-color-default: #fff; --white-key-color-active: #ddd; /* And a linear gradient that follows the following pattern */ background: linear-gradient(to right, var(-white-key-1) 5%, var(--white-key-2) 5%, var(--white-key-2) 10%, ...); }
See the Pen Single Div Piano Keys by Dan Wilson (@danwilson) on CodePen.
我们使用 JavaScript 根据 white-key-1
的状态将它的值设置为变量 white-key-color-default
或者 white-key-color-active
。
这个方法对于切换某个属性非常有用 (比如直接改变大小、位置或颜色)。手风琴右侧按钮部分用的就是这一方法 (如果关键帧方法不被支持的话可以用这个方法替换).
See the Pen Single Div Accordion Breakdown: Right by Dan Wilson (@danwilson) on CodePen.
九个按钮的 CSS 默认使用以下环形渐变,其中 --color1
是浅蓝色, --button-dim
是 1.4vmin:
--button: radial-gradient(circle,
var(--color1) var(--button-dim),
transparent var(--button-dim));
如果我想把一个按钮改变成被按下的状态,我可以在 CSS 设置一个特定的值,比如下面设置了第四个按钮:
--button4: radial-gradient(circle,
var(--button4-color, var(--color1)) var(--button4-dim, var(--button-dim)),
transparent var(--button4-dim, var(--button-dim)));
属性和上面是一样的,但是 --button-dim
and --color1
被特殊状态的按钮值替换了,并使用 var()
设置了默认值。 变量中的默认值可以使用 var(--my-specific-variable, 13px)
这种形式指定。我们可以做的更好一点,甚至使用另一个变量的值作为默认值,例如: var(--my-specific-variable, var(--my-default-variable))
。第二种形式就是实例代码中创建第四个按钮的特殊定义,它可以保持默认值相同。如果你希望某些按钮保持默认状态不变,它们可以在不同的 background-position
上使用默认的 --button
属性。
在手风琴的例子中, --button4-color
或者 --button4-dim
在 CSS 中没有明确定义。所以当加载时它们会使用 --color1
和 --button-dim
的默认值。最终使用 JS 修改它们的值并创建开关动画。
var enabled = false; setInterval(function() { enabled = !enabled; div.style.setProperty('--button4-dim', enabled ? '1.2vmin' : 'var(--button-dim)'); div.style.setProperty('--button4-color', enabled ? 'var(--color1alt)' : 'var(--color1)'); }, 1000);
这与直接在关键帧中改变自定义属性是一样的。正如我们已经讨论的, background
和 *-shadow
属性可以设置动画 (并且具有过渡效果,只是 transform
或者 opacity
的效果不佳,但是简单使用的话已经足够了)。
如果我们使用 JS 的开关方法并且在 background
上结合 CSS 的 transition
属性, 我们可以获得一个过渡状态而不是跳跃状态。
div { transition: background 2000ms ease-in-out; }
使用 requestAnimationFrame
根据每个组件的组成情况,改变属性的方法可能不行。如果你想移动某个部分,你可以试试 requestAnimationFrame
。
我最喜欢的单元素艺术之一是 Tricia Katz 制作的背包:
See the Pen Single Div Backpack by Trish Katz (@techxastrish) on CodePen.
我喜欢那个来回移动地拉链。使用一个自定义属性表示拉链的水平位移值 x
,然后通过 requestAnimationFrame
改变这个值就可以实现拉链的左右移动。
See the Pen Single Div Backpack with CSS Variables for Animation by Dan Wilson (@danwilson) on CodePen.
总结
给一个 div
添加动画的方法有很多,这些方法都可以锻炼你的技能。为了获得最广泛的支持,现在我们还不能使用纯 CSS 实现,虽然我们已经取得了很大进展。自定义属性可以更直接的修改值,即使与 JavaScript 结合(我们也可以依赖变量命名来明确我们正在更改的内容)。
See the Pen Single Div Animation Options by Dan Wilson (@danwilson) on CodePen.
当你想学习 CSS 或者 JavaScript 的新知识时,你可以尝试使用“单元素”的方式去学习。如果你想在概念上分解属性或者给复杂的值添加动画,自定义属性会给你提供一些新想法。
感谢您的阅读,如果您对我的文章感兴趣,可以关注我的博客,我是叙帝利,下篇文章再见!
开发低代码平台的必备拖拽库 https://github.com/ng-dnd/ng-dnd
低代码平台必备轻量级 GUI 库 https://github.com/acrodata/gui
适用于 Angular 的 CodeMirror 6 组件 https://github.com/acrodata/code-editor
基于 Angular Material 的中后台管理框架 https://github.com/ng-matero/ng-matero
Angular Material Extensions 扩展组件库 https://github.com/ng-matero/extensions
Unslider 轮播图插件纯 JS 实现 https://github.com/nzbin/unsliderjs
仿 Windows 照片查看器插件 https://github.com/nzbin/photoviewer
仿 Windows 照片查看器插件 jQuery 版 https://github.com/nzbin/magnify
完美替代 jQuery 的模块化 DOM 库 https://github.com/nzbin/domq
简化类名的轻量级 CSS 框架 https://github.com/nzbin/snack
与任意 UI 框架搭配使用的通用辅助类 https://github.com/nzbin/snack-helper
单元素纯 CSS 加载动画 https://github.com/nzbin/three-dots
有趣的 jQuery 卡片抽奖插件 https://github.com/nzbin/CardShow
悬疑科幻电影推荐 https://github.com/nzbin/movie-gallery
锻炼记忆力的小程序 https://github.com/nzbin/memory-stake