Web技巧(15)
上一期咱们围绕着Web动画展开,其中有的动画对部分用户群体会造成不良的反应,会引起癫痫。为此,为了避免这种现象出现,可以使用条件CSS中@media
中的prefers-reduced-motion
条件来做处理。除此之外,prefers-reduced-motion
在<picture>
元素中还有一些小技巧,可以帮我们做一些其他有意义的事情。这一期,就从这个特性开始讲起。
prefers-reduced-motion
和 <picture>
的结合
<picture>
是HTML5的一个元素,可以使用该元素来较好的实现响应式图片。另外,HTML的<img>
元素的srcset
和sizes
属性也能实现类似的效果。
如果你对Web中图像相关的知识感兴趣的话,可以花点时间阅读下面相关的文章:
回到prefers-reduced-motion
和<picture>
上来。
当用户系统中开启减弱动态效果之后,我们就可以通过媒体查询@media
中prefers-reduced-motion
来减弱动态效果,即元素直接不启用任何动效:
/* 开启 减弱动态效果 的设备会禁用 aniName动画 */
@media screen and (prefers-reduced-motion) {
/ * 禁用不必要的动画 * /
.element {
animation: none;
}
}
上面对于CSS控制的动效可以得到很好的降级处理,但Web页面会启用一些.gif
动态图片。那么对于使用媒体查询就不好处理。不过,值得庆幸的是,HTML5的<picture>
可以基于source
中的media
值来对动态图做一些降级处理。当然,在media
中也同样需要基于prefers-reduced-motion
的取值。比如下面这个示例:
<picture>
<source srcset="static.png" media="(prefers-reduced-motion: reduce)" />
<img srcset="animated.gif" alt="animated image" />
</picture>
就上面的示例而言,在<picture>
中的<img>
元素引入了默认的动态图片,然后在<source>
中引入降级处理的图片。一旦用户系统启用了“减弱动态效果”:
Web终端(比如Safari浏览器)就会启用降级图片static.png
。
虽然.gif
动图可以让视觉效果动起来(Web动效中模式之一),但.gif
文件的引用在性能上是有所制约,特别是在移动终端上。不过,除了.gif
动图之外,还可以使用.mp4
这样的视频文件也能让Web视觉效果动起来,而且视频要比.gif
图性能好得多。事实上,有不少同学开始将.gif
文件转换成视频文件,然后再将其运用到Web中。
那么我们就可以在<picture>
的<source>
中引入一个.mp4
文件。只不过这样我们就需要三个源媒体文件:
- 当开启“减弱动态效果”,会启用非动态图片,比如
static.png
- 同样在
<img>
中引用一个动态图片做为默认资源,比如animated.gif
- 如果识别
<picture>
中引用的资源,而且未开启“减弱动态效果”,则会启用视频文件来替代.gif
图,比如animated.mp4
例如:
<picture>
<source srcset="static.png" media="(prefers-reduced-motion: reduce)" />
<source srcset="animated.mp4" type="video/mp4" />
<img srcset="animated.gif" alt="animated image" />
</picture>
就上例,在不同的浏览器会加载不同的源媒体:
虽然Firefox也支持
<picture>
元素,但它似乎不能正常工作,加载的依旧是animated.gif
文件。具体原因不名,有可能是Firefox对<source>
的支持还有一定的缺陷。
有关于这方面更详细的介绍可以阅读@Chris Coyier的《Reduced Motion Picture Technique, Take Two》一文。
Flexbox 和 Grid 容器中的伪元素
熟悉Flexbox和Grid布局的同学都应该知道。如果在元素上显式的设置display
的值为flex/inline-flex
或grid/inline-grid
就会创建Flex容器或Grid容器,同时就创建了FFC(Flexbox Formatting Context)或GFC(Grid Formatting Context)。那么Flex容器或Grid容器的子元素就自动成为Flex项目或Grid项目。同时相应的Flex项目属性或Grid项目属性就可以运用到这些元素之上。
在Flex容器或Grid容器上可以使用伪元素::before
或::after
。运用于容器的伪元素在很大程度上就像一个子元素,因此也自动会变成Flex项目或Grid项目。
Flex容器或Grid容器中的伪元素::before
或::after
看上去像一个子元素。不过有一个棘手的问题,除了用于创建它的选择器之外,没有其他选择器可以选中它。
ul::before {
content: 'x';
display: inline-flex;
justify-content: center;
align-items: center;
min-height: 10vh;
background: #f36;
margin: 5px;
color: #fff;
}
或许你会想到,既然伪元素看上去像一个子元素,那么结构性选择器,比如:nth-child()
或:nth-last-child()
可以选中。事实是不可以的,如果伪元素和子元素完全一样,将会影响这些选择器。
ul > :nth-child(1) {
background: #f90;
border:2px solid #09f;
}
ul > :nth-last-child(2) {
background: #09f;
border:2px solid #f90;
}
另外一个问题就是,在JavaScript中不能像选择常规子元素那样选择伪元素。比如,document.querySelector('.flex::before')
将返回一个null
。如果你想在JavaScript选中伪元素以及想看看它的样式规则,可以使用CSSOM中的相关特性来获取到:
const styles = window.getComputedStyle(
document.querySelector('.flex'), '::before'
)
console.log(styles.content) // ❯ "x"
console.log(styles.color) // ❯ rgb(255, 255, 255)
console.log(styles.getPropertyValue('color')) // ❯ rgb(255, 255, 255)
基于CSS自定义属性和em
单位的吶应式布局
响应式布局中会根据不同的终端屏幕设计不同的字号、间距等。在以前的相关教程介绍了精准流体布局的方法:
其实他们的原理非常简单,利用视窗单位vw
等和calc()
函数来计算font-size
或者padding
等属性的值。
@guerriero_se在的新博文中介绍了另一种实现响应式布局的方法,即基于CSS自定义属性和em
单位。
主要分为两部分,一部分是对font-size
、line-height
相关的设计(有关于文字排版);另一部分就是有关于间距的设计。
响应式排版
为了控制文本相关的缩放,定义了两个自定义属性:--text-base-size
和--text-scale-ratio
。第一个是body
元素的font-size
;第二个是用于缩放的比例。而且--text-base-size
的默认值是1em
。
:root {
// body font size
--text-base-size: 1em;
// type scale
--text-scale-ratio: 1.2;
--text-xs: calc((1em / var(--text-scale-ratio)) / var(--text-scale-ratio));
--text-sm: calc(var(--text-xs) * var(--text-scale-ratio));
--text-md: calc(var(--text-sm) * var(--text-scale-ratio) * var(--text-scale-ratio));
--text-lg: calc(var(--text-md) * var(--text-scale-ratio));
--text-xl: calc(var(--text-lg) * var(--text-scale-ratio));
--text-xxl: calc(var(--text-xl) * var(--text-scale-ratio));
--text-xxxl: calc(var(--text-xxl) * var(--text-scale-ratio));
// line-height
--body-line-height: 1.4;
--heading-line-height: 1.2;
// capital letters - used in combo with the lhCrop mixin
--font-primary-capital-letter: 1;
}
注意,在定义每种文本大小类型的自定义属性时,使用1em
乘以--text-scale-ratio
。1em
不是基于--text-base-size
的值。你可以根据自己的需要设置--text-base-size
的值不等于1em
。由于em
单位是基于当前font-size
的大小计算的,如果我们在不同的媒体查询中更新--text-base-size
的值,那么就会更新body
的font-size
,并使用级联效应,更新所有文本大小自定义属性。这样一来,整个排版都会受到影响。
@supports(--css: variables) {
:root {
@include breakpoint(md) {
--text-base-size: 1.25em;
}
}
}
响应式间距
响应式间距和响应式排版有点类似:
:root {
--space-unit: 1em;
--space-xxxxs: calc(0.125 * var(--space-unit));
--space-xxxs: calc(0.25 * var(--space-unit));
--space-xxs: calc(0.375 * var(--space-unit));
--space-xs: calc(0.5 * var(--space-unit));
--space-sm: calc(0.75 * var(--space-unit));
--space-md: calc(1.25 * var(--space-unit));
--space-lg: calc(2 * var(--space-unit));
--space-xl: calc(3.25 * var(--space-unit));
--space-xxl: calc(5.25 * var(--space-unit));
--space-xxxl: calc(8.5 * var(--space-unit));
--space-xxxxl: calc(13.75 * var(--space-unit));
}
设置了一个自定义属性--space-unit
的值为1em
,而且模块化缩放比例是基于斐波那契数列(Fibonacci sequence)。同样的,在不同的媒体查询中,--space-unit
可以设置不同的的值:
@supports(--css: variables) {
:root {
@include breakpoint(md) {
--space-unit: 1.25em;
}
}
}
有关于更详细的代码,可以查阅读CodyHouse Framework。
图解JavaScript的map()
、filter()
和reduce()
JavaScript中有关于数组的API有很多:
其中有关于map()
、filter()
和reduce()
的几个方法,有很多非常形象的图来阐述,比如@Una Kravets的手绘图:
@JavaScript Teacher还为这几个API设计了相应的动态图:
有关于这方面更多的介绍,可以阅读:
- Map, Filter and Reduce – Animated
- An Illustrated (and Musical) Guide to Map, Reduce, and Filter Array Methods
- JavaScript’s Reduce Method Explained By Going On a Diet
- JavaScript Map() Method Explained by Going On a Hike
- JavaScript’s Filter Function Explained By Applying To College
小结
在这一期中主要围绕三个部分展开,其一介绍了prefers-reduced-motion
媒体查询条件和HTML5的<picture>
的<source>
结合在一起对动态图片做降级处理,或者说在最适合的环境加载最适合的源媒体;其二介绍了如何使用CSS自定义属性和em
单位实现响应式布局的另一种方法;其三介绍了Flexbox和Grid容器中的伪元素会生成相对应的项目,具备Flex项目、Grid项目的属性,不过他们看上去类似子元素,但结构性选择器无法运用到其身上,如果使用JavaScript来操作伪元素的样式的话,需要借助CSSOM等特性。最后用收集了不同的图来阐述JavaScript的map
、filter
和reduce
等API特性。