bfc ifc gfc/css绘制几何图形/ css houdini (js in css)/css高级效果

300 阅读15分钟

[TOC]

bfc ifc gfc

CSS2.1中只有BFC和IFC, CSS3中才有GFC和FFC。 FC的全称是:Formatting Contexts,即格式化上下文,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。

BFC 块级格式化上下文

BFC(Block Formatting Contexts)直译为"块级格式化上下文"。容器里面的子元素不会在布局上影响到外面的元素,反之也是如此。 如何产生BFC?

  • float的值不为none。
  • overflow的值不为visible。
  • position的值不为relative和static。
  • display的值为table-cell, table-caption, inline-block中的任何一个。

那BFC一般有什么用呢? 比如常见的多栏布局,独立的块级元素等。

BFC原理(渲染规则) 1.BFC区域垂直方向会发生重叠 2.BFC不会与浮动元素相互干扰,可实现清除浮动 3.BFC区域是个独立的容器,其外部元素不会对其造成干扰 4.计算BFC高度时候,浮动也会参与计算

IFC 内联格式化上下文

IFC(Inline Formatting Contexts)直译为"内联格式化上下文",IFC的line box(线框)高度由其包含行内元素中最高的实际高度计算而来(不受到竖直方向的padding/margin影响) IFC中的line box一般左右都贴紧整个IFC,但是会因为float元素而扰乱。 float元素会位于IFC与与line box之间,使得line box宽度缩短。 同个ifc下的多个line box高度会不同。 IFC中时不可能有块级元素的,当插入块级元素时(如p中插入div)会产生两个匿名块与div分隔开,即产生两个IFC,每个IFC对外表现为块级元素,与div垂直排列。

那么IFC一般有什么用呢?

  • 水平居中:当一个块要在环境中水平居中时,设置其为inline-block则会在外层产生IFC,通过text-align则可以使其水平居中。
  • 垂直居中:创建一个IFC,用其中一个元素撑开父元素的高度,然后设置其vertical-align:middle,其他行内元素则可以在此父元素下垂直居中。

GFC 网格布局格式化上下文

GFC(GridLayout Formatting Contexts)直译为"网格布局格式化上下文",当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域,我们可以通过在网格容器上定义网格行和网格列。

那么GFC有什么用呢,和table又有什么区别呢? 首先同样是一个二维的表格,但GridLayout会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制。

FFC 自适应格式化上下文

FFC(Flex Formatting Contexts)直译为"自适应格式化上下文",Flex Box由伸缩容器和伸缩项目组成。通过设置元素的 display 属性为 flex 或 inline-flex 可以得到一个伸缩容器。设置为 flex 的容器被渲染为一个块级元素,而设置为 inline-flex 的容器则渲染为一个行内元素。

css绘制特殊图形

基础图形主要有:三角形、梯形、直角三角形、圆形、椭圆等,大多启发点都是源于下面的原始图形,原始图形原理演示如下,实际css都可以做出动画片: [原始图形]

CSS绘制一个QQ企鹅 椭圆原理:

效果图:

椭圆也可变异为石头等形状,演示如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>css 绘制特殊图形</title>
  <style>
    div{
      width: 100px;
      height: 100px;
      border: 30px solid;
      background-color: orange;
      border-left-color: blueviolet;
      border-right-color: brown;
      border-top-color: yellowgreen;
      border-bottom-color: teal;
      margin-top: 10px;
    }
    .三角形{
      width: 0px;
      height: 0px;
      background-color: transparent;
      border-left-color: transparent;
      border-right-color: transparent;
      border-top-color: transparent;
      border-bottom-color: teal;
    }
    .直角三角形{
      width: 0px;
      height: 0px;
      background-color: transparent;
      border-top-color: transparent;
      border-right-color: transparent;
      border-left-color: teal;
    }
    .对角切割 {
      width: 0px;
      height: 0px;
      background-color: transparent;
      border-bottom: 0;
      border-left: 0;
  }
    .直角三角形2{
      background-color: transparent;
      border-top-color: transparent;
    }
    .梯形{
      height: 0px;
      background-color: transparent;
      border-right-color: transparent;
      border-left-color: transparent;
      border-top-color: transparent;
    }
    .圆环{
      border-radius: 50%;
    }
    .圆{
      border:0;
      border-radius: 50%;
    }
    .椭圆{
      border:0;
      width: 200px;
      border-radius: 50%;
      /* border-top-left-radius: 50%;
      border-top-right-radius: 50%;
      border-bottom-left-radius: 50%;
      border-bottom-right-radius: 50%; */
    }
  </style>
</head>
<body>
  <div class="原始图形"></div>
  <div class="三角形"></div>
  <div class="直角三角形"></div>
  <div class="对角切割"></div>
  <div class="对角切割 直角三角形2"></div>
  <div class="梯形"></div>
  <div class="圆环"></div>
  <div class="圆"></div>
  <div class="椭圆"></div>
</body>
</html>

css next

css预处理器: sass,less,stylus css后处理器:postcss PostCSS 是使用 javascript 插件转换 CSS 的后处理器。PostCSS 本身不会对你的 CSS 做任何事情,你需要安装一些 plugins 才能开始工作。这不仅使其模块化,同时功能也会更强。 它的工作原理就是解析 CSS 并将其转换成一个 CSS 的节点树,这可以通过 javascript 来控制(也就是插件发挥作用)。然后返回修改后的树并保存。它与 Sass(一种预处理器)的工作原理不同,你基本上是用一种不同的语言来编译 CSS 。 案例参考: 展望未来:使用 PostCSS 和 cssnext 书写 CSS cssnext特性

css自定义函数

案例1:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>css自定义函数</title>
    <style>
        :root {
            --number-var: 1
        }

        div::before {
            counter-reset: number var(--number-var);
            content: counter(number)
        }
    </style>
</head>

<body>
    <div></div>
    <script>
      // 使用js给css变量赋值:setProperty,不会触发重排
      setTimeout(() => {
        document.documentElement.style.setProperty("--number-var", 4)
      }, 1000);
        
    </script>
</body>
</html>

css doodle

案例2: css-doodle:一个制作Web图案的组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>css doodle</title>
    <style>
        :root {
            --customUnit: 100%;
            --flag: 0;
        }

        html,
        body {
            width: var(--customUnit);
            height: var(--customUnit);
            display: flex;
            align-items: center;
            justify-content: center;
        }

        body {
            background: #0a0c27;
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/css-doodle/0.7.1/css-doodle.min.js"></script>
</head>

<body>
    <css-doodle>
		/* 将页面分成1行10列的树格 */
        :doodle {
        	@grid: 1x10 / 61.8vmax; 
		}
		/* 将每个格子按照中心排列 */
		@place-cell:center;
		/* 根据索引逐渐变大 */
        @size:calc(@index() * 10%); 
        border-radius: 50%;
        border-width: calc(@index()*1vmin);
        border-style: dashed;
		border-color: hsla(calc(20 * @index()), 70%, 68%, calc( 3 / @index() * .8));
		
		/* 添加动画 */
		/* 私有变量定义 */
		--d: @rand(20s, 40s);
        --rf: @rand(360deg);
        --rt: calc(var(--rf) + @pick(1turn, -1turn));
        animation: spin var(--d) linear infinite;
        @keyframes spin
        {
            from{
                transform: rotate(var(--rf))
            }
            to{
                transform: rotate(var(--rt))
            }
        }
    </css-doodle>
</body>

</html>

js in css: css houdini (js in css)

常用的方式为css in js,即将css代码打包到js中

浏览器在网頁的渲染过程如下: file:///private/var/folders/sm/02s6fkbd3sq130n5npc9bhrr0000gp/T/WizNote/e4802792-b496-4ea7-adb6-50662f235cb2/index_files/66936370.png

浏览器的 Render Pipeline 中,JavaScript 与 Style 两个阶段会解析 HTML 并为加载的 JS 与 CSS 建立 Render Tree,也就是所谓的 DOM 与 CSSOM。 而就现阶段的 Web 技术來看,开发者们能操作的就是通过 JS 去控制 DOM 与 CSSOM,來来影响页面的变化,但是对于接下來的 Layout、Paint 与 Composite 就几乎沒有控制权了。 为了解決上述问题,为了让 CSS 的魔力不再浏览器把持,Houdini 就诞生了!

css Houdini CSS魔术师Houdini API介绍

案例1:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <style>
        .el {
            --elUnit: 500px;
            --arcColor: #8266ff;
            height: var(--elUnit);
            width: var(--elUnit);
            background: url("../test.png");  /* 2.使用图片或者gif做Houdini降级展示(浏览器不支持时候使用) */

            /* 1.Houdini(实现了js in css,跳过重绘和重排使用GPU渲染):绘制canvas,除了不可以操作dom,其他就是canvas语法;把canvas效果直接粘贴过来就可以 */
            --background-canvas: (ctx, geom)=> {
                ctx.strokeStyle= `var(--arcColor)`;
                ctx.lineWidth=2;
                ctx.beginPath();
                ctx.arc(200, 200, 50, 0, 2*Math.PI);
                ctx.stroke();
                ctx.closePath();
            };
            /* background-canvas 为Houdini中的自定义属性 */
            background: paint(background-canvas);
        }
    </style>
    <title>Houdini实战1</title>
</head>

<body>
    需要使用http-server 启动访问才可,否则资源无法获取
    <div class="el"></div>
</body>
<script>
    CSS.paintWorklet.addModule("./arc.js");  // 这行代码是浏览器原生支持的
</script>

</html>

arc.js
// css  Houdini 
// 也可以采用类的方式注册,参考sky.js
if (typeof registerPaint !== "undefined") {
    registerPaint("background-canvas", class {
        static get inputProperties() {
          // --background-canvas 是index.html中css定义的样式变量
            return ["--background-canvas"];
        }
        paint(ctx, geom, properties) {
            eval(properties.get("--background-canvas").toString())(
                ctx, geom, properties
            );
        }
    });
}

案例2:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Houdini实战1</title>
    <style>
        body:before {
            --star-sky: 1;
            background-image: paint(yd-sky);
        }
    </style>
</head>

<body>
    <script>
        CSS.paintWorklet.addModule("./sky.js");
    </script>
</body>

</html>

sky.js
class YdSky {
    static get inputProperties() {
        return ["--star-sky"];
    }
    paint(ctx, geom, properties) {
        const starSky = properties.get("--star-sky");
        //....... ctx绘制动画
    }
}

registerPaint("yd-sky", YdSky);

新的css高级效果

自动的marign+flex 实现居中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>自动的marign</title>
    <style>
        .automargin {
            --ydUnit: 500px;
            width: var(--ydUnit);
            height: var(--ydUnit);
            border: 1px solid yellowgreen;
            /* 设置为flex,结合下面的margin可以实现多种居中效果 */
            display: flex;
        }
        .automargin div{
            margin: auto;
        }
    </style>
</head>

<body>
    <div class="automargin">
        <div>
            <img src="./assets/batman.png">
        </div>
    </div>

</body>
</html>

色相:色域角度变化改变色相

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>色相的妙用</title>
    <style>
        .btn{
            background: yellow;
            width: 100px;
            height:100px;
            /* 实现变色效果,且通过60deg的角度旋转,实现修改多种色相 */
            filter: hue-rotate(60deg)
        }
    </style>
</head>
<body>
    <div class="btn"></div>
</body>
</html>

改变色相实现流光的字体

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>色相之流光的字体</title>
    <style>
        @keyframes hue
        {
            from{
                filter: hue-rotate(0deg)
            }
            to{
                filter: hue-rotate(360deg)
            }
        }
        .yd-slogon{
            font-size: 120px;
            background-image: linear-gradient(
                to right,
                red,
                orange,
                yellow,
                green,
                cyan,
                blue,
                purple
            );
            /* 字体裁剪+填充:将渐变背景投到字的颜色上 */
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            /* 添加色相使色相循环变化 */
            animation: hue 2s linear infinite;
        }
    </style>
</head>
<body>
    <h1 class="yd-slogon">京程一灯冲击月薪3万</h1>
</body>
</html>

左右分栏的拖动效果实现

案例1:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>分栏CSS</title>
    <style>
        .resize-bar{
            border: 1px solid #bbb;
            width: 100px;
            height: 100px;
            /* resize实现大小可拖动 */
            resize: horizontal;
            overflow: scroll;
        }
        /* 修改默认拖动角的样式 */
        .resize-bar::-webkit-scrollbar{
            width: 60px;
            height:60px;
        }
    </style>
</head>
<body>
  让一个div可拖动
    <div class="resize-bar"></div>
</body>
</html>

案例2:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>可拖动大小的左右分栏布局</title>
    <style>
        :root {
            --ydUnit: 400px;
        }

        .column {
            overflow: hidden;
            border: 1px dashed yellowgreen;
        }

        .column-left {
            height: var(--ydUnit);
            position: relative;
            float: left;
        }

        .column-right {
            padding: 16px;
            box-sizing: border-box;
            overflow: hidden;
        }

        .resize-content {
            position: absolute;
            top: 0;
            right: 5px;
            bottom: 0;
            left: 0;
            padding: 16px;
            overflow-x: hidden;
        }

        .resize-bar {
            width: 200px;
            height: inherit;
            /* 支持拖动 */
            resize: horizontal;
            overflow: scroll;
            /* 指示双向重新设置大小 */
            /* cursor: ew-resize; */
            /* 通过透明隐藏拖动下标样式 */
            opacity: 0;
        }

        .resize-bar::-webkit-scrollbar {
            height: inherit;
        }
        /* 分割线 */
        .resize-line {
            position: absolute;
            right: 0;
            top: 0;
            bottom: 0;
            /* 两条线有刻在页面内的阴影视觉效果 */
            border-right: 2px solid #eee;
            border-left: 1px solid #bbb;
            /* 取消鼠标事件 */
            pointer-events: none;
        }

        .resize-bar:hover ~ .resize-line{
            border-left: 1px dashed aqua;
        }
    </style>
</head>

<body>
    <div class="column">
        <div class="column-left">
            <div class="resize-bar"></div>
            <div class="resize-line"></div>
            <div class="resize-content">左侧内容 左侧内容 左侧内容 左侧内容 左侧内容</div>
        </div>
        <div class="column-right">
            右侧内容 右侧内容 右侧内容 右侧内容 右侧内容 右侧内容 右侧内容
        </div>
    </div>
</body>
</html>

更改滚动条样式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>更改滚动条样式</title>
    <style>
        html {
            background: #100e17;
        }
        /* 滚动槽 */
        html::-webkit-scrollbar {
            width: 10px;
            height: 10px;
        }
        /* 拖动手把 */
        html::-webkit-scrollbar-thumb{
            border-radius: 10px;
            background: linear-gradient(to bottom,#ff8a00,#da1b60);
        }
        /* 滚动槽边框 */
        html::-webkit-scrollbar-track {
            background: linear-gradient(
                to right,
                #201c29,
                #201c29 1px,
                #100e17 1px,
                #100e17
                )
        }
    </style>
</head>

<body>
    <img src="./assets/scroll.png" width="700px" alt="">
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
    <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
</body>

</html>

图层遮罩裁边效果

两张原图:

最终效果:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>图层遮罩裁边效果</title>
    <style>
        .mask-image{
            width: 260px;
            height: 250px;
            -webkit-mask-image: url(./assets/star.png)
        }
    </style>
</head>
<body>
    <img src="./assets/bg.jpeg" width="800px" class="mask-image" alt="">
</body>
</html>

混合模式---汽车换肤 mix-blend-mode

原图:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>混合模式---汽车换肤</title>
    <style>
      html,
      body {
        height: 100%;
      }
      body {
        margin: 0;
      }

      img {
        height: 100%;
        width: 100%;
        object-fit: cover;
      }

      input {
        padding: 0;
        border: none;
        position: absolute;
        /* input 大小和图片一样大,浮在上一层 */
        width: 100%;
        height: 100%;
        /* 混合模式 */
        mix-blend-mode: hue;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <input type="color" value="#0000ff" />
    <img src="./assets/car.jpg" />
  </body>
</html>

混合模式+滤镜: 房间变晚上

原图:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>混合模式+滤镜</title>
    <style>
        .night{
            width: 780px;
            height: 520px;
            background: rgba(0,40,140,0.6) url(./assets/house.jpeg);
            /* 混合模式:实现房间黑夜效果 */
            background-blend-mode: darken;
            /* 滤镜:brightness 亮度 contrast 对比度 grayscale 灰度 */
            filter: brightness(80%) grayscale(20%) contrast(1.2)
        }
    </style>
</head>
<body>
    <div class="night"></div>
</body>
</html>

滑动轮播图

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>滑动轮播图</title>
    <style>
        body{
            width: 100vw;
            height: 100vh;
            overflow: hidden;
            margin: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #333;
        }
        .container{
            width: 400px;
            overflow-x: auto;
            overflow-y: hidden;
            white-space: nowrap;
            margin: auto;
            /* 在滚动容器中的执行方式  */
            scroll-snap-align: x mandatory;
        }
        .container img{
            width: 400px;
            height: 600px;
            scroll-snap-align: center;
        }
    </style>
</head>
<body>
    <div class="container">
        <img src="./assets/1.jpeg" alt="">
        <img src="./assets/2.jpeg" alt="">
        <img src="./assets/3.jpeg" alt="">
        <img src="./assets/4.jpeg" alt="">
        <img src="./assets/5.jpeg" alt="">
        <img src="./assets/6.jpeg" alt="">
        <img src="./assets/7.jpeg" alt="">
        <img src="./assets/8.jpeg" alt="">
    </div>
</body>
</html>

github代码如下: 欢迎给个小星星,谢谢🙏