平时很喜欢用CSS构建一些有趣的图形。我们先来看一个简单的例子。首先,假设我们实现了一个10x10的网格:此时,我们可以使用一些随机效应来优化这个图案。比如我们给它随机添加不同的颜色:虽然它是用随机性来随机填充每个格子的颜色,看起来有点意思,但这只是一个杂乱的图形,没有任何艺术感。为什么是这样?因为这里的随机性是完全随机的,是一种白噪声。什么是白噪声?噪声实际上是一个随机数发生器。那么,什么是白噪声?如果从程序员的角度来理解,可以理解为我们在JavaScript中使用的random()函数,生成的数字大致是0~1范围内完全随机的。噪声的基础是随机数。例如,如果我们给上面图形的每一个格子添加一种随机的颜色,得到的就是一个杂乱无章的图形块,没有太多美感。白噪声,简称白噪声,是一种具有恒定功率谱密度的随机信号。也就是说,该信号在各个频段的功率谱密度是相同的。由于白光是各种频率(颜色)的单色光的混合,这种具有平坦功率谱的信号的性质称为“白”信号,因此该信号称为白噪声。因为使用白噪声生成的图形看起来不自然,不太美观。观察现实生活中的自然噪音,它们不会像上面那样增长。例如,木头的纹理和山脉的起伏在形状上往往是分形的,即它们包含不同层次的细节。这些随机成分并不是完全独立的,而是存在一定的联系。显然,白噪声不会那样做。Perlinnoise这么一来,我们自然就引入了Perlinnoise。柏林噪声(Perlinnoise)指的是KenPerlin发明的自然噪声生成算法。在介绍之前,我们先看看上图,如果我们用柏林噪声代替白噪声(完全随机)会是什么样子?可能是这样的:这里我做了一个动图,你可以感觉到每次点击都是使用Perlin噪声随机性给每个格子赋予不同随机颜色的结果:可以看到使用Perlin噪声随机效果生成的图形是彼此不无关系,它们之间的变化是连续的,它们之间没有跳跃。这种随机效应类似于自然界中的随机效应,如上面提到的木纹变化、山体起伏等。如上所述,噪声实际上是一个随机数发生器。在这里:白噪声的问题在于它太随机和不规则。柏林噪波基于随机性,并在此基础上使用缓动曲线进行平滑插值,使最终的噪波效果更加自然具体。实现方法在这里ImprovedNoisereferenceimplementation,你可以看看,源代码其实不多://这段代码实现了我在相应的SIGGRAPH2002paper中描述的算法。//JAVAREFERENCEIMPLEMENTATIONOFIMPROVEDNOISE-COPYRIGHT2002KENPERLIN.publicfinalclassImprovedNoise{staticpublicdoublenoise(doublex,doubley,doublez){intX=(int)Math.floor(x)&255,//找到Y=(int)的单位立方体Math.floor(y)&255,//包含点。Z=(int)Math.floor(z)&255;x-=Math.floor(x);//求相对X,Y,Zy-=Math.floor(y);//立方体中的点。z-=数学。地板(z);doubleu=fade(x),//计算渐变曲线v=fade(y),//对每个X、Y、Z。w=褪色(z);intA=p[X]+Y,AA=p[A]+Z,AB=p[A+1]+Z,//B=p[X+1]+Y,BA=p[的散列坐标B]+Z,BB=p[B+1]+Z;//8个立方体角,返回lerp(w,lerp(v,lerp(u,grad(p[AA],x,y,z)),//并添加grad(p[BA],x-1,y,z)),//混合lerp(u,grad(p[AB],x,y-1,z),//结果grad(p[BB],x-1,y-1,z))),//从8lerp(v,lerp(u,grad(p[AA+1],x,y,z-1),//CORNERSgrad(p[BA+1],x-1,y,z-1)),//OFCUBElerp(u,grad(p[AB+1],x,y-1,z-1),grad(p[BB+1],x-1,y-1,z-1))));}staticdoublefade(doublet){returnt*t*t*(t*(t*6-15)+10);}staticdoublelerp(doublet,doublea,doubleb){returna+t*(b-a);}静态双学位(inthash,doublex,doubley,doublez){inth=hash&15;//转换LO4位哈希码doubleu=h<8?x:y,//进入12个梯度方向。v=h<4?是:h==12||h==14?x:z;返回((h&1)==0?你:-u)+((h&2)==0?v:-v);}staticfinalintp[]=newint[512],permutation[]={151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,??36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,static{for(inti=0;i<256;i++)p[256+i]=p[i]=排列i];}}当然,这篇文章不是专门讲如何实现Perlin噪声的。任何人看到上面的代码都会被惊呆。我们只需要知道我们可以使用柏林噪声来构建更规则的图形效果。让我们的图形更美观。使用CSS-doodle在CSS中使用PerlinNoise那么我们如何在CSS中使用PerlinNoise?一种方法是找一些现成的库,比如p5.js中的noise函数。当然在这里,我习惯使用CSS-doodle这个我在很多文章中都介绍过的CSS图形构建库。简单的说,CSS-doodle是一个基于Web-Component的库。让我们可以快速创建基于CSSGrid布局的页面,并提供各种方便的指令和功能(随机、循环等),让我们通过一套规则获得不同的CSS效果。你可以简单的看一下它的主页——CSS-doodle主页,5分钟就能快速上手。例如上图,它的整个代码::doodle{@size:50vmin;间隙:1px;}background:hsl(@rn(255,1,2),@rn(10%,90%),@rn(10%,90%));可以,可以勾勒出这样的图案简单几句:CSSPattern--CSSDoodleissimple说明:css-doodle是基于Web-Component封装的,基本上所有的代码都写在标签中。当然,你也可以写一些原生的CSS/JavaScript来帮助你使用grid="10x10"生成一个10x10的Grid网格,结合@size:50vmin,意思是生成一个10x10的Grid网格布局,宽高为50vmin,其中gap:1px表示Grid网格布局的间隙。最后,整个代码的核心部分是后台:hsl(@rn(255,1,2),@rn(10%,90%),@rn(10%,90%)),这里的意思是赋值一个为每个网格项设置背景色,其中@rn(),是核心部分,使用Perlin噪声算法有规律地将背景色映射到每个网格。当然,在最新的CSS-doodle文档中暂时找不到@rn()函数的用法。为此,我特地请教了图书馆的作者袁川先生。得到的回复是官网近期会重构,所以目前还没有更新最新的语法。同时@rn()的实现使用了Perlinnoise的实现。同时该函数相当于p5.js中的noise函数,同时做一个map,map在前面函数参数设置的从到到到的范围内。这里的@rn()Perlinnoise会根据Grid格子随机映射到每个格子上,使得相邻Grid项之间的值之间存在一定的相关性。比如我们有一个10x10的Grid布局,给每个Grid项添加一个伪元素,用@r(100)填充伪元素的内容。注意@r()函数是不规则的,是完全随机的,那么生成的数字大概是这样的:可以看出,它们之间的每一个数字都是完全随机的,互不相关的。如果我们使用相关的柏林噪声随机性呢?如果用@rn(100)来填充每一个格子,大概是这样的:观察,很容易发现相邻的框之间,或者多个连续的格子之间,存在一定的相关性,这使得我们使用创建的图形它会有一定的规则。可以简单的看下源码的实现。目前前提是你需要对CSS-doodle的用法有一定的了解:rn({x,y,context,position,grid,extra,shuffle}){letcounter='noise-2d'+位置;让[ni,nx,ny,nm,NX,NY]=last(extra)||[];让isSeqContext=(ni&&nm);return(...args)=>{让{from=0,to=from,frequency=1,amplitude=1}=get_named_arguments(args,['from','to','frequency','amplitude']);if(args.length==1){[from,to]=[0,from];}if(!context[counter]){context[counter]=newPerlin(shuffle);}frequency=clamp(frequency,0,Infinity);振幅=钳位(振幅,0,无穷大);让transform=[from,to].every(is_letter)?by_charcode:by_unit;让t=isSeqContext?context[counter].noise((nx-1)/NX*frequency,(ny-1)/NY*frequency,0):context[counter].noise((x-1)/grid.x*frequency,(y-1)/grid.y*频率,0);让fn=transform((from,to)=>map2d(t*amplitude,from,to,amplitude));让价值=fn(从,到);返回push_stack(上下文,'last_rand',值);};},语法大概是@rn(from,to,frequency,amplitude),其中from和to表示随机范围,而frequency表示噪声的频率,amplitude表示噪声的振幅。这两个参数可以理解为控制随机效应的频率和大小。其中,newPerlin(shuffle)使用了Perlin噪声算法。ShowTimeOK,上面已经介绍了很多与噪声和CSS-doodle相关的知识,让我们回到CSS,回到本文的正文。在以上图形的基础上,我们可以添加随机scale()和skew()。如果完全随机,代码是这样的::doodle{grid-gap:1px;宽度:600px;高度:600px;}背景:hsl(@r(360),80%,80%);变换:缩放(@r(1.1,.3,3))倾斜(@r(-45deg,45deg,3));html,body{宽度:100%;高度:100%;background-color:#000;}上面的代码表示一个20x20的Grid网格,每个Grid项都设置了完全随机的背景色,scale()和skew()。当然,这里我们使用@r()而不是@rn(),每个格子的每个属性都是随机的,没有任何相关性,那么我们会得到这样一个模式:嗯,这是什么鬼,没有美感在全部。我们只需要在上面代码的基础上把普通的完全随机改成柏林噪声随机@rn()即可::doodle{grid-gap:1px;宽度:600px;高度:600px;}背景:hsl(@rn(360),80%,80%);transform:scale(@rn(1.1,.3,3))skew(@rn(-45deg,45deg,3));此时可以得到完全不同的效果:这是因为每个Griditem的随机效果是基于它们在Grid布局中的位置,并且它们是相互关联的。这是Perlin噪声随机效应。我可以添加色调旋转动画:html,body{width:100%;高度:100%;背景色:#000;animation:change10slinearinfinite;}@keyframeschange{10%{filter:hue-rotate(360deg);}}看效果,在CSS-doodle中,由于随机效果,每次刷新都能得到不同的图案:CSSDoodle-CSSPattern2当然,这种风格也可以搭配其他各种想法,比如this:CSSDoodle-CSSPattern3orthis:CSSDoodle-CSSPattern4emmm,orthis:CSSDoodle-CSSPattern5是的,我们可以随机应用PerlinNoise在各种属性方面,我们可以放飞想象力,尝试各种组合。下面是Perlin噪声在点阵定位中的使用::doodle{@size:90vmin;视角:10px;}位置:绝对;顶部:0;左:0;宽度:2px;高度:2px;边界半径:50%;顶部:@rn(1%,100%,1.5);左:@rn(1%,100%,1.5);变换:比例(@rn(。1、5、2));背景:hsl(@rn(1,255,3),@rn(10%,90%),@rn(10%,90%));CodePenDemo--CSSDoodle-CSSPattern6也可以结合使用transform:rotate():@place-cell:center;@size:计算(@i*1.5%);:涂鸦{宽度:60vmin;高度:60vmin;}z-index:calc(999-@i);边界半径:50%;border:1px@p(dashed,solid,double)hsl(@rn(255),70%,@rn(60,90%));边框底色:透明;左边框颜色:透明;transform:rotate(@rn(-720deg,720deg))scale(@rn(.8,1.2,3));效果如下:当然每次都是随机的,会有不同结果:鳕鱼ePenDemo--CSSdoodle-CSSPattern7嗯,个人想象力有限,大家可以自己找一个DEMO,fork一下,尝试碰撞出不一样的火花。终于,本文到此结束,希望对大家有所帮助:)更多精彩的CSS技术文章汇总在我的Github——iCSS,持续更新中。欢迎点个星订阅收藏。有什么问题或者建议可以多交流。原创文章文笔有限,知识匮乏。如果文章中有任何不准确的地方,请告诉我。