Electron-electron-corner-smoothing:把 CSS 圆角磨出系统级平滑曲线的实现与用法
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
-electron-corner-smoothing是 Electron 专属的实验性 CSS 规则,用来在border-radius的圆角基础上进一步“磨平”曲率突变,得到类似 macOS SwiftUI 连续圆角(continuous corners)的视觉效果。本文以官方 API 文档为骨架,结合 Electron 仓库中的 Chromium 补丁、渲染端 C++ 实现与像素级测试用例,完整讲解该规则的语法、system-ui关键字的平台差异、从 CSS 声明到 Skia 路径的渲染管线,以及通过disableBlinkFeatures控制其可用性的方法。读完后你可以直接在自己的 Electron 应用里配置平滑圆角,并理解其背后的几何算法与实现边界。
规则定位:解决什么问题
普通border-radius生成的圆角由“直线边 + 四分之一圆弧”拼接而成,在边与圆弧的连接点处曲率会发生突变。对于重视与操作系统设计语言一致性的桌面应用来说,这种突变是一个容易被用户察觉的细节——macOS 的界面圆角实际采用的是曲率连续过渡的曲线,而 Electron 的网页渲染内容默认并不具备这种能力。
-electron-corner-smoothing正是为此设计的:它不改变圆角半径本身,而是调整圆角的“平滑度”(smoothness),让曲率在直线与圆弧之间缓慢过渡,效果类似 Apple SwiftUI 的连续圆角,也类似 Figma 中对设计元素提供的 “corner smoothing” 控制。该规则的作用对象与border-radius一致,并且会同时影响目标元素的**边框(borders)、外框线(outlines)与阴影(shadows)**的轮廓形状。与border-radius的行为类似,当元素尺寸过小、边长不足以容纳所选值时,平滑度会渐进地回退(back off)。
需要强调的是两点边界:该规则仅在 Electron 中实现,在浏览器中没有任何效果,避免在非 Electron 环境依赖它;同时它被明确视为实验性功能,未来若被正式 CSS 标准取代,可能需要迁移(官方文档中提到的迁移方向可参考上游corner-shape相关演进,见后文失效列表)。
形式化参考与基本语法
官方文档给出的形式化定义如下:
-electron-corner-smoothing = <percentage [0,100]> | system-ui| 属性 | 说明 |
|---|---|
| Initial value | 0% |
| Inherited | No |
| Animatable | No |
| Computed value | As specified |
也就是说:初始值为0%(不平滑);该属性不继承;不可参与 CSS 动画;计算值就是指定值本身。取值只有两种形式:0%–100%的百分比,或关键字system-ui。
以下示例展示不同平滑度百分比下的效果(文档原例):
.box { width: 128px; height: 128px; background-color: cornflowerblue; border-radius: 24px; -electron-corner-smoothing: var(--percent); /* Column header in table below. */ }| 0% | 100% |
|---|---|
(30%、60% 的中间状态见 corner-smoothing-example-30.svg 与 corner-smoothing-example-60.svg。)
非法取值的处理
从源码结构看,解析与渲染两端各有约束。CSS 解析层(补丁中的longhands_custom.cc)使用ConsumePercent并指定CSSPrimitiveValue::ValueRange::kNonNegative范围,因此负百分比(如-10%)在解析阶段即被判为非法声明,属性落回初始值;而200%这类超出[0,100]的百分比可以解析通过,但在渲染层被std::clamp(smoothness, 0.0f, 1.0f)钳制为100%(见 Chromium 补丁)。仓库测试夹具 spec/fixtures/api/corner-smoothing/shape/test.html 专门用200%、-10%、-200%三种“invalid”取值渲染了一整行样本,并以此对照参考图验证上述行为。
system-ui关键字:跟随系统设计语言
如果不想手动挑选百分比,可以使用system-ui关键字让平滑度自动匹配当前操作系统的圆角风格:
.box { width: 128px; height: 128px; background-color: cornflowerblue; border-radius: 24px; -electron-corner-smoothing: system-ui; /* Match the system UI design. */ }各平台上的解析结果如下:
| OS: | macOS | Windows, Linux |
|---|---|---|
| Value: | 60% | 0% |
| Example: |
这个平台差异可以直接在实现中得到印证。补丁在contoured_border_geometry.cc中新增了SmoothnessFromLength函数(补丁片段):
float SmoothnessFromLength(const Length& length) { // `none` = 0% if (length.IsNone()) { return 0.0f; } // `system-ui` keyword, represented internally as "auto" length if (length.HasAuto()) { #if BUILDFLAG(IS_MAC) return 0.6f; // macOS: 60% #else return 0.0f; // Windows / Linux: 0% #endif } return length.Percent() / 100.0f; }可以注意一个实现细节:system-ui在内部并不是独立存储的关键字,而是被编码为Length::Auto()——补丁注释里写得很直白:“To keep this patch small, Length is used instead of a more descriptive custom type”(为了让补丁更小,用Length代替了更具描述性的自定义类型)。因此system-ui最终被解析为Length::Auto()(见补丁中StyleBuilderConverter::ConvertCornerSmoothing的实现),序列化回 CSS 文本时再由CSSValueFromComputedStyleInternal还原为system-ui标识符。
渲染管线:从 CSS 声明到 Skia 路径
该功能通过一个完整的 Chromium 补丁落地(feat_corner_smoothing_css_rule_and_blink_painting.patch),补丁头部的提交说明将其拆为三块主要改动,理解这三块就能理解整条管线:
1. 注册 CSS 规则
规则元数据注册在blink/renderer/core/css/css_properties.json5中(补丁片段),关键配置包括:
property_methods:ParseSingleValue与CSSValueFromComputedStyleInternal——前者是自定义解析逻辑(先尝试system-ui标识符,再回退为百分比),后者负责从计算样式序列化出 CSS 值;type_name: "Length"、default_value: "Length::None()"、keywords: ["system-ui"];runtime_flag: "ElectronCSSCornerSmoothing"——规则可用性由该运行时特性开关控制(下文详述);invalidate: ["border-radius", "paint", "corner-shape"]——该属性被声明为与border-radius、paint以及上游corner-shape属性联动失效。这里可以看到 Electron 在为上游corner-shape标准预留了对齐关系,也解释了官方文档中“若被 CSS 标准取代可能需要迁移”的表述。
配套地,补丁还在css_property_equality.cc中补充了该属性的相等性比较(ElectronCornerSmoothing()逐字节比较),保证样式重算时的正确判定。
2. 改造 Blink 的圆角绘制路径
Blink 用ContouredRect描述带圆角的矩形。补丁为其CornerCurvature结构增加了一个smoothness_分量(默认 0),并让ComputeContouredBorderFromStyle在四角曲率之后追加SmoothnessFromLength(style.ElectronCornerSmoothing())作为输入;同时IsRound()的判定增加!IsSmooth()条件——也就是说只有平滑度为 0 的纯圆角才会走快速路径,平滑圆角会走新的分支。
PathBuilder::AddContouredRect在检测到IsSmooth()后,对半径做ConstrainRadii()(保证半径不超出盒模型),取每个角半径两个维度中的最小值(实现只支持单一半径,椭圆角半径会以最小维度值近似),然后调用 Electron 侧的实现:
builder_.addPath(electron::DrawSmoothRoundRect( box.x(), box.y(), box.width(), box.height(), smoothness, min_radius(radii.TopLeft()), min_radius(radii.TopRight()), min_radius(radii.BottomRight()), min_radius(radii.BottomLeft())));3. Electron 侧的几何算法
核心绘制函数是 shell/renderer/electron_smooth_round_rect.cc 中的DrawSmoothRoundRect,其接口声明在 shell/renderer/electron_smooth_round_rect.h。头文件注释给出了语义约定:
- smoothness 取值 0.0–1.0(对应 0%–100%),决定每个角可以“吃掉”多少边长,消耗量随该角半径缩放;
- 边长不足时平滑度会动态缩放回退,与圆角半径的回退机制类似;
- 每个角的半径可独立传入,且半径应已被平衡(每边上
Radius1 + Radius2 <= Length); - 椭圆角半径(elliptical radii)当前不受支持。
算法的几何思路在源码注释中有一段完整的 ASCII 图解(源码注释),其思路可概括为:
- 普通圆角中,直线边与四分之一圆弧的交点是曲率突变点。目标是在该交点两侧“拓出”一段额外空间,构造一条让曲率从直线平滑过渡到圆弧的曲线;
- 这段过渡曲线用两段三次贝塞尔曲线 + 中间一段圆弧实现。每个贝塞尔的四个控制点中,第一个锚定直线边、最后一个锚定圆弧,第三个由“边延长线与圆弧切线的交点”唯一确定,第二个则只受“落在边延长线上”的约束、可自由选取;
- 一个角消耗多少边长由
LengthForCornerSmoothness(smoothness, radius) = (1 + smoothness) * radius决定(源码),即平滑度从 0 到 1 时,角落消耗从1×radius增长到2×radius。
对于“同一条边上的两个角都要求平滑”的情况,函数ConstrainSmoothness(源码)负责约束:若两角的消耗总和超过边长,则按半径比例r1/(r1+r2)分配可用边长,反推出每个角各自的有效平滑度,并保证结果不小于 0。这正对应官方文档中“元素尺寸过小则平滑度渐进回退”的表述。
影响范围:边框、轮廓、阴影与裁剪
官方文档声明该规则“影响目标元素上边框、外框线与阴影的形状”。仓库的测试夹具 spec/fixtures/api/corner-smoothing/shape/test.html 用一整页样本覆盖了这些渲染路径,每个平滑度档位(0/30/60/100/非法值)都会渲染同一套 8 种元素:
- 纯背景圆角(
background-color的黑色方块); <img>图片的圆角裁切;- 实线 / 虚线 / 双线边框(
border-style: solid / dashed / double); overflow: clip子内容的圆角裁剪;box-shadow(带 offset、spread 的偏移阴影);- 四角不同半径的多个
border-radius值(border-radius: 0 0 r1 r2)。
这说明平滑轮廓不仅作用于背景填充,还贯穿了边框绘制、图片裁剪、阴影路径与内容裁剪等多条绘制分支——这与实现上“在ContouredRect这一共用几何描述中注入平滑度”的方案一致:所有以该轮廓为输入的路径构建都会自动获得平滑角。
控制可用性:disableBlinkFeatures
该规则由 Blink 运行时特性开关ElectronCSSCornerSmoothing控制。补丁在runtime_enabled_features.json5中注册了这一开关(补丁片段,状态为stable)。开发者可以按窗口粒度通过webPreferences.disableBlinkFeatures将其禁用(文档原例):
const myWindow = new BrowserWindow({ // [...] webPreferences: { disableBlinkFeatures: 'ElectronCSSCornerSmoothing' // Disables the `-electron-corner-smoothing` CSS rule } })禁用后该 CSS 规则不再可用,元素会按普通border-radius圆角渲染。这一机制同样被测试所依赖:测试用同一个页面分别以“可用 / 不可用”两种偏好创建窗口,并对照各自的参考截图断言结果(见下节)。
测试验证:像素级截图对比
该功能的回归测试是 spec/api-corner-smoothing-spec.ts,思路是“渲染页面 →webContents.capturePage()截图 → 与参考图做平均全局像素差比较”:
- 比较函数
compareImages计算两张位图逐像素 RGB 差的均值,容忍阈值COMPARISON_TOLERANCE = 2.5(注释说明实测匹配图差异约 1.3、不匹配图差异至少约 7.3); shape用例:加载 shape/test.html,分别以disableBlinkFeatures: 'ElectronCSSCornerSmoothing'(不可用)与未禁用(可用)两种方式创建 800×600 窗口,截图对照expected-false.png/expected-true.png;system-ui用例:加载 system-ui-keyword/test.html(页面并排渲染0%、system-ui、100%三个 256×256 圆角方块,border-radius: 48px),截图按process.platform对照expected-darwin.png、expected-win32.png、expected-linux.png——这也直接验证了上文SmoothnessFromLength的平台分支:darwin 参考图应呈现 60% 平滑形态,而 win32 与 linux 参考图应一致且等同于 0%。
测试失败时会把实际截图作为 artifact 输出(corner-rounding-expected-*.png),方便人工比对角部曲线差异,是排查渲染回归的实用入口。
限制与注意事项
- 仅 Electron 可用:浏览器中该规则无效,不要把它写进跨环境复用的样式代码;
- 实验性、可能迁移:官方文档明确其为 experimental,且失效列表中已声明与上游
corner-shape属性的关联(见 css_properties.json5 补丁配置),长期项目中建议将其收敛在独立样式文件中便于未来迁移; - 不可动画:
Animatable: No,不能通过 CSS transition/animation 平滑改变平滑度; - 椭圆角半径不受支持:
DrawSmoothRoundRect只接受单一半径(头文件声明),Blink 侧会以两维度半径的最小值近似传入; - 负值非法、超界钳制:负百分比解析失败落回
0%,超过 100% 的值渲染时被钳制为 100%; - 小尺寸回退:与
border-radius一样,边长不足时平滑度会按ConstrainSmoothness的比例规则自动收缩,无需额外处理。
延伸阅读
- 官方 API 文档:docs/api/corner-smoothing-css.md
- 实现补丁:patches/chromium/feat_corner_smoothing_css_rule_and_blink_painting.patch
- 几何绘制实现:shell/renderer/electron_smooth_round_rect.cc、shell/renderer/electron_smooth_round_rect.h
- 回归测试:spec/api-corner-smoothing-spec.ts、测试夹具 spec/fixtures/api/corner-smoothing/
【免费下载链接】electron:electron: Build cross-platform desktop apps with JavaScript, HTML, and CSS项目地址: https://gitcode.com/GitHub_Trending/el/electron
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考