likes
comments
collection
share

现代化CSS

作者站长头像
站长
· 阅读数 11

1、W3C标准中的CSS变量

借助SASS,LESS等兼容CSS的扩展语言,我们可以很自然的写出各种样式变量,但大家有没有用原生CSS写过变量呢?大部分的编程语言自出现开始就是支持变量的,但CSS一开始就是不支持的。不过实际上CSS目前是支持了原生变量了的并且有一段时间了,只是对兼容性还是有一定要求。

css中原生变量的定义语法:

--variableName: variableValue;

变量名称(variable-name)的规范:

  • 以“--”双横杠开头,后面可以是数字[0-9]字母[a-zA-Z]、下划线`_` 短横线-这些组合,甚至是中文也行,但不能包含$、[、^、(、%等字符。

  • 大小写是敏感的

  • 定义只能在声明块{}里面

    :root{ --1: red; /有效的/ --background-color: blue; /有效的/ --font_size: 20px; /有效的/ --宽度: 100px; /有效的/ --$color: red; /无效的/ } --color: red; /无效的/

    // 在:root根元素中定义的变量是全局的,你可以在任意的HTML元素中定义(相当于局部变量)。 // 当定义多个同名变量时,变量的覆盖规则会是由CSS选择器的权重来决定的。

变量使用语法:

cssPropertyName: var(--variableName [, declarationValue]);

// var()会返回--variableName的值,declarationValue表示默认值,也就是当--variableName没有定义的时候,取declarationValue的值。

这里需要注意两点:

  • var()第二个参数不需要双引号单引号括起,括起无效。

  • var()的默认值起作用仅限于变量未定义,像上面例子中,--color-name等于20px,然后赋给color只会失效,而不会取默认值

变量之间还可以相互传递,也就是相互赋值,类似JavaScript中的变量一样:

:root{  
    --red: red;  
    --color: var(--red);
}
div{  color: var(--color);}
<div>我是红色的</div>

使用限制:

  • CSS自定义属性变量是不能用作CSS属性名称的,比如:var(--color): red;

  • 不能用作背景地址,比如:url(var(--url))

  • 由于var()后面会默认跟随一个空格,因此在其后面加单位是无效的,比如:--size:20; font-size: var(--size)px会解析成font-size: 20 px;

在使用css原生变量时,请注意检查设备兼容性:

现代化CSS

两种方式检测浏览器是否支持

// 1、使用 @supports 方法
@supports ( (--size: 0)) {  /* 支持 */}
@supports ( not (--size: 0)) { /* 不支持 */}

//2、使用JavaScript
if(window.CSS && window.CSS.supports && window.CSS.supports('--size', 0)) {  
    /* 支持 */
}else{  
    /* 不支持 */
}

使用JavaScript操作原生属性变量

// 1、获取
var rootStyles = getComputedStyle(document.documentElement);
var value = rootStyles.getPropertyValue('--variableName');
// 获取某个元素中定义的属性变量
value = element.style.getPropertyValue('--variableName');


// 2、修改
element.style.setProperty('--variableName', value);
document.documentElement.style.setProperty('--variableName', value);


//实例:
div{  color: var(--color);}
<div>我是红色的,由JavaScript设置</div>
<script>  document.documentElement.style.setProperty('--color', 'red');</script>

2、CSS预处理器与PostCSS

预处理器是处理特定格式源文件到目标css[浏览器所认识的]的处理程序。比如sass、less、stylus 。功能强大的postcss则是一种对css编译的工具,类似babel对js的处理,常见的功能如:

  • 使用下一代css语法
  • 自动补全浏览器前缀
  • 自动把px代为转换成rem/vw
  • css代码压缩等等

不同与sass/less等css预处理器可以用来扩充css语法,postcss只是一个工具,本身并不会对css进行操作,它通过插件实现功能,可以和less/sass结合使用,在webpack的配置中,可以在根目录中新建postcss的配置文件postcss.config.js,安装所需插件后进行配置

// postcss.config.js:

module.exports = {  
    plugins: [
        require('postcss-preset-env')({      
            autoprefixer: {        
                flexbox: 'no-2009',      
            },      
            stage: 3,    
        }),   
        require('postcss-px-to-viewport')({     
            viewportWidth: 750,     
            viewportHeight: 1334,     
            unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数     
            viewportUnit: 'vw',     
            selectorBlackList: ['.ignore', '.hairlines'],     
            minPixelValue: 1,       // 小于或等于`1px`不转换为视窗单位     
            mediaQuery: false       // 允许在媒体查询中转换`px`   
        })   
    ]
}

常用的postcss插件:

Autoprefixer。前缀补全,全自动的,无需多说

postcss-cssnext。使用下个版本的css语法

postcss-pxtorem。把px转换成rem

postcss-custom-properties 运行时变量

postcss-simple-vars 与scss一致的变量实现

postcss-import 实现类似sass的import

postCSS利用自身的parser可以将css代码解析为AST,再利用众多插件改写AST,最终输出改写后的css代码。postCSS的输入与输出产物都是css文件。

现代化CSS

3、CSS in JS

Houdini API

在javascript中,对于一些浏览器不支持或者还未实现的特性,总有人做出对应的polyfills,让你可以无感知的快速将新特性应用到Production环境,更别提我们还有Babel等工具帮忙转译。

但在css方面,除了制定CSS标准规范所需的时间外,各家浏览器的版本、实战进度差异更是况日持久,顶多利用PostCSS、Sass等工具来帮我们转译出浏览器能接受的css,开发者们能操作的就是通过JS去控制DOM与CSSOM来影响页面的变化,但是对于接下来的Layout、Point与Composite就几乎没有控制权了。

为了解决上述问题,为了让CSS的魔力不再被浏览器把持,Houdini就诞生了。CSS Houdini是由一群来自Mozilla,Apple,Opera,Microsoft,HP,Intel,IBM,Adobe与Google的工程师所组成的工作小组,志在建立一系列的API,让开发者能够介入浏览器的CSS engine。通过CSS Houdini API,开发者可以直接触及CSSOM,告诉浏览器如何去解析CSS,从而影响浏览器的渲染过程。

盒模型也发生了变化,现在可以拆分的非常细致,可控制元素颗粒度更小。Box tree API目的就是希望让开发者能够取得这些fragments的信息,至于取得后要如何使用,基本上应该会跟后面介绍的Parser API、Layout API与Paint API有关联。

现代化CSS

现代化CSS

Parser、Paint、Layout API:CSS词法分析器

  • CSS Parser API还没有被写入规范,它的基本思想是:允许开发者自由扩展CSS词法分析器,引入新的结构,比如新的媒体规则、新的伪类、嵌套、@extends、@apply等等。只要新的词法分析器知道如何解析这些结构,CSSOM就不会直接忽略它们,会把这些结构放到正确的地方。

  • CSS Layout API允许开发者通过CSS Layout API实现自己的布局模块,这里的"布局模块"指的是display的属性值。

  • CSS Paint API 与Layout API非常相似,他提供了一个registerPaint方法,操作方式和registerLayout方法也很相似。当想要构建一个CSS图像时候,开发者随时可以调用paint()函数,也可以直接使用注册好的名字。

比如,可以自己扩充实现一个布局 瀑布流,点击去试试

.item-list {display: layout(waterfall)}
// 注册这个布局方式的方法,其中带*是一定要实现的
registerLayout('waterfall', class {
    static get inputProperties()
    static get childrenInputProperties()
    static get childDisplay()
    * intrinsicSizes(children, styleMap)
    * layout(constraints, children, styleMap, edges, breakToken)
})

// ===> 实现:
registerLayout(waterfall, class {
    static get inputProperties(){
        return ['width', 'height']
    }
    static get childrenInputProperties() {
        return ['x', 'y', 'position']
    }
    layout(constraints, children, styleMap, edges, breakToken){
        // 
    }
})

Worklets实践

xxxWorklet.addModule('xxx.js').then(...)

registerLayout和registerPaint这些代码放在哪里呢?答案就是worklet脚本(工作流脚本文件)。

Worklets的概念和web worker类似,它们允许你引入脚本文件并执行特定的JS代码,这样的JS代码需要满足两个条件:第一可以在渲染流程中调用;第二和主线程独立。

Worklet脚本严格控制了开发者所能执行的操作类型,这就保证了性能,Worklets的特点就是轻量以及生命周期较短。

CSS.paintWorklet.addModule('xxx.js')
CSS.layoutWorklet.addModule('xxx.js')
// xxx.js
registerPaint('xxx', class{
    static get inputProperties(){...}
    static get inputArguments(){...}
    paint (ctx, geom, props){...}
})

typed OM Object

可以把CSS Typed OM视为CSSOM2.0,目的是解决目前模型的一些问题,并实现CSS Parsing API 和 CSS属性与值API相关的特性。它将CSS值转化为有意义类型的JavaScript对象,而不是现在的字符串。现在读取CSS值增加了新的基类CSSStyleValue,有许多的子类可以更加精准的描述CSS值的类型:

现代化CSS

 使用 Typed OM主要有两种方法:

  1. 通过 attributeStyleMap设置和获取有类型的行间样式;
  2. 通过 computedStyleMap获取元素完整的 Typed OM样式。

现代化CSS

CSS?.px?.(4) ?? "4px"          

现代化CSS

现代化CSS

//1、demo

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JS IN CSS</title>
  <style>
    .el {
      --elUnit:500px;
      --arcColor:#8266ff;
      width: var(--elUnit);
      height: var(--elUnit);
      background: paint(background-canvas);
      --background-canvas:(ctx,geom) => {
        console.log(ctx);
        ctx.strokeStyle = `var(--arcColor)`;
        ctx.lineWidth = 4;
        ctx.beginPath();
        ctx.arc(200,200,50,0,2*Math.PI);
        ctx.stroke();
        ctx.closePath();
      }
    }
  </style>
</head>
<body>
  <div class="el"></div>
  <script>
    // 必须要启动一个server 否则无法加载
    CSS.paintWorklet.addModule('./arc.js')
  </script>
</body>
</html>

// arc.js
registerPaint(
  'background-canvas', 
  class {
    static get inputProperties() {
      return ['--background-canvas']
    }
    paint(ctx,geom,properties) {
      eval(properties.get('--background-canvas').toString())(ctx,geom)
    }
  }
);
// 需要启一个本地服务启动html页面,否则会提示跨域
              

现代化CSS

// demo2 星空

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JS IN CSS</title>
  <style>
    body{
      margin:0;
      color: #fff;
      font-size: 24px;
      background: #000;
    }
    body::before {
      content: '';
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      --star-density:0.8;
      --star-opacity:1;
      background-image: paint(yd-sky);
    }
    body::before {
      animation: shine 1s linear alternate infinite;
    }
    @keyframes shine{
      from {
        opacity: 1;
      }
      to {
        opacity: 0.4;
      }
    }
  </style>
</head>
<body>
  <div class="el"></div>
  <script>
    // 必须要启动一个server 否则无法加载
    CSS?.paintWorklet?.addModule('./arc.js') ??
    (() => {
      console.log('很遗憾,您的浏览器暂不支持Houdini')
    })()
  </script>
</body>
</html>

// arc.js
class YdSky {
  static get inputProperties() {
    return ['--star-density','--star-opacity']
  }
  paint(ctx,geom,properties) {
    const xMax = geom.width;
    const yMax = geom.height;
    ctx.fillRect(0,0,xMax,yMax)
    let starDensity = properties.get("--star-density").toString() || 1
    starDensity > 1 && (starDensity = 1)
    const hmTimes = Math.round((xMax + yMax) * starDensity);
    for(let i=0;i<hmTimes;i++){
      let x = Math.floor(Math.random() * xMax + 1);
      let y = Math.floor(Math.random() * yMax + 1)
      const size = Math.floor(Math.random() * 2 + 1)
      const hue = Math.floor(Math.random() * 360 +1)
      const opacity1 = Math.floor(Math.random()*9 +1)
      const opacity2 = Math.floor(Math.random()*9 +1)
      const opacity = +('.'+(opacity1+opacity2))*starDensity;
      ctx.fillStyle = `hsla(${hue},30%,80%,${opacity})`
      ctx.fillRect(x,y,size,size)
    }
  }
}

registerPaint(
  'yd-sky', 
  YdSky
);
              

现代化CSS

参考文章:

www.w3cschool.cn/lugfe/lugfe…

zhuanlan.zhihu.com/p/357492062 blog.csdn.net/zhanghuanhu…

转载自:https://juejin.cn/post/7178814952948367415
评论
请登录