# 2021不一样的css新玩法(上)

783 阅读9分钟

long time no see, 又是好久没有写博客了, 之前的博客全是写一写原理啊, 框架思想啊, 算法啥的, 今天趁着周末, 咱们来点不一样的, 可能很多同学对于css的感知还停留在style is just style的阶段, 不过这几年css虽然迭代的慢, 但是也算是出来了不少比较nice的新特性, 我们一起来看看喽

在本文中我们主要探究以下属性:

  • filter
  • box-reflect
  • css variable
  • aspect-ratio
  • gap

filter

顾名思义, filter的意思就是滤镜, 滤镜咱都知道吧, 整个自拍 可不得加个滤镜美颜啥的, 什么黑白, 彩色, 复古滤镜, 滤镜其实就是控制一个元素的颜色产生偏移(说通俗点就是给你换色), 知道了含义, 我们大概来看看滤镜应该怎么玩

这玩意可以填的属性比较多, 我们一个一个来玩玩

filter: blur(): 模糊

当我们给一个元素添加filter: blur(模糊像素值)的时候, 他会给该元素实现一个高斯模糊的效果:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>css new features</title>
  <style>

    body {
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      background-color: #f5f5f5;
    }

    /* filter: blur */
    .container {
      width: 400px;
      height: 400px;
      background-color: #000;
      /* 关键就在于这行代码, 如果没有这行代码, 我们都知道页面会是一个什么效果 */
      filter: blur(10px); 
    }

  </style>
</head>
<body>
  <div class="container""></div>
</body>
</html>

当添加了filter: blur以后, 效果如下

2021-07-24-08-53-54.png

blur的参数值越高, 模糊程度越高, 参数值越低, 模糊程度越低

功能知道了, 那么我们可以用这个属性来干嘛呢? 最常见的就是企业级开发中的毛玻璃效果

<!-- 还是和之前一样的代码, 我们给container加点背景 -->
...

<style>
  .container {
    width: 380px;
    height: 210px;
    background-color: #000;
    filter: blur(2px);
    background-image: url(https://game.gtimg.cn/images/lol/act/img/skin/big134005.jpg);
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    text-align: center;
    color: #fff;
    line-height: 210px;
    font-size: 24px;
    font-weight: bold;
    font-family: Consolas;
  }
</style>

...

效果如下了, 以前我们处理毛玻璃效果真的是js逻辑搞一大堆, 或者还需要UI出图, 现在一个css属性给你搞定, 灰常nice

2021-07-24-09-04-08.png

不过其实我们还会发现一个问题, 当我们在container元素中加入文字的时候, 文字也一起被模糊掉了, 如果我们不想要这样的效果应该怎么办呢? 聪明的同学就知道用afterbefore进行定位覆盖, 这个是没毛病的, 但是css官方也想到了这个点, 所以他们推出了backdrop-filter属性, 顾名思义: 这个属性是叫做backdrop, 那么他是给指定backdrop-filter的元素加上一层滤镜, 使得元素后面的东西产生模糊, 所以他不能加给自身, filter可以可以模糊自身, backdrop-filter不行, 要注意这一点哈


...

<style>
  .container {
    ...
    /* filter: blur(2px); 我们注释掉这行代码 */
    ...
  }

  /* 同时给filter-box进行backdrop过滤 */
  .filter-box {
    width: 100%;
    height: 100%;
    backdrop-filter: blur(6px);    
  }
</style>
...

<!-- 所以我们必须得改一下结构 -->
<div class="container"">
  <div class="filter-box">hello, world</div>
</div>
...

这时候效果如下, 是不是非常的nice

2021-07-24-09-17-36.png

filter: grayscale(): 灰度

我们拍了一张照片以后, 有时可能会把照片变成黑白照片有木有, 而filter: grayscale正是帮我们做这个事情的, 我们来看看使用场景吧

grayscale()的参数值是一个百分比, 百分比越高, 灰度值越高(灰度值越高, 就越接近黑白)

用这个属性, 我们可以实现一些hover过后黑白照片变彩色的效果, 类似于给一个激活状态, 来看看例子

实现效果如下, 真的没毛病

grayscale.gif

filter: hue-rotate(): 色相变更

什么叫色相变更哈, 我们不是专业的UI人员, 粗浅理解的话你可以认为色相变更就是色值的变换(比如从红色到绿色就是色相变更), 过去, 我们如果要实现一个盒子, 无限的变换颜色, 我们需要在JS逻辑中采集生成随机的rgb色值吧, 现在, 有了hue-rotate, 我们翻身做主人了, 我们来看看色相变更的强大之处

...
<style>
  .container {
    width: 200px;
    height: 200px;
    background-color: green;
    animation: hueRotateAnimate 6s linear infinite;      
  }

  @keyframes hueRotateAnimate {
    0% {
      filter: hue-rotate(0)
    }

    100% {
      filter: hue-rotate(360deg);
    }
  }
</style>
...
<div class="container">
</div>

实现效果如下, 是不是很炫酷, 很nice, 不知道大家有没有看到过华为的充电效果, 其实那个就是用filter: hue-rotate实现的

hueRotate.gif

filter: contrast(): 对比度

对比度这个东西我相信大家也不陌生了吧, 对比度越高, 色彩差异越大, 对比度越低, 色彩差异越小, 当对比度为0的时候 我们可以直接来看个效果

...
<style>
  .container {
    width: 200px;
    height: 400px;
    position: relative;
    display: flex;
  }
  
  .container .building-bg,
  .container .real-building {
    width: 100%;
    height: 100%;
    background-position: center;
    background-repeat: no-repeat;
    background-size: contain;
    background-image: url(./oldBuilding .png);
  }

  .building-bg {
    /* 我们将左侧的div 背景的对比度设置为0 */
    filter: contrast(0);
  }
</style>

<div class="container"">
    <div class="building-bg"></div>
    <div class="real-building"></div>
</div>

...

上面的代码, 本质上两个div都是同一个背景, 但是出来的效果如下

2021-07-24-10-25-57.png

那么利用这个效果我们可以干啥呢, 不知道大家有没有做过那种不规则图形的边框, 真的非常难搞, 多数情况下, 这个都是由UI出图, 我们直接用, 但是有了这个属性, 我们自己也可以搞定, 我们改一下上面的样式代码

.container {
  width: 200px;
  height: 400px;
  position: relative;
}

.container .building-bg,
.container .real-building {
  width: 100%;
  height: 100%;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  background-image: url(./古楼\ .png);
}

.building-bg {
  filter: contrast(0);
}

/* 主要增加这一段 */
.container  .real-building {
  position: absolute;
  top: 4%;
  left: 4%;
  width: 92%;
  height: 92%;
}

出来的效果如下, 是不是非常的nice

2021-07-24-10-30-19.png

filter: url(): svg滤镜(这个用的少, 但是非常强大 )

我们知道svg是一个非常强大的东西, 这哥们可以帮我们绘制很多图形乃至icon出来, filter: url的强大之处就是他可以让你将svg作为滤镜值, 这里你可能还不太理解, 前提是这要基于你对svg的认知足够多的前提下

下面我要做一个例子, 这个例子需要一些前置知识, 你可以去学习学习, 或者你可以直接越过filter: url这一块, 因为他平时用的实在是不多, 但是因为他非常非常强大, 可以做出非常炫酷的特效, 所以笔者还是拿出来说说

前置知识如下:

  1. svg标签: developer.mozilla.org/en-US/docs/…
  2. FeTubulence 标签: developer.mozilla.org/en-US/docs/…
  3. filter标签: developer.mozilla.org/en-US/docs/…
  4. animate标签: developer.mozilla.org/en-US/docs/…

我们来写一段代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Ring of Fire</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background-color: #000;
    }

    .circle {
      position: relative;
      width: 600px;
      height: 600px;
      filter: url(#wavy) blur(1px);
    }

    .circle::before {
      content: "";
      position: absolute;
      top: 100px;
      left: 100px;
      bottom: 100px;
      right: 100px;
      border: 20px solid #fff;
      border-radius: 50%;
      box-shadow: 0 0 50px #0f0,
      0 0 50px #0f0 inset;
      animation: changeColorAnimate 5s linear infinite;
    }

    .circle::after {
      content: "";
      position: absolute;
      top: 100px;
      left: 100px;
      bottom: 100px;
      right: 100px;
      border: 20px solid #fff;
      border-radius: 50%;
      box-shadow: 0 0 50px #fff,
      0 0 50px #fff inset;
    }

    svg {
      width: 0;
      height: 0;

    }

    @keyframes changeColorAnimate {
      0% {
        filter: hue-rotate(0deg)
      }
      100% {
        filter: hue-rotate(360deg)
      }
    }

  </style>
</head>
<body>
  <div class="circle"></div>
  <div class="circle"></div>
  <svg>
    <filter id="wavy">
      <feTurbulence x="0" y="0" baseFrequency="0.009" numOctaves="5" seed="2">
        <animate attributeName="baseFrequency" dur="60s" values="0.02;0.005;0.02" repeatCount="indefinite" />
      </feTurbulence>
      <feDisplacementMap in="SourceGraphic" scale="30" />
    </filter>
  </svg>
</body>
</html>

他实现的效果如下, 兄弟, 这个你说炫不炫酷 牛不牛逼, 这个也是结合了一部分我们上面的知识

circle.gif


其实整个filter的属性还有很多, 我这里只举例了一些最常用而且帮助最大的, 实际上我们知道对于滤镜还有很多操作, 比如曝光度, 饱和度, 透明度等等操作, 而这些操作filter都给我们集成了不同的属性值, 大家感兴趣的可以去mdn自行查阅

filter mdn链接: developer.mozilla.org/en-US/docs/…

同时, 我们必须关注的就是filter的一个兼容情况

2021-07-24-11-51-47.png

box-reflect

不知道大家有没接触过倒影的需求, 在一些需要比较视觉效果丰富的产品上, 可能会看到, 而box-reflect就是来帮我们实现这个功能的, 虽然他还是一个experiment阶段的东西, 不过这也是一个未来功能的趋势, 而且实在是方便, 目前w3c官方也纳入了草案

该属性是接收几个参数, 类似于border:

  • 第一个参数: 投影的方向(above, below, left, right四个值可选)
  • 第二个参数: 投影和实物之间的距离
  • 第三个参数: 这个参数不得了, 他控制了投影是什么东西(这意味着投影可不一定投出来的是实物的倒影啊, 他完全可以是其他的什么图片)
...

<style>
  .container {
    width: 200px;
    height: 200px;
    background-color: #000;
    -webkit-box-reflect: below 1px linear-gradient(transparent, #000);
    /* 第三个参数你也可以填url(你要显示的路径), 非常的nice和好用 */
  }
</style>

<div class="container"">
</div>

...

页面效果如下, 我们就可以看到一个倒影了

2021-07-24-15-05-38.png

那利用这个玩意, 我们再结合一些其他的东西, 能干啥呢, 这能干的事情还真的挺多的, 就看你乐不乐意去想了, 一些类似于Netflix官网的字体闪烁倒影效果不就信手拈来了

...
 <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      background-color: rgb(94, 29, 29);
      min-height: 100vh;
    }
    
    .container {
      font-family: Menlo;
      font-size: 6rem;
      color: rgb(138, 15, 15);
      width: 100%;
      text-align: center;
      letter-spacing: 15px;
      -webkit-box-reflect: below 1px linear-gradient(transparent, #000);
      line-height: 5rem;
      animation: animate 5s linear infinite;
      font-weight: bold;
    }


    @keyframes animate {
      0%, 18%, 20%, 50.1%, 60%, 65.1%, 80%, 90.1%, 92% {
        color:rgb(138, 15, 15);
        text-shadow: none;
      }
      18.1%, 20.1%, 30%, 50%, 60.1%, 65%, 80.1%, 92.1%, 90%, 100% {
        color: #fff;
        text-shadow: 0 0 10px firebrick,
        0 0 20px firebrick,
        0 0 40px firebrick,
        0 0 80px firebrick,
        0 0 160px firebrick;
      }
    }

  </style>
...
  <div class="container">Netflix</div>
...
...

实现的效果如下, 是不是很炫酷, 很nice

boxReflect.gif

box-reflect的一个支持情况如下

2021-07-24-11-52-48.png

css variable

lesssass乃至post-css我相信大家都是不陌生的, 他们主要解决了一个什么问题呢? 最重要的就是一个css的写法还有css变量的问题, 可能还有的会用到css module的情况, 而w3c官方也早就注意到了这些, 这不, css 变量就来了

...
<style>
  ...
  :root {
    /* 在:root下声明的变量, 全局皆可通用 */
    --container-bg-color: #07252d;
  }

  .container {
    /* 而在container内部声明的变量, 只能在container容器中使用, 这个没毛病吧 */
    --container-bg-color:red;
    width: 200px;
    height: 200px;
    /* 我们使用var语法来使用变量 */
    background-color: var(--container-bg-color);
  }
</style>

这个时候一个红色的div盒子就产生了, 我们知道像类似于less, sass等都需要构建工具的配合, 而且他们是使用的js对文件的io处理, 这会比较大的影响开发时的热更新性能, 如果css官方能够支持这玩意, 那其实真的挺香的

2021-07-24-15-28-09.png

同样, css variable的支持程度如下

2021-07-24-11-50-54.png

aspect-ratio

我们来设想一个场景, 假设产品经理和你说, 我们现在做的是移动端, 这个父级的容器我不管外界屏幕尺寸怎么变化, 你始终要给我保持16:9的一个显示区域

这个需求到手虽然谈不上裂开哈, 但是如果我们使用的是Vue或者React的话, 我们就需要使用computed或者useMemo去实时计算一些值了, 这样其实就增加了我们一定的逻辑复杂度, aspect-ratio就是为了这种场景存在的

<style>
  ...
  .container {
    /* 当我们使用aspect-ratio的时候就是希望他根据尺寸的变化而变化, 所以我们不用给固定的高 */
    width: 100%;
    background-image: url(https://game.gtimg.cn/images/lol/act/img/skin/big51010.jpg);
    background-repeat: no-repeat;
    background-size: cover;
    aspect-ratio: 16 / 9;
  }
  ...
</style>

...
<div class="container"></div>

这个时候我们就会发现, 无论我们的视口尺寸怎么变化, 整个container区域始终保持这一个16:9的尺寸, 这个是你过去用css怎么都办不到的

aspect-ratio.gif

同时我们还可以对aspect-ratio进行媒体查询, 我们可以选择在不同的视口比例下我们要怎么去显示内容, 在没有aspect-ratio的时候, 我们做的对视口的响应式一般都是对具体的px范围区间做约束, 而有了aspect-ratio的媒体查询以后, 针对移动端我们的约束就更加的精准了

/* 针对不同的视口尺寸, 使用最精准的样式控制 */
@media (aspect-ratio: 16/9) {
  div {
    background: #9af;
  }
}

@media (aspect-ratio: 4/3) {
  div {
    background: #9ff;
  }
}

aspect-ratio的兼容性如下

image.png

gap

大家日常肯定遇到过这样的需求: 有很多元素, 我们需要将它们规规整整的排齐, 类似于淘宝的商品, 然后产品又希望他们之间都来点间距, 过去我们实现这样的效果, 无非就是flex布局加flex子元素的margin, 其实这样会有很多问题, 需要我们写更多的代码去弥补掉这些问题, 现在我们可以使用gap

<style>
  .container {
      display: flex;
      width: 400px;
      height: 600px;
      border-radius: 8px;
      border: 2px solid gray;
      justify-content: space-between;
      /* gap给间距 */
      gap: 2%;
      padding: 1%;
      box-sizing: border-box;
      /* 
        注意哈, 当flex容器里有多行的时候, 你的align-items就不会生效了
        必须使用align-content
       */
      align-content: baseline;
      flex-wrap: wrap;
    }

    .container div {
      width: 48%;
      aspect-ratio: 2 / 1;
      background-repeat: no-repeat;
      background-position: center; 
      background-size: contain;     
    }

    .Vayne {
      background-image: url(https://game.gtimg.cn/images/lol/act/img/skin/big67014.jpg);
    }

    .Cassiopeia {
      background-image: url(https://game.gtimg.cn/images/lol/act/img/skin/big69003.jpg);
    }

    .Caitlyn {
      background-image: url(https://game.gtimg.cn/images/lol/act/img/skin/big51010.jpg);
    }

    .Azir {
      background-image: url(https://game.gtimg.cn/images/lol/act/img/skin/big268005.jpg);
    }

    .Oriana {
      background-image: url(https://game.gtimg.cn/images/lol/act/img/skin/big61007.jpg);
    }

</style>

...
  <div class="container"">
    <div class="Cassiopeia"></div>
    <div class="Caitlyn"></div>
    <div class="Vayne"></div>
    <div class="Azir"></div>
    <div class="Oriana"></div>
  </div>
...

实现的效果如下, 几乎可以说不费吹灰之力就实现了一个自适应的流式布局

2021-07-24-17-09-30.png

gap的一个兼容性如下

image.png

写在最后, 其实css的新特性还是有很多的, 今天就写到这儿, 笔者要去扫地去了, 希望这些东西能够对大家的日常开发产生帮助和少走弯路, 话说回来, 我们自身也需要不断的对新事物保持探索和好奇, 新的东西总归是更好的, 保持好奇心和学习能力也是不被行业淘汰的基本素质, 笔者也会在下周分享中篇, 下下周分享下篇~ 一起加油