box-shadow参数学习及渲染过程研究

参数定义

CSS 的 box-shadow 属性用于在元素的框架周围添加阴影效果。它可以接受多个由逗号分隔的阴影效果,每个阴影效果由以下几部分组成:

  • h-offset:水平阴影的位置。正值将阴影向右移动,负值将阴影向左移动。
  • v-offset:垂直阴影的位置。正值将阴影向下移动,负值将阴影向上移动。
  • blur:阴影的模糊距离。值越大,阴影边缘越模糊。如果省略,值为0,即阴影将尖锐清晰。
  • spread:阴影的大小。正值会增加阴影的扩展范围,负值会减小阴影的扩展范围。如果省略,值为0。
  • color:阴影的颜色。如果省略,值为 black。
  • inset:如果指定,阴影将在元素内部(即内阴影)。默认情况下,阴影在元素外部(即外阴影)。

使用示例

以下是一个 box-shadow 的示例:

.box {box-shadow: 10px 5px 5px black;
}

在这个示例中,阴影向右移动10px,向下移动5px,模糊距离为5px,颜色为黑色。
其阴影效果如下图所示
在这里插入图片描述
你也可以指定多个阴影,如下所示:

.box {box-shadow: 3px 3px 5px #000, -1px -1px 5px red;
}

在这个示例中,定义了两段阴影,一个向下3px,向右3px的黑色阴影;一个向左1px,向上1px的红色阴影。其显示阴影效果如下:
在这里插入图片描述
以上阴影都是向外扩散的外阴影,加上inset属性后,阴影向内扩散。

box-shadow: 3px 3px 5px #000, -1px -1px 5px red inset;

其效果如下:
在这里插入图片描述
在 CSS 的 box-shadow 属性中,除了blur和spread,其他几个参数应该都很容易理解。这里重点研究下blur和spread参数。blur 和 spread 参数分别控制阴影的模糊程度和大小。

  1. blur:这个值定义了阴影的模糊程度。它的值是一个长度,表示模糊半径,即从阴影的边缘开始向外或向内延伸的距离。blur 的值越大,阴影的边缘就越模糊,阴影的扩散面积也就越大。如果 blur 的值为 0,那么阴影就是一个固定大小的矩形,没有模糊效果。

  2. spread:这个值定义了阴影的大小。它的值也是一个长度,表示阴影的扩展范围。正值会增加阴影的扩展范围,负值会减小阴影的扩展范围。如果 spread 的值为 0,那么阴影的大小就等于元素的大小。

以下是一个示例:

.box {box-shadow: 10px 10px 5px 3px rgba(0,0,0,0.75);
}

在这个示例中,阴影的模糊半径是 5px,扩展范围是 3px。这意味着阴影从边缘开始向外延伸 5px,过渡到完全透明,同时阴影的大小比元素的大小大 3px。
在这里插入图片描述
从实际展示的效果来看,这俩参数都影响了阴影的大小。但是观察实际的绘制过程,发现blur并不影响阴影矩形的大小,而spread是会实际影响阴影矩形的大小的。为什么这么说,下面可以通过Chrome的devtools中的layers来具体看下实际的绘制过程。

paint profiler

在Chrome中,可以通过"开发者工具"中的"Layers"标签来可视化页面的分层情况,并查看每个图层的clipRect。
具体步骤如下:
在Chrome中任意位置右键点击,选择"检查";
进入到开发者工具后,选择"Layers"标签;
在Layer页面,可以看到每个图层的平移、旋转、复位操作按钮,以及clipRect属性。

在 Google Chrome 开发者工具的 Paint Profiler 中,时间的顺序是由上至下的。也就是说,最早发生的绘制操作在顶部,最后发生的绘制操作在底部。

Paint Profiler 提供了一个详细的视图,显示了浏览器在绘制页面时的每一步操作。这可以帮助你理解页面的渲染过程,以及可能导致性能问题的绘制操作。

在 Paint Profiler 中,每一行代表一个绘制命令,例如填充矩形、绘制文本等。每个命令都有一个颜色条,表示该命令的执行时间。颜色条越长,执行时间就越长。

你可以点击每一行来查看该命令的详细信息,包括命令类型、参数、执行时间等。你还可以使用工具栏上的按钮来放大、缩小、选择和导航视图。

绘制过程

blur参数

不设偏移,也不设spread,查看blur半径对渲染的影响。

.box-shadow {position: fixed;left: 200px;top: 200px;width: 100px;height: 100px;background: yellow;box-shadow: 0 0 100px 0 red;}

一下是对应的paint profiler调用过程:
在这里插入图片描述

查看profiler,可以发现主要调用了以下4个方法。

drawPaint(paint)	//绘制底部白色画板
translate(150, 150)	//blur属性会影响这个偏移量
clipRect(1,1,99,99, "kDifference_Op",false) // 做差值区域裁剪
drawRect(0,0,100,100, paint) //red 绘制红色阴影矩形
drawRect(0,0,100, 100, paint)	//yellow	//绘制黄色区域

以上几个调用的方法,熟悉canvas的从命名大致能理解是用来干啥的,这里不具体研究每个方法。这些方法是Skia提供的,Skia 是一个开源的 2D 图形处理库,用于Chrome的图形引擎。有兴趣的可以去看Skia的文档,后面会给出具体的链接(估计是需要翻墙的)。
在这里插入图片描述

从绘制阴影矩形的参数可知,绘制的阴影矩形面积和实际的元素尺寸是一样的,都是100px,blur属性不影响阴影矩形的面积。为了有所对比,我们改变一下blur,将blur值设为原来的一般50px,再查看下绘制过程中发生了哪些变化。

.box-shadow {position: fixed;left: 200px;top: 200px;width: 100px;height: 100px;background: yellow;box-shadow: 0 0 50px 0 red;
}

在这里插入图片描述
新的绘制调用堆栈如下:

drawPaint(paint)
translate(75, 75)
clipRect(1,1,99,99, "kDifference_Op",false) 
drawRect(0, 0, 100, 100, paint)
drawRect(0, 0, 100, 100, paint)

这里可以看到,在第二行中的translate的入参发生了变化,而第四行绘制阴影矩形的尺寸并没有发生变化。所以我这里推论,blur参数并不影响阴影矩形的面积尺寸,但是会改变阴影开始模糊的位置。在layer中,可以看见图形外围的那个绿色框框。
在这里插入图片描述

spread

在上面的基础上,设置spread参数,查看spread对那些绘制产生印象。

.box-shadow {position: fixed;left: 200px;top: 200px;width: 100px;height: 100px;background: yellow;box-shadow: 0 0 50px 10px red;
}

在这里插入图片描述
这里面执行了以下序列函数,从中可以看出在第二步和第四步发生了变化,首先可以肯定的是,第二步translate的变化会对阴影模糊的效果产生影响,第四步对阴影矩形的大小和位置做了改变。对于阴影矩阵,可以很容易看出其相对于内容矩阵而言,分别向4个方向扩展了10px,也就是spread的大小。而translate的偏移量也相对的增加了10px。

drawPaint(paint)
translate(85, 85)//这里的偏移量发生了变化
clipRect(1,1,99,99,"kDifference_Op",false)
drawRect(-10,-10,110,110,paint)//这里阴影rect的尺寸和绘制起点都发生了变化
drawRect(0, 0,100,100, paint)

如果再在此基础上加横向和纵向偏移,会改变哪几行代码的执行呢?其实,根据上面的执行效果来看,可以很容易的推断出,首先阴影的尺寸大小应该不会发生变化,实际显示区域的内容也不会发生变化,也就是最后3行,理论上都不会变化。那么,第二行,变化就可能发生在第二行。

.box-shadow {position: fixed;left: 200px;top: 200px;width: 100px;height: 100px;background: yellow;box-shadow: 10px 10px 50px 10px red;//这里增加了x y向的偏移
}

修改成上面的代码,运行一下,实际的效果如下。可以很明显的看到,整体阴影向右下偏移。
在这里插入图片描述
外层绿色框框的区域是整个阴影绘制画板区域,横向纵向偏移可以理解为移动该区域。如何计算外层区域绘制的大小呢?
先忽略x轴和y轴偏移,计算外层矩形的位置。以下图为例,需计算出N点的位置。

 .box-shadow {position: fixed;left: 200px;top: 200px;width: 100px;height: 100px;background: yellow;box-shadow: 0 0 50px 10px red;
}

在上面这段代码中,可以看出,内层显示区域大小ABCD为100X100;由于有spread:10px,所以一外层HGFE的尺寸为110X110;blur半径为50px,所以从外层阴影边缘向外扩展50,此处为模糊区域;从模糊边缘在向外扩展模糊半径的一般即25,即是最外层边框的位置,整体距离内层显示区域距离85。左上角横向85,纵向85,即是外层画布的起点,由此可以确定整个外层边框区域。计算公式大致为:spreadWidth + blurWidth + blurWidth/2。
计算出外层区域后,之后的横向纵向偏移,在保持内部显示区域不懂,只需对整个大的外层进行平移即可。
在这里插入图片描述
以上都是针对外阴影的计算与绘制,内阴影则不适用。接下来可以看下内阴影的绘制过程

内阴影

设置以下内阴影,查看绘制过程如下

 .box-shadow {position: fixed;left: 200px;top: 200px;width: 100px;height: 100px;background: yellow;box-shadow: 0 0 50px 0 red inset;
}

从下面图中右侧顶部的绘制图层可以看出,最外层的绿色大边框画布与元素大小重叠。在整个的绘制过程中,执行了以下几个函数:

drawPaint(paint)
drawRect(0, 0, 100, 100, paint)	//黄色矩形框
clipRect(0,0,100,100, "kIntersect_Op", false)
drawDRRect(outer, inner, paint)

在这里插入图片描述
这里与外阴影不同的是,外阴影先绘制了阴影rect,后绘制的显示rect,内阴影则相反。模糊半径影响了哪里?毫无疑问,肯定是最后一个函数了。查看最后一个函数的入参可以看见,这里有些变化。
在这里插入图片描述
改变blur的大小,会影响outer的位置和大小;改变spread的大小,会影响inner的尺寸和位置。

.box {width: 100px;height: 100px;box-shadow: 0 0 50px 10px inset;
}

如上面的例子,元素大小100,设置spread为10。则inner的大小为90X90,由于内阴影向内扩展,所以inner的rect为{top: 10,left: 10,right: 90,bottom:90}
如果设置blur为50,则outer为元素向外扩展50,对应的rect为{top: -50,left: -50,right: 100+50,bottom: 100+50}
内阴影的x轴y轴偏移:

.box-shadow {position: fixed;left: 200px;top: 200px;width: 100px;height: 100px;background: yellow;box-shadow: 10px 10px 20px 10px red inset;
}

在这里插入图片描述

Skia

Skia是一个开源的2D图形库,它提供了可以在各种硬件和软件平台上工作的通用api。它是Google Chrome和ChromeOS、Android、Flutter、Mozilla Firefox和Firefox OS以及许多其他产品的图形引擎。官方地址:https://skia.org/docs/。这里只是简单列举了下渲染过程中出现的几个函数,有兴趣的再继续研究吧。

drawRect 绘制矩形函数
void SkCanvas::drawRect(const SkRect &rect,const SkPaint &paint )	
clipRect 矩形裁剪函数

在Chrome中,clipRect方法通常用于定义一个矩形区域,该区域将被裁剪或剪切,以只显示该区域内的内容。这个方法通常与Canvas API或SVG等图形绘制技术一起使用。

例如,在Canvas API中,clipRect方法可以用于裁剪一个矩形区域,以便在绘制其他图形或文本时只显示该区域内的内容。

clipRect(1,1,99,99,"kDifference_Op",false)

在这里插入图片描述

SkRegion::Op 是 Skia 中用于定义区域操作(例如合并、差集等)的枚举。kDifference_Op 是这个枚举的一个值,表示执行差集操作。

差集操作意味着从一个区域中减去另一个区域。如果两个区域有重叠的部分,那么重叠的部分将从结果区域中被移除。

简单地说,如果你有两个区域 A 和 B,并且对它们执行差集操作,那么结果区域将只包含那些在 A 中但不在 B 中的点。

drawDRRect

drawDRRect的作用有点类似于canvas中的drawDRRect方法,用于在画布上绘制一个带有圆角的矩形,并在其内部挖出一个小的带有圆角的矩形,形成一个“圆角矩形环”,在box-shadow绘制内阴影时会调用该函数。

以下是 drawDRRect 方法的基本用法:

void SkCanvas::drawDRRect(const SkRRect &outer,const SkRRect &inner,const SkPaint &paint)

总结

通过查看图层渲染的过程,可以很好的帮助我们理解box-shadow的绘制,以及如何设置blur和spread参数的值来达到想要的阴影效果。本文主要是想研究blur和spread在阴影绘制过程中如何影响最终的渲染效果的,只有知道这些,你才能知道如何合理的设置对应的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/326849.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Filament】基于物理的光照(PBR)

1 前言 自定义Blinn Phong光照模型中实现了基础的自定义光照,与现实的光照还是有些差别,本文将实现更逼真的光照效果,即基于物理的光照(PBR)。 读者如果对 Filament 不太熟悉,请回顾以下内容。 Filament环…

debug mccl 02 —— 环境搭建及初步调试

1, 搭建nccl 调试环境 下载 nccl 源代码 git clone --recursive https://github.com/NVIDIA/nccl.git 只debug host代码,故将设备代码的编译标志改成 -O3 (base) hipperhipper-G21:~/let_debug_nccl/nccl$ git diff diff --git a/makefiles/common.mk b/makefiles/…

scanf函数和printf函数

1.scanf函数 int scanf ( const char * format, ... );函数功能: 从键盘读取数据如果读取成功,返回读取到的数据个数如果读取失败,返回EOF 不常见的读取格式: %md -->读取m个宽度的数据 int main() {int n 0;scanf("%4d&…

安装阿里云CLI之配置阿里云凭证信息

有时候需要再主机上通过 OpenAPI 的调用访问阿里云,并完成控制,此时就需要在服务器上安装阿里云CLI,并完成账号的设置。 1. 登录阿里云创建账号 1.1 点击阿里云头像 ——》 控制访问 ——》创建一个拥有DNS权限的用户 这个用户不用太多权限…

ATTCK视角下的信息收集:主机发现

目录 1、利用协议主动探测主机存活 利用ICMP发现主机 利用ARP发现主机 利用NetBIOS协议发现主机 利用TCP/UDP发现主机 利用DNS协议发现主机 利用PRC协议发现主机程序 2、被动主机存活检测 利用Browser主机探测存活主机 利用ip段探测主机存活 利用net命令探测主机存活…

Redis实现订单超时自动关闭真的好吗,MQ更具性价比

由于Redis具有过期监听的功能,于是就有人拿它来实现订单超时自动关闭的功能,但是这个方案并不完美。今天来聊聊11种实现订单超时自动关闭的方案,总有一种适合你!这些方案并没有绝对的好坏之分,只是适用场景的不大相同。…

2024--Django平台开发-Web框架和Django基础(二)

day02 Web框架和Django基础 今日概要: 网络底层引入,到底什么是web框架?常见web框架对比django快速上手(创建网站)常见操作:虚拟环境、django项目、多app应用、纯净版逐点剖析:路由、视图、模…

【Linux 内核源码分析】关于Linux内核源码目录结构

Linux内核源码采用树形结构。功能相关的文件放到不同的子目录下面,使程序更具有可读行。 使用Source Insight打开源码,如下图所示,可以看到源码是树形结构。 目录含义描述arch存放与体系结构相关的代码,包括不同硬件平台的特定代…

聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化

聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化 目录 聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.聚类分析 | Matlab实现基于RIME-DBSCAN的数据聚类可视化(完整源码和数据) 2.多特征输入&…

Linux下从sqlite3源码编译出sqlite3库及相关可执行程序

目录 1. 下载sqlite3源码并编译 2. 下载Tcl库并编译 3. 再次编译sqlite源码 1. 下载sqlite3源码并编译 打开SQLite Download Page,滚动到页面的下面,找到源码量最大的那个(其它的估计也行,但源码最大的本人感觉功能最全&#…

【LeetCode-剑指offer】-- 25.两数相加II

25.两数相加II 方法:栈 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.ne…

【详解】求解迷宫所有路径(递归实现)----直接打穿迷宫

目录 递归的模型: 栈帧: 递归调用深度: ​编辑 用递归算法求解迷宫问题: 小结: 结语: 递归的小小总结,朋友们可以看看,有助于理解后面的递归程序。 递归的模型: …