平时写CSS,感受有不少多余的代码或者很差实现的方法,因而有了预处理器的解决方案,主旨是write less &do more。其实原生css中,用上css变量也不差,加上bem命名规则只要嵌套不深也能和less、sass的嵌套媲美。在一些动画或者炫酷的特效中,不用js的话多是用了css动画、svg的animation、过渡,复杂动画实现用了js的话可能用了canvas、直接修改style属性。用js的,而后有没有想过一个问题:“要是canvas那套放在dom上就爽了”。由于复杂的动画频繁操做了dom,违背了滚瓜烂熟的“性能优化之一:尽可能少操做dom”的规矩,嘴上说着不要,手却是很诚实地ele.style.prop = <newProp>
,但是要实现效果这又是迫不得已或者大大减少工做量的方法。css
咱们都知道,浏览器渲染的流程:解析html和css(parse),样式计算(style calculate),布局(layout),绘制(paint),合并(composite),修改了样式,改的环节越深代价越大。js改变样式,首先是操做dom,整个渲染流程立刻从新走,可能走到样式计算到合并环节之间,代价大,性能差。而后痛点就来了,浏览器有没有能直接操做前面这些环节的方法呢而不是依靠js?有没有方法不用js操做dom改变style或者切换class来改变样式呢?html
因而就有CSS Houdini了,它是W3C和那几个顶级公司的工程师组成的小组,让开发者能够经过新api操做CSS引擎,带来更多的自由度,让整个渲染流程均可以被开发者控制。上面的问题,不用js就能够实现曾经须要js的效果,并且只在渲染过程当中,就已经按照开发者的代码渲染出结果,而不是渲染完成了再从新用js强行走一遍流程。git
关于houdini最近动态可点击这里 上次CSS大会知道了有Houdini的存在,那时候只有cssom,layout和paint api。前几天忽然发现,Animation api也有了,不得不说,之后极可能是Houdini遍地开花的时代,如今得进一步了解一下了。一句话:这是css in js到js in css的转变github
若是你用less、sass只为了人家有变量和嵌套,那用原生css也是差很少的,由于原生css也有变量:chrome
好比定义一个全局变量–color(css变量双横线开头)canvas
:root { --color: #f00; }
使用的时候只要var一下api
.f{ color: var(--color); }
咱们的html:浏览器
<div class="f">123</div>
因而,红色的123就出来了。sass
css变量还和js变量同样,有做用域的:性能优化
:root { --color: #f00; } .f { --color: #aaa } .g{ color: var(--color); } .ft { color: var(--color); }
html:
<div className="f"> <div className="ft">123</div> </div> <div className=""> <div className="g">123</div> </div>
因而,是什么效果你应该也很容易就猜出来了:
css能搞变量的话,咱们就能够作到修改一处牵动多处的变更。好比咱们作一个像准星同样的四个方向用准线锁定鼠标位置的效果: 用css变量的话,比传统一个个元素设置style优雅多了:
<div id="shadow"> <div class="x"></div> <div class="y"></div> <div class="x_"></div> <div class="y_"></div> </div>
:root{ --x: 0px; --y: 0px; } body{ margin: 0 } #shadow{ width: 50%; height: 600px; border: #000 1px solid; position: relative; margin: 0; } .x, .y, .x_, .y_ { position: absolute; border: #f00 2px solid; } .x { top: 0; left: var(--x); height: 20px; width: 0; } .y { top: var(--y); left: 0; height: 0; width: 20px; } .x_ { top: 600px; left: var(--x); height: 20px; width: 0; } .y_ { top: var(--y); left: 100%; height: 0; width: 20px; }
const style = document.documentElement.style shadow.addEventListener('mousemove', e => { style.setProperty(`--x`, e.clientX + 'px') style.setProperty(`--y`, e.clientY + 'px') })
那么,对于github的404页面这种内容和鼠标位置有关的页面,思路是否是一会儿就出来了
都有DOM了,那CSSOM也理所固然存在。咱们平时改变css的时候,一般是直接修改style或者切换类,实际上就是操做DOM来间接操做CSSOM,而type om是一种把css的属性和值存在attributeStyleMap对象中,咱们只要直接操做这个对象就能够作到以前的js改变css的操做。另一个很重要的点,attributeStyleMap存的是css的数值而不是字符串,并且支持各类算数以及单位换算,比起操做字符串,性能明显更优。
接下来,基本脱离不了window下的CSS这个属性。在使用的时候,首先,咱们能够采起渐进式的作法:
if('CSS' in window){...}
CSS.px(1); // 1px 返回的结果是:CSSUnitValue {value: 1, unit: "px"} CSS.number(0); // 0 好比top:0,也常常用到 CSS.rem(2); //2rem new CSSUnitValue(2, 'percent'); // 还能够用构造函数,这里的结果就是2% // 其余单位同理
本身在控制台输入CSSMath,能够看见的提示,就是数学运算
new CSSMathSum(CSS.rem(10), CSS.px(-1)) // calc(10rem - 1px),要用new否则报错 new CSSMathMax(CSS.px(1),CSS.px(2)) // 顾名思义,就是较大值,单位不一样也能够进行比较
既然是新的东西,那就有它的使用规则。
element.attributeStyleMap.get(attributeName)
,返回一个CSSUnitValue对象element.attributeStyleMap.set(attributeName, newValue)
,设置值,传入的值能够是css值字符串或者是CSSUnitValue对象固然,第一次get是返回null的,由于你都没有set过。“那我仍是要用一下getComputedStyle再set咯,这还不是和以前的差很少吗?”
实际上,有一个相似的方法:element.computedStyleMap
,返回的是CSSUnitValue对象,这就ok了。咱们拿前面的第一部分CSS变量的代码测试一波
document.querySelector('.x').computedStyleMap().get('height') // CSSUnitValue {value: 20, unit: "px"} document.querySelector('.x').computedStyleMap().set('height', CSS.px(0)) // 不见了
paint、animation、layout API都是以worker的形式工做,具体有几个步骤:
// worker.js class RandomColorPainter { // 能够获取的css属性,先写在这里 // 我这里定义宽高和间隔,从css获取 static get inputProperties() { return ['--w', '--h', '--spacing']; } /** * 绘制函数paint,最主要部分 * @param {PaintRenderingContext2D} ctx 相似canvas的ctx * @param {PaintSize} PaintSize 绘制范围大小(px) { width, height } * @param {StylePropertyMapReadOnly} props 前面inputProperties列举的属性,用get获取 */ paint(ctx, PaintSize, props) { const w = (props.get('--w') && +props.get('--w')[0].trim()) || 30; const h = (props.get('--h') && +props.get('--h')[0].trim()) || 30; const spacing = +props.get('--spacing')[0].trim() || 10; for (let x = 0; x < PaintSize.width / w; x++) { for (let y = 0; y < PaintSize.height / h; y++) { ctx.fillStyle = `#${Math.random().toString(16).slice(2, 8)}` ctx.beginPath(); ctx.rect(x * (w + spacing), y * (h + spacing), w, h); ctx.fill(); } } } } registerPaint('randomcolor', RandomColorPainter);
接着咱们须要引入该worker:
CSS && CSS.paintWorklet.addModule('worker.js');
最后咱们在一个class为paint的div应用样式:
.paint{ background-image: paint(randomcolor); width: 100%; height: 600px; color: #000; --w: 50; --h: 50; --spacing: 10; }
再想一想用js+div,是否是要先动态生成n个,而后各类计算各类操做dom,想一想就可怕。若是是canvas,这但是canvas背景,你又要在上面放一个div,并且还要定位一波。
注意: worker是没有window的,因此想搞动画的就不能内部消化了。不过能够靠外面的css变量,咱们用js操做css变量能够解决,也比传统的方法优雅
支持状况 点击这里查看 首先,看一下支持度,目前浏览器并无彻底稳定使用,因此须要跟着它的提示:
Experimental Web Platform features” on chrome://flags
,在chrome地址栏输入chrome://flags
再找到Experimental Web Platform features
并开启。
CSS.registerProperty({
name: '--myprop', //属性名字 syntax: '<length>', // 什么类型的单位,这里是长度 initialValue: '1px', // 默认值 inherits: true // 会不会继承,true为继承父元素 });
说到继承,咱们回到前面的css变量,已经说了变量是区分做用域的,其实父做用域定义变量,子元素使用该变量其实是继承的做用。若是inherits: true
那就是咱们看见的做用域的效果,若是不是true则不会被父做用域影响,并且取默认值。
这个自定义属性,精辟在于,能够用永久循环的animation驱动一次性的transform。换句话说,咱们若是用了css变量+transform,能够靠js改变这个变量达到花俏的效果。可是,如今不须要js,只要css内部消化,transform成为永动机。
// 咱们先注册几种属性 ['x1','y1','z1','x2','y2','z2'].forEach(p => { CSS.registerProperty({ name: `--${p}`, syntax: '<angle>', inherits: false, initialValue: '0deg' }); });
而后写个样式
#myprop, #myprop1 { width: 200px; border: 2px dashed #000; border-bottom: 10px solid #000; animation:myprop 3000ms alternate infinite ease-in-out; transform: rotateX(var(--x2)) rotateY(var(--y2)) rotateZ(var(--z2)) }
再来看看咱们的动画,为了眼花缭乱,加了第二个改了一点数据的动画
@keyframes myprop { 25% { --x1: 20deg; --y1: 30deg; --z1: 40deg; } 50% { --x1: -20deg; --z1: -40deg; --y1: -30deg; } 75% { --x2: -200deg; --y2: 130deg; --z2: -350deg; } 100% { --x1: -200deg; --y1: 130deg; --z1: -350deg; } } @keyframes myprop1 { 25% { --x1: 20deg; --y1: 30deg; --z1: 40deg; } 50% { --x2: -200deg; --y2: 130deg; --z2: -350deg; } 75% { --x1: -20deg; --z1: -40deg; --y1: -30deg; } 100% { --x1: -200deg; --y1: 130deg; --z1: -350deg; } }
html就两个div:
<div id="myprop"></div> <div id="myprop1"></div>