現代 CSS 高階技巧:實現平滑內凹圓角的工程化實踐

通過 數學計算 + CSS mask 復合遮罩 實現的真正幾何內凹效果:

在這里插入圖片描述

背景是一張圖片,用來證明中間的凹陷是透明的。


完整代碼

app.js

import FormPage from "./pages/formPage";
import "./App.css";
const App = () => {return (<div className="box"><div className="block"></div></div>);
};export default App;

app.css


.box {background: url(../src/pages/img/10.jpg) 100%;height: 500px;
}.block {/* 增大圓角半徑 */--r: 6px;/* 適當增加斜切長度 */--s: 69px;/* 微調角度使過渡更自然 */--a: 44deg;/* 更柔和的邊緣過渡 */--_m: 0/calc(2*var(--r)) var(--r) no-repeat radial-gradient(50% 100% at bottom, #000 calc(100% - 0.8px), transparent);/* 自動計算的水平偏移 */--_d: (var(--s) + var(--r)) * cos(var(--a));width: 300px;height: 100px;background-color: antiquewhite;border-radius: 150px;mask:calc(50% + var(--_d)) var(--_m), calc(50% - var(--_d)) var(--_m),radial-gradient(var(--s) at 50% calc(-1*sin(var(--a))*var(--s)),transparent 100%, #000 calc(100% + 0.8px)) 0 calc(var(--r)*(1 - sin(var(--a)))),linear-gradient(90deg, #000 calc(50% - var(--_d)), transparent 0 calc(50% + var(--_d)), #000 0);mask-repeat: no-repeat;
}

整體思路

這段代碼的核心思想是:

使用多個 mask 圖層組合,通過 radial-gradient 和 linear-gradient 的疊加,形成一個“中間凹進去、邊緣過渡柔和”的視覺效果。

它利用了 CSS 中的變量(--r, --s, --a)、三角函數和 mask 屬性,實現了動態可配置的內凹圓角效果。


變量解析(CSS Custom Properties)

--r: 6px;        /* 凹陷區域的半徑(控制凹陷大小) */
--s: 69px;       /* 圓弧的半徑(控制凹陷的位置) */
--a: 44deg;      /* 角度(用于三角函數計算) */

這些變量可以方便地調整最終的視覺效果。


計算變量(關鍵邏輯)

--_m:定義一個遮罩圖層(更柔和的邊緣過渡)

--_m: 0 / calc(2*var(--r)) var(--r) no-repeatradial-gradient(50% 100% at bottom, #000 calc(100% - 0.8px), transparent);
  • 創建一個寬度為 2 * --r,高度為 --r 的徑向漸變。
  • 漸變從底部開始,顏色從不透明到透明,形成一個邊緣模糊的遮罩條帶
  • 這個條帶會在最終 mask 中作為“邊緣柔化”層使用。

--_d:水平偏移距離(基于三角函數自動計算)

--_d: (var(--s) + var(--r)) * cos(var(--a));
  • 使用余弦函數計算出一個水平方向上的偏移值。
  • 這個偏移值決定了凹陷區域在水平方向上的位置。
  • 結合下面的 mask 設置,使凹陷區域對稱分布在中心兩側。

Mask 圖層詳解(這是整個效果的關鍵)

mask:calc(50% + var(--_d)) var(--_m),calc(50% - var(--_d)) var(--_m),radial-gradient(var(--s) at 50% calc(-1*sin(var(--a))*var(--s)),transparent 100%, #000 calc(100% + 0.8px)) 0 calc(var(--r)*(1 - sin(var(--a)))),linear-gradient(90deg, #000 calc(50% - var(--_d)), transparent 0 calc(50% + var(--_d)), #000 0);

我們來逐行拆解這四個 mask 圖層:


🔹 第一層 & 第二層(邊緣柔化層)

calc(50% + var(--_d)) var(--_m),
calc(50% - var(--_d)) var(--_m)
  • 這兩個圖層使用的是之前定義好的 --_m 遮罩條帶。
  • 分別放置在中心左右各偏移 --_d 的位置。
  • 作用是柔和邊緣,避免生硬的裁剪邊界。

🔸 第三層(核心凹陷層)

radial-gradient(var(--s) at 50% calc(-1*sin(var(--a))*var(--s)),transparent 100%, #000 calc(100% + 0.8px))0 calc(var(--r)*(1 - sin(var(--a))));
  • 創建一個以中心為圓心、向上偏移一定距離的徑向漸變。
  • 半徑為 --s,位于垂直方向上偏移 sin(a) * s
  • 漸變從透明到黑色,超出部分變為不透明。
  • 最后定位在 0 calc(...),即垂直方向向下偏移一點,讓凹陷區域更貼合整體形狀。

? 這一層是形成“內凹”視覺的核心圖層


🔷 第四層(背景遮罩層)

linear-gradient(90deg, #000 calc(50% - var(--_d)), transparent 0 calc(50% + var(--_d)), #000 0)
  • 水平方向的線性漸變:

    • 左側和右側為黑色(顯示區域)
    • 中間一段為透明(隱藏區域)
  • 作用是遮住中間的凹陷區域,只保留兩邊的內容。


最終效果總結

圖層作用
第一、二層邊緣柔化處理(避免鋸齒感)
第三層核心凹陷區域(模擬“內凹”形狀)
第四層背景遮罩(只顯示兩邊,中間隱藏)

結合起來就形成了一個中間凹陷、邊緣柔和、對稱分布的視覺效果,非常適合用在按鈕、卡片等需要輕微凹陷質感的 UI 元素中。


示例效果預覽(文字描述)

想象一個橢圓形的盒子(border-radius: 150px),原本是完整的圓形。但在它的正中央,有一個向上彎曲的凹陷區域,就像輕輕按下按鈕時那種感覺,邊緣還有輕微的陰影過渡。

這種效果常見于 macOS 的菜單欄按鈕、iOS 控件等現代 UI 設計中。


如何調整?

你可以通過修改以下變量來實時調整視覺效果:

變量默認值調整建議影響
--r6px增大 → 凹陷更大凹陷區域大小
--s69px增大 → 凹陷更深凹陷位置與深度
--a44deg增大 → 凹陷更靠上凹陷角度與位置
background-colorantiquewhite更淺/深色整體對比度和質感

在小程序中實現實現平滑內凹圓角


在支付寶小程序中使用 canvas 實現帶內凹圓角矩形

小程序的結構:

<view class="mask-layer"><canvas style="width:100%;height:100%" id="canvas" type="2d" onReady="onCanvasReady"></canvas>
</view>

小程序的canvas邏輯:


Page({onCanvasReady() {const systemInfo = my.getSystemInfoSync();const screenWidth = systemInfo.windowWidth;my.createSelectorQuery().select('#canvas').node().exec((res) => {const canvas = res[0].node;if (!canvas || !canvas.getContext) return;const ctx = canvas.getContext('2d');// 設置 canvas 像素尺寸(避免模糊)canvas.width = screenWidth;canvas.height = 200;// 動態計算參數let width = screenWidth * 0.9;let x = (screenWidth - width) / 2; // 居中顯示let y = 0;let height = 100;let radius = Math.min(width, height) / 2;let indentationWidth = width * 0.35;let indentationDepth = 15;// 設置顏色ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';// 調用繪制函數this.drawRoundedIndentedRect(ctx, x, y, width, height, radius, indentationWidth, indentationDepth);});},drawRoundedIndentedRect(ctx, x, y, width, height, radius, indentationWidth, indentationDepth) {ctx.beginPath();ctx.moveTo(x + radius, y);ctx.lineTo(x + width / 2 - indentationWidth / 2, y);ctx.bezierCurveTo(x + width / 2 - indentationWidth / 4, y,x + width / 2 - indentationWidth / 4, y + indentationDepth,x + width / 2, y + indentationDepth);ctx.bezierCurveTo(x + width / 2 + indentationWidth / 4, y + indentationDepth,x + width / 2 + indentationWidth / 4, y,x + width / 2 + indentationWidth / 2, y);ctx.lineTo(x + width - radius, y);ctx.quadraticCurveTo(x + width, y, x + width, y + radius);ctx.lineTo(x + width, y + height - radius);ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);ctx.lineTo(x + radius, y + height);ctx.quadraticCurveTo(x, y + height, x, y + height - radius);ctx.lineTo(x, y + radius);ctx.quadraticCurveTo(x, y, x + radius, y);ctx.closePath();ctx.fill();}
});

在 css 中使用 canvas 實現平滑內凹圓角

直接展示源碼了,和上邊小程的差不多

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Canvas - 帶凹陷的圓角矩形</title><style>canvas {display: block;margin: 40px auto;box-shadow: 0px 0px 5px #ccc;border-radius: 8px;background: url("./截屏2025-05-28 11.15.50.png") no-repeat center center;background-size: cover;}</style>
</head><body><canvas id="canvas" width="500" height="200">當前瀏覽器不支持canvas元素,請升級或更換瀏覽器!</canvas><script>function draw() {const canvas = document.getElementById('canvas');if (!canvas || !canvas.getContext) return;const ctx = canvas.getContext('2d');// 動態設置畫布大小canvas.width = window.innerWidth * 0.9;canvas.height = 150;ctx.clearRect(0, 0, canvas.width, canvas.height);let width = canvas.width * 0.9;let height = 80;let x = (canvas.width - width) / 2;let y = 40;let radius = Math.min(width, height) / 2;let indentationWidth = width * 0.7;let indentationDepth = 20;ctx.fillStyle = '#bfc';drawRoundedIndentedRect(ctx, x, y, width, height, radius, indentationWidth, indentationDepth);}function drawRoundedIndentedRect(ctx, x, y, width, height, radius, indentationWidth, indentationDepth) {ctx.beginPath();ctx.moveTo(x + radius, y);ctx.lineTo(x + width / 2 - indentationWidth / 2, y);ctx.bezierCurveTo(x + width / 2 - indentationWidth / 4, y,x + width / 2 - indentationWidth / 4, y + indentationDepth,x + width / 2, y + indentationDepth);ctx.bezierCurveTo(x + width / 2 + indentationWidth / 4, y + indentationDepth,x + width / 2 + indentationWidth / 4, y,x + width / 2 + indentationWidth / 2, y);ctx.lineTo(x + width - radius, y);ctx.quadraticCurveTo(x + width, y, x + width, y + radius);ctx.lineTo(x + width, y + height - radius);ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);ctx.lineTo(x + radius, y + height);ctx.quadraticCurveTo(x, y + height, x, y + height - radius);ctx.lineTo(x, y + radius);ctx.quadraticCurveTo(x, y, x + radius, y);ctx.closePath();ctx.fill();}window.addEventListener('load', draw);window.addEventListener('resize', draw);</script>
</body></html>

嘿嘿!
簡單點,直接找UI 要背景圖,直接用就好 !!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83208.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83208.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83208.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Qt不同布局添加不同控件

對于這種 不同布局添加不同控件 的情況,可以采用以下幾種簡化方法: 方法 1:使用 std::pair 或 std::tuple 配對(C++17 推薦) for (auto [layout, widget] : {std::pair{m_layoutMistakeCalibrate,

MySQL 事務解析

1. 事務簡介 事務&#xff08;Transaction&#xff09; 是一組操作的集合&#xff0c;它是一個不可分割的工作單位&#xff0c;事務會把所有的操作作為一個整體一起向系統提交或撤銷操作請求&#xff0c;即這些操作要么同時成功&#xff0c;要么同時失敗。 經典案例&#xff1…

PyTorch中 torch.utils.data.DataLoader 的詳細解析和讀取點云數據示例

一、DataLoader 是什么&#xff1f; torch.utils.data.DataLoader 是 PyTorch 中用于加載數據的核心接口&#xff0c;它支持&#xff1a; 批量讀取&#xff08;batch&#xff09;數據打亂&#xff08;shuffle&#xff09;多線程并行加載&#xff08;num_workers&#xff09;自…

在MDK中自動部署LVGL,在stm32f407ZGT6移植LVGL-8.4,運行demo,顯示label

在MDK中自動部署LVGL&#xff0c;在stm32f407ZGT6移植LVGL-8.4 一、硬件平臺二、實現功能三、移植步驟1、下載LVGL-8.42、MDK中安裝LVGL-8.43、配置RTE4、配置頭文件 lv_conf_cmsis.h5、配置lv_port_disp_template 四、添加心跳相關文件1、在STM32CubeMX中配置TIM7的參數2、使能…

德思特新聞 | 德思特與es:saar正式建立合作伙伴關系

德思特新聞 2025年5月9日&#xff0c;德思特科技有限公司&#xff08;以下簡稱“德思特”&#xff09;與德國嵌入式系統專家es:saar GmbH正式達成合作伙伴關系。此次合作旨在將 es:saar 的先進嵌入式開發與測試工具引入中國及亞太市場&#xff0c;助力本地客戶提升產品開發效率…

fork函數小解

學了好久終于搞懂fork函數的一些作用 1. fork函數作用&#xff1a;用于創建新的子進程 這是fork最根本的功能&#xff0c;在父進程里創建新的子進程、 但是創建新的子進程之后呢&#xff1f; 子進程和父進程的關系是什么樣的&#xff1f; 為什么fork得到的子進程返回值為0&am…

opencv(C++) 變換圖像與形態學操作

文章目錄 使用腐蝕和膨脹圖像形態濾波器實現案例使用形態學濾波器對圖像進行開運算和閉運算實現案例在灰度圖像上應用形態學操作算子形態學梯度(Morphological Gradient)黑帽變換(Black-hat Transform)使用分水嶺算法進行圖像分割使用 MSER 提取顯著區域MSER 檢測與可視化使…

測試工程師學LangChain之promptTemplate 實戰筆記

一、引言:大模型時代的測試自動化革命 2025 年,隨著大模型(如 DeepSeek)在自動化測試領域的廣泛應用,Prompt 編寫已成為測試工程師的核心技能之一。 為什么? 大模型輸出的質量 90% 取決于輸入的 PromptLangChain 的 PromptTemplate 提供了參數化 Prompt 的標準化方案Ope…

CP2K 軟件介紹與使用指南

CP2K 軟件介紹與使用指南 一、CP2K簡介 CP2K是一款開源的量子化學和固態物理模擬軟件包&#xff0c;主要用于原子尺度模擬&#xff0c;特別擅長以下領域&#xff1a; 第一性原理計算&#xff1a;基于密度泛函理論(DFT)的電子結構計算分子動力學(MD)&#xff1a;包括從頭算分…

npm、pnpm、yarn使用以及區別

npm 使用 安裝包&#xff1a;在項目目錄下&#xff0c;npm install <包名> 用于本地安裝包到 node_modules 目錄&#xff0c;并添加到 package.json 的 dependencies 中&#xff1b;npm install -g <包名> 用于全局安裝&#xff0c;適用于命令行工具等。初始化項目…

2025年北京市職工職業技能大賽第六屆信息通信行業網絡安全技能大賽復賽CTF部分WP-哥斯拉流量分析

2025年北京市職工職業技能大賽第六屆信息通信行業網絡安全技能大賽復賽CTF部分WP-哥斯拉流量分析 一、流量分析 題目沒有任何提示,附件gzl.pcap 解題哥斯拉流量300多KB包很多,沒啥經驗只能挨個看回來之后又狠狠得擼了一把哥斯拉流量分析我這里用的是哥斯拉4.0.1 測試鏈接…

GitLab 18.0 正式發布,15.0 將不再受技術支持,須升級【六】

GitLab 是一個全球知名的一體化 DevOps 平臺&#xff0c;很多人都通過私有化部署 GitLab 來進行源代碼托管。極狐GitLab 是 GitLab 在中國的發行版&#xff0c;專門為中國程序員服務。可以一鍵式部署極狐GitLab。 學習極狐GitLab 的相關資料&#xff1a; 極狐GitLab 官網極狐…

React 項目中封裝 Excel 導入導出組件:技術分享與實踐

文章目錄 前言一、為什么需要封裝 Excel 組件&#xff1f;二、技術選型三、核心實現1. 安裝依賴2. 封裝Excel導出3. 封裝導入組件 &#xff08;UploadExcel&#xff09; 總結 前言 在 React 項目中&#xff0c;處理 Excel 文件的導入和導出是常見的業務需求。無論是導出報表數…

RustDesk 搭建自建服務器并設置服務自啟動

目錄 0. 介紹 1. 事前準備 1.1 有公網 ip 的云服務器一臺 1.2 服務端部署包 1.3 客戶端安裝包 2. 部署 2.1 服務器環境準備 2.2 上傳服務端部署包 2.3 運行 pm2 3. 客戶端使用 3.1 安裝 3.2 配置 3.2.1 解鎖網絡設置 3.2.2 ID / 中級服務器 3.3 啟動效果 > …

基于Qt封裝數據庫基本增刪改查操作,支持多線程,并實現SQLite數據庫單例訪問

抽出來的&#xff0c;直接用就行 頭文件CPP文件使用示例 頭文件 #ifndef DATABASECOMMON_H #define DATABASECOMMON_H/** 單例封裝SQLite通用操作&#xff0c;支持多線程調用&#xff1b;可擴展兼容其他數據庫&#xff0c;照著SysRunDatabase寫&#xff0c;并且重載openDataba…

AI筆記 - 網絡模型 - mobileNet

網絡模型 mobileNet mobileNet V1網絡結構深度可分離卷積空間可分![在這里插入圖片描述](https://i-blog.csdnimg.cn/direct/aff06377feac40b787cfc882be7c6e5d.png) 參考 mobileNet V1 網絡結構 MobileNetV1可以理解為VGG中的標準卷積層換成深度可分離卷積 可分離卷積主要有…

第十五篇:MySQL 高級實戰項目:構建高可用、可觀測、性能優化一體化數據庫平臺

本篇聚焦于如何基于 MySQL 構建一個真正面向生產環境的數據庫平臺&#xff0c;集成高可用、可觀測與性能調優三大核心能力&#xff0c;助力穩定、可擴展的系統運行。 一、項目背景與目標 在實際生產環境中&#xff0c;數據庫系統需要應對以下挑戰&#xff1a; 業務高速增長帶來…

華為OD機試真題——文件目錄大小(2025 A卷:100分)Java/python/JavaScript/C++/C語言/GO六種語言最佳實現

2025 A卷 100分 題型 本文涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、測試用例以及綜合分析; 并提供Java、python、JavaScript、C++、C語言、GO六種語言的最佳實現方式! 2025華為OD真題目錄+全流程解析/備考攻略/經驗分享 華為OD機試真題《文件目錄大小》: 目錄 題…

qwen 2.5 并行計算機制:依靠 PyTorch 和 Transformers 庫的分布式能力

qwen 2.5 并行計算機制:依靠 PyTorch 和 Transformers 庫的分布式能力 完整可運行代碼: import torch import torch.nn.functional as F from transformers

TIDB創建索引失敗 mkdir /tmp/tidb/tmp_ddl-4000/1370: no such file or directory.

TIDB創建索引失敗&#xff1a;解決“mkdir /tmp/tidb/tmp_ddl-4000/1370: no such file or directory”問題 在使用 TIDB 數據庫時&#xff0c;我們有時會遇到創建索引失敗的問題。常見的錯誤信息為&#xff1a; mkdir /tmp/tidb/tmp_ddl-4000/1370: no such file or directo…