CSS Custom Properties 自定义属性

为何要使用CSS自定义属性?

因为css preprocessor在变量的使用上有局限性:

  1. 不能动态修改变量值

  2. 在渲染时无法参考DOM的结构

  3. 变量值不能被javascript引用

 

如何声明CSS自定义属性?

类似于css preprocessor 的variables, $ in Sass, @ in Less, css custom propertiy 使用 -- 声明一个属性


.box{
  --box-color: #4d4e53;
  --box-padding: 0 10px;
}

 

如何使用CSS自定义属性?

使用var()函数

  <div class="box">
    box
    <div>
      box div
    </div>
  </div>
    .box{
      --box-color:rgba(200, 10, 100, .5);
      --box-padding: 0 10px;

      padding: var(--box-padding);
    }

    .box div{
      color: var(--box-color);
    }

 

114931_thYA_2510955.png

 

如何设置默认值?作为var()函数的第二个参数

.box{
  --box-color:#4d4e53;
  --box-padding: 0 10px;

  /* 10px is used because --box-margin is not defined. */
  margin: var(--box-margin, 10px);
}

 

CSS自定义属性的计算

使用calc()函数


:root{
  --indent-size: 10px;

  --indent-xl: calc(2*var(--indent-size));
  --indent-l: calc(var(--indent-size) + 2px);
  --indent-s: calc(var(--indent-size) - 2px);
  --indent-xs: calc(var(--indent-size)/2);
}

这里的:root代表html标签

如果使用了非单位数值(非px, rem等等),需要结合使用calc(),看下面的例子

:root{
  --gap: 10;
}

.box{
  padding: var(--spacer)px 0; /* DOESN'T work */
  padding: calc(var(--spacer)*1px) 0; /* WORKS */
}

 

CSS自定义属性的作用域和继承性

先来看看Sass变量的作用域

120052_AH8J_2510955.png

图片来源:smashingmagazine

可见,Sass变量的作用域依赖于代码的结构,然而css 自定义属性和css其他属性是一样的,是默认继承的(根据DOM的结构)

那么css 自定义变量的全局作用域在哪里?是:root

看个css 自定义属性的例子

html:

global
<div class="enclosing">
  enclosing
  <div class="closure">
    closure
  </div>
</div>

css:

Tip: .closure 继承了.enclosing的属性,但是font-size又是自己定义的,而且使用了.enclosing中定义的属性--enclosingVar

:root {
  --globalVar: 10px;
}

.enclosing {
  --enclosingVar: 20px;
}

.enclosing .closure {
  --closureVar: 30px;

  font-size: calc(var(--closureVar) + var(--enclosingVar) + var(--globalVar));
  /* 60px for now */
}

其实css写成下面这样效果也是一样的,因为class 是天然 cascading (根据DOM的结构)

    :root {
      --globalVar: 10px;
    }

    .enclosing {
      --enclosingVar: 20px;
    }

    .closure {
      --closureVar: 30px;
      font-size: calc(var(--closureVar) + var(--enclosingVar) + var(--globalVar));
      /* 60px for now */
    }

 

到这里我们再来看看前面说到的css preprocessor 变量的三个问题

  1. 不能动态修改变量值

  2. 在渲染时无法参考DOM的结构

  3. 变量值不能被javascript引用

第一个,动态修改变量值,个人感觉这个没啥用,但css 自定义属性可以解决这个问题

:root {
  --globalVar: 10px;
}

.enclosing {
  --enclosingVar: 20px;
}

.enclosing .closure {
  --closureVar: 30px;

  font-size: calc(var(--closureVar) + var(--enclosingVar) + var(--globalVar)); /* 80px for now, --closureVar: 50px is used */
  
  --closureVar: 50px;
}

一旦修改了自定义的属性值,浏览器就会重新计算相关变量值

 

第二个问题,渲染时没有参考DOM结构

还是看例子吧,比如有这么个需求,默认情况下,字体大小为10px, 在“highlighted”class时字体大小为30px

html:

<div class="default">
  default
</div>

<div class="default highlighted">
  default highlighted
</div>

sass:

.highlighted {
  $highlighted-size: 30px;
}

.default {
  $default-size: 10px;
  @if variable-exists(highlighted-size) {
    font-size: $highlighted-size;
  }
  @else {
    font-size: $default-size;
  }
}

结果:

095907_r5HG_2510955.png

分析:sass的结果是class=highlighted的文字的font-size还是为10px, 变量highlighted-size: 30px并没有起作用,这是为什么?

前面说过,css preprocessor变量的作用域是依赖于code的结构的,在.default{}的作用域中,访问.highlighted类中的变量,当然是未定义,所以font-size走else分支,使用default-size。为什么没有参考DOM结构呢?其实是因为sass变量的计算和处理都是在编译过程中,此时她完全不知道也无法知道DOM结果,所以,变量的作用域只能依赖于code的结构。

一个解决方法是.default{}加上.highlighted class:  .default.highlighted {}

但是css 自定义属性在作用域和属性的层级结构上却有天然的优势,她可以像css其他的属性一样,参考页面的DOM结构

css:

.highlighted {
  --highlighted-size: 30px;
}

.default {
  --default-size: 10px;
  
  /* use the "default-size" except the "highlighted-size" is provided */
  font-size: var(--highlighted-size, var(--default-size));
}

 

结果:

101203_RY6e_2510955.png

第三个问题,css preprocessor 变量值不能被javascript引用

但css 自定义变量却可以使用getPropertyValue和setProperty()读、写变量值

/**
* Gives a CSS custom property value applied at the element
* element {Element}
* varName {String} without '--'
*
* For example:
* readCssVar(document.querySelector('.box'), 'color');
*/
function readCssVar(element, varName){
  const elementStyles = getComputedStyle(element);
  return elementStyles.getPropertyValue(`--${varName}`).trim();
}

/**
* Writes a CSS custom property value at the element
* element {Element}
* varName {String} without '--'
*
* For example:
* readCssVar(document.querySelector('.box'), 'color', 'white');
*/
function writeCssVar(element, varName, value){
  return element.style.setProperty(`--${varName}`, value);
}

 

CSS 属性4个常用属性值和all属性

css的属性有4个通用的属性值:initial, inherit, unset, revert, 它们也可以使用在css自定义属性上

  • initial: 缺省值,使用官方的css specification, 比如<p>元素,text-align为left;display为inline;
  • inherit: 使用父元素的属性值,如果父元素的该属性没有定义,就相当于revert的效果
  • unset: initial + inherti, 表示没定义时怎么办,有些属性会使用inherit,继承父元素的属性定义,比如color, 有些属性会使用initial,比如border
  • revert: 以前叫做“default”, 当任何属性值在作者的stylesheet都没有定义时,采用下面的顺序寻找属性值
  1. 用户定义的stylesheet
  2. useragent stylesheet
  3. unset

详情参考这篇文章,里面的例子很详细:http://126kr.com/article/7ssx9rd04t6

现在,假如有个需求,某个组件要使用模块化的样式,可以使用all属性

.my-wonderful-clean-component{
  all: initial;
}

上面的代码表示重置了my-wonderful-clean-component类的样式,所有元素的属性值都使用官方css specification中的定义,排除了inherit; 但是all对于css 自定义属性并不起作用

未来理想的样子:

.my-wonderful-clean-component{
  --: initial; /* reset all CSS custom properties */
  all: initial; /* reset all other CSS styles */
}

 

浏览器支持情况

113516_6zTA_2510955.png

http://caniuse.com/#search=custom%20properties

 

CSS自定义属性使用javascript动态改变三维视图例子

核心css:

#world{
    --translateZ:0;
    --rotateX:65;
    --rotateY:0;

    transform-style:preserve-3d;
    transform:translateZ(calc(var(--translateZ) * 1px)) rotateX(calc(var(--rotateX) * 1deg)) rotateY(calc(var(--rotateY) * 1deg));
}

定义了三个变量,分别控制视图的缩放,x轴、y轴的旋转,通过监听鼠标的mousewheel和mouseover事件,动态改变这三个变量,实现视图的三维动态响应

核心js:

class css3dCube {
  constructor() {
    this.worldEl = document.querySelector('#world');

    this.worldZ = 0;
    this.worldXAngle = 0;
    this.worldYAngle = 0;

    this.bindEvents();
  }

  // CSS
  updateView() {
    this.worldEl.style.setProperty('--translateZ', this.worldZ);
    this.worldEl.style.setProperty('--rotateX', this.worldXAngle);
    this.worldEl.style.setProperty('--rotateY', this.worldYAngle);
  }

  // EVENTS
  onMouseWheel(e) {

    let delta;
    if (e.detail) {
      delta = e.detail * -5;
    } else if (e.wheelDelta) {
      delta = e.wheelDelta / 8;
    } else {
      delta = e.deltaY;
    }

    if (!delta) return;

    this.worldZ += delta * 5;

    // scroll/perspective check
    if (this.worldZ > 300) {
      this.worldZ = 300;
    } else if (this.worldZ < -3000) {
      this.worldZ = -3000;
    } else {
      e.preventDefault();
    }

    this.updateView();
  };

  onMouseMove(e) {
    this.worldXAngle = (.5 - (e.clientY / window.innerHeight)) * 180;
    this.worldYAngle = -(.5 - (e.clientX / window.innerWidth)) * 180;
    this.updateView();
  };

  bindEvents() {
    window.addEventListener('mousewheel', this.onMouseWheel.bind(this));
    window.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this));
    window.addEventListener('mousemove', this.onMouseMove.bind(this));
  };
}

new css3dCube();

监听鼠标的变化,动态改变worldZ, worldXAngel, worldYAngel三个成员变量的值,然后通过updateView()方法修改DOM中元素的css 自定义属性值

详见:http://codepen.io/malyw/pen/xgdEQp

参考资料:

https://www.smashingmagazine.com/2017/04/start-using-css-custom-properties/?utm_source=frontendfocus&utm_medium=email

http://126kr.com/article/7ssx9rd04t6

转载于:https://my.oschina.net/u/2510955/blog/884260

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值