Web技巧(15)

上一期咱们围绕着Web动画展开,其中有的动画对部分用户群体会造成不良的反应,会引起 癫痫 。为此,为了避免这种现象出现,可以使用条件CSS中 @media 中的 prefers-reduced-motion 条件来做处理。除此之外, prefers-reduced-motion 元素中还有一些小技巧,可以帮我们做一些其他有意义的事情。这一期,就从这个特性开始讲起。

prefers-reduced-motion 的结合

是HTML5的一个元素 ,可以使用该元素来较好的实现响应式图片。另外,HTML的 元素的 srcsetsizes 属性也能实现类似的效果。

如果你对Web中图像相关的知识感兴趣的话,可以花点时间阅读下面相关的文章:

回到 prefers-reduced-motion 上来。

当用户系统中开启 减弱动态效果 之后,我们就可以通过媒体查询 @mediaprefers-reduced-motion 来减弱动态效果,即 元素直接不启用任何动效

/* 开启 减弱动态效果 的设备会禁用 aniName动画 */ 
@media screen and (prefers-reduced-motion) { 
    / * 禁用不必要的动画 * / 
    .element { 
        animation: none; 
    } 
}

上面对于CSS控制的动效可以得到很好的降级处理,但Web页面会启用一些 .gif 动态图片。那么对于使用媒体查询就不好处理。不过,值得庆幸的是,HTML5的 可以基于 source 中的 media 值来对动态图做一些降级处理。当然,在 media 中也同样需要基于 prefers-reduced-motion 的取值。比如下面这个示例:


    
    animated image

就上面的示例而言,在 中的 元素引入了默认的动态图片,然后在 中引入降级处理的图片。一旦用户系统启用了“减弱动态效果”:

Web终端(比如Safari浏览器)就会启用降级图片 static.png

虽然 .gif 动图可以让视觉效果动起来(Web动效中模式之一),但 .gif 文件的引用在性能上是有所制约,特别是在移动终端上。不过,除了 .gif 动图之外,还可以使用 .mp4 这样的视频文件也能让Web视觉效果动起来,而且视频要比 .gif 图性能好得多。事实上,有不少同学开始 .gif 文件转换成视频文件 ,然后再将其运用到Web中。

那么我们就可以在 中引入一个 .mp4 文件。只不过这样我们就需要三个源媒体文件:

  • 当开启“减弱动态效果”,会启用非动态图片,比如 static.png
  • 同样在 中引用一个动态图片做为默认资源,比如 animated.gif
  • 如果识别 中引用的资源,而且未开启“减弱动态效果”,则会启用视频文件来替代 .gif 图,比如 animated.mp4

例如:


    
    
    animated image

就上例,在不同的浏览器会加载不同的源媒体:

虽然Firefox也支持 元素,但它似乎不能正常工作,加载的依旧是 animated.gif 文件。具体原因不名,有可能是Firefox对 的支持还有一定的缺陷。

有关于这方面更详细的介绍可以阅读@Chris Coyier的《 Reduced Motion Picture Technique, Take Two 》一文。

Flexbox 和 Grid 容器中的伪元素

熟悉 Flexbox 和Grid布局的同学都应该知道。 如果在元素上显式的设置 display 的值为 flex/inline-flexgrid/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-sizeline-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-ratio1em 不是基于 --text-base-size 的值。你可以根据自己的需要设置 --text-base-size 的值不等于 1em 。由于 em 单位是基于当前 font-size 的大小计算的,如果我们在不同的媒体查询中更新 --text-base-size 的值,那么就会更新 bodyfont-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设计了相应的动态图

有关于这方面更多的介绍,可以阅读:

小结

在这一期中主要围绕三个部分展开,其一介绍了 prefers-reduced-motion 媒体查询条件和HTML5的 结合在一起对动态图片做降级处理,或者说在最适合的环境加载最适合的源媒体;其二介绍了如何使用CSS自定义属性和 em 单位实现响应式布局的另一种方法;其三介绍了Flexbox和Grid容器中的伪元素会生成相对应的项目,具备Flex项目、Grid项目的属性,不过他们看上去类似子元素,但结构性选择器无法运用到其身上,如果使用JavaScript来操作伪元素的样式的话,需要借助CSSOM等特性。最后用收集了不同的图来阐述JavaScript的 mapfilterreduce 等API特性。