当前位置: 首页 > Web前端 > JavaScript

现代CSS高级技巧,完美的波浪进度条效果!

时间:2023-03-27 13:26:48 JavaScript

本文为稀土掘金科技社区首篇署名文章。14天内禁止转载。14天后禁止擅自转载。侵权必究!我的新专栏——现代CSS和Web动画解决方案。它将专注于实现复杂的布局、兼容设备差异、制作炫酷的动画、制作复杂的交互、提高可访问性以及构建异想天开的效果。在兼顾基本概况的同时,注重技能的挖掘和结合实际运用。欢迎您的关注。正文从这里开始。本文是CSSHoudini上的CSSPaintingAPI系列的第三篇。现代CSS高级图像淡化技术现代CSS高级技术,像Canvas一样自由绘图和构建风格!在上两篇文章中,我们一步步详细介绍了CSSPaintingAPI是如何实现自定义图案甚至动画效果的!在本文中,我们将继续探索和尝试使用CSSPaintingAPI来实现一些过去纯CSS无法实现的效果。CSSPaintingAPI让我们简单快速地了解一下什么是CSSPaintingAPI。CSSPaintingAPI是CSSHoudini的一部分。另一方面,Houdini是一组暴露部分CSS引擎的低级API,允许开发人员通过加入浏览器渲染引擎的样式和布局过程来扩展CSS。Houdini是一组API,可让开发人员直接访问CSS对象模型(CSSOM),允许开发人员编写浏览器可以解析为CSS的代码,创建新的CSS功能,而无需等待它们在浏览器中完成。CSSPaintAPI的当前版本是CSSPaintingAPILevel1。它也被称为CSSCustomPaint或Houdini'sPaintWorklet。我们可以把它理解为JS中的CSS,利用JavaScriptCanvas的强大能力,实现以往CSS无法实现的功能。使用CSSPaintingAPI实现波浪效果CSS实现波浪效果一直是CSS的难点之一。以前虽然我们有很多方法可以使用Hack制作一些波浪效果,但是我在之前的很多文章中都反复提到:纯CSS实现波浪效果!使用CSS制作波浪效果的巧妙方法圆角秒杀滤镜,使用滤镜制作圆角和波浪效果!是的,大多数时候,我们使用一些技巧来实现波浪效果,就像这样:现在,使用CSSPaintingAPI,我们已经可以绘制出真正的波浪效果。看代码:

div{position:relative;宽度:300px;高度:300px;背景:油漆(waveDraw);边界半径:50%;border:2pxsolidrgba(255,0,0,0.5);}我们定义了一个waveDraw方法,然后,我们实现了使用registerPaint这个方法就可以了。//文件名为CSSHoudini.jsregisterPaint("waveDraw",class{staticgetinputProperties(){return[];}paint(ctx,size,properties){const{width,height}=size;constinitY=height*0.5;ctx.beginPath();for(leti=0;i<=width;i++){ctx.lineTo(i,initY+Math.sin((i)/20)*10);}ctx.lineTo(宽度,高度);ctx.lineTo(0,height);ctx.lineTo(0,initY);ctx.closePath();ctx.fillStyle='rgba(255,0,0,0.9)';ctx.fill();}});这样我们就得到了这样一个波浪效果:k3u1fbpfcp-zoom-1.image">上面的代码其实很容易理解,简单解释一下,我们的核心是根据函数的Math.sin()三角函数图画一个sin(x)三角形。整个图从ctx.beginPath()开始,第一个点是ctx.lineTo(0,initY+Math.sin((i)/20)*10),但是Math.sin(0)=0,所以是等于ctx.lineTo(0,initY)initY在这里的作用是控制绘制波浪图形的高度。我们这里的值为initY=height*0.5,定义为图形的中间位置。使用for(leti=0;i<=width;i++)循环,配合ctx.lineTo(i,initY+Math.sin((i)/20)*10),即先画一个点再画三个每个x轴上的点循环外的ctx.lineTo的作用是让整个图形成一个闭环。最后ctx.closePath()完成整个路径,ctx.fill()用于着色。如果不使用ctx.fill()着色,使用ctx.stroke()绘制边框也是可以的。其实我们得到这样一张图:zoom-1.image">上图中css代码中的border-radius:50%也去掉了,方便大家理解。当然,上面的图形有个很大的问题,它不能移动,所以我们需要用一个CSS的@Property自定义变量,让它有一些动画效果。我们需要修改代码,首先,添加一个CSS@Property自定义变量:@property--animation-tick{syntax:'';继承:假的;初始值:1000;}div{//...代码与上面动画一致:移动20s无限线性;--animation-tick:1000;}@keyframesmove{100%{--animation-tick:0;}}我们添加了一个--animation-tick变量,并使用CSS动画,让它从1000减少到0。下一步是使用这个不断变化的CSS自定义变量。我们在waveDraw方法中使用://文件名为CSSHoudini.jsregisterPaint("waveDraw",class{staticgetinputProperties(){return["--animation-tick"];}paint(ctx,size,properties){lettick=Number(properties.get("--animation-tick"));const{width,height}=size;constinitY=height*0.5;ctx.beginPath();for(leti=0;i<=width;i++){ctx.lineTo(i,initY+Math.sin((i+tick)/20)*10);}ctx.lineTo(width,height);ctx.lineTo(0,height);ctx.lineTo(0,initY);ctx.closePath();ctx.fillStyle='rgba(255,0,0,0.9)';ctx.fill();}});仔细看,和上面的代码没有太大的变化,核心是在用三角函数画图形的时候,我们把这个变量加进去。由原来的ctx.lineTo(i,initY+Math.sin((i)/20)*10)变成ctx.lineTo(i,initY+Math.sin((i+tick)/20)*10).这样,在这个不断变化的变量的作用下,我们的波浪图就可以动了:CodePenDemo--CSSHoudiniWave可以动了,但是总感觉少了点什么。如果我们把这个波浪效果应用到进度条这样的效果上,我们可以快速定义波浪的幅度、每个波峰之间的距离、效果的颜色、百分比等等。所以我们需要再传递一个CSS变量,使它成为一个真正可用的、包装良好的波浪形进度条。让我们做一个简单的转换:@property--animation-tick{syntax:'';继承:假的;初始值:1000;}@property--height{语法:'';继承:假的;初始值:.7;}div{位置:相对;宽度:300px;高度:300px;背景:油漆(waveDraw);动画:移动20s无限线性;边界半径:50%;边框:2px实心var(--color1);--振幅:15;--差距:28;--动画刻度:700;--高度:0.7;--color1:rgba(255,0,0,0.5);--color2:rgba(255,0,0,0.4);--color3:rgba(255,0,0,0.3);transition:--height8s;}可以看到,我们定义了很多CSS变量,每一次,都是有意义的:--animation-tick代表波浪运动的速度--amplitude波浪的幅度--gappeakspacing--initHeight初始高度--color1,--color2,--color3我们将叠加3层波浪效果更逼真,这里的3种颜色代表3层波浪的颜色定义了这些CSS之后变量,我们可以在实际的waveDraw方法中使用它们。看代码:registerPaint("waveDraw",class{staticgetinputProperties(){return["--animation-tick","--height","--gap","--amplitude","--color1","--color2","--color3"];}paint(ctx,size,properties){lettick=Number(properties.get("--animation-tick"));letinitHeight=Number(属性.get("--height"));让gap=Number(properties.get("--gap"));让amplitude=Number(properties.get("--amplitude"));让color1=properties。get("--color1");让color2=properties.get("--color2");让color3=properties.get("--color3");this.drawWave(ctx,size,tick,amplitude,gap,initHeight,color1);this.drawWave(ctx,size,tick*1.21,amplitude/0.82,gap+2,initHeight+0.02,color2);this.drawWave(ctx,size,tick*0.79,amplitude/1.19,gap-2,initHeight-0.02,color3);}/***ctx*size*tick速率*amplitude振幅*gap波峰间隔*initHeight初始高度*color颜色*/drawWave(ctx,size,tick,amplitude,gap,initHeight,color){const{width,height}=尺寸;constinitY=height*initHeight;打勾=打勾*2;ctx.beginPath();for(leti=0;i<=width;i++){ctx.lineTo(i,initY+Math.sin((i+tick)/gap)*amplitude);}}ctx.lineTo(宽度,高度);ctx.lineTo(0,高度);ctx.lineTo(0,initY);ctx.closePath();ctx.fillStyle=颜色;ctx.填充();}});如您所见,我们在paint()方法中调用了this.drawWave()。每次调用this.drawWave()都会生成一个波浪图形,通过3层生成3层波浪的叠加效果。此外,我们在CSS中定义的所有变量分别用于控制波浪效果的不同参数。这样我们就得到了这样一个波浪效果:通过控制CSS中的--height变量,也可以改变高度,从而完成真正的百分比,实现一个进度条效果。div:hover{--height:0;}效果如下:很好,很好的效果。借助上面提到的一些CSS自定义变量,我们可以通过封装的waveDraw方法实现不同颜色、不同大小、不同速度的波浪进度条效果。我们只需要简单的改变传入的CSS变量参数:
div{position:relative;宽度:300px;高度:300px;背景:油漆(waveDraw);动画:移动20s无限线性;边界半径:50%;边框:2px实心var(--color1);--振幅:15;--差距:28;:700;--高度:0.7;--color1:rgba(255,0,0,0.5);--color2:rgba(255,0,0,0.4);--color3:rgba(255,0,0,0.3);过渡:--height8s;}div:nth-child(2){--amplitude:6;--差距:25;--动画刻度:300;--高度:0.5;--color1:rgba(28,90,199,0.5);--color2:rgba(28,90,199,0.4);--color3:rgba(28,90,199,0.3);}div:nth-child(3){--amplitude:3;--差距:30;--动画刻度:1200;--高度:0.3;--color1:rgba(178,120,33,0.5);--color2:rgba(178,120,33,0.4);--color3:rgba(178,120,33,0.3);}查看它是如何工作的:CodePenDemo--CSSHudiniCu风暴波效应!就这样,借助CSSPaintingAPI,我们完美的实现了波浪图形,并以此实现了波浪进度条效果。通过传入不同的CSS变量,我们就有能力快速批量生成不同的效果。弥补了以往CSS在波浪效果上的缺陷!当然,基于上面的代码,还有一些优化的空间:在上面的CSS代码中,我们可以看到我们传入了3个关于颜色的CSS变量,--color1,--color2,--color3,正常,这里传入一种颜色就可以了,通过转换为HSL颜色表示法,替换L颜色值,得到另外两个近似颜色值。当然这样做会增加很多JavaScript代码,所以为了方便大家理解,本文偷懒,直接传入3个CSS颜色变量值;我把整个波浪效果单轮的动画时长设置为20s,但是在本文的动画中,手尾连接没有适配,即每20s,波浪效果可能会有明显的跳跃感。为了解决这个问题,有两种思路。通过精确的计算,让动画的最后一帧与动画的第一帧衔接起来。把--animation-tick的值设置的很大,然后把对应的单轮动画时间设置的很长,这样就几乎感觉不到动画的跳帧了。第三个问题可能是兼容性?兼容性?嗯,其实上一篇文章也讲了兼容性问题,因为可能有很多同学看了这篇文章,没有看前面两篇文章。那么,CSSPaintingAPI的兼容性如何呢?CanIUse-registerPaint数据如下(截至2022-11-23):基于Chromium内核的Chrome和Edge浏览器长期支持,主流浏览器中Firefox和Safari暂不支持。CSSHoudini虽然强大,但是大规模生产环境似乎还需要一段时间等待。让我们给它一点时间!最后,本文到此结束,希望本文对大家有所帮助:)有什么问题或者建议可以多交流,原创文章,文笔有限,知识匮乏,如有不妥之处,敬请谅解!这篇文章,请告诉我。