成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

現代 CSS 高階技巧,不規則邊框解決方案

開發 前端
在這一篇中,我們將繼續探索,嘗試使用 CSS Painting API,去實現過往 CSS 中非常難以實現的一個點,那就是如何繪制不規則圖形的邊框。

本文是 CSS Houdini 之 CSS Painting API 系列第四篇。

  • 現代 CSS 之高階圖片漸隱消失術[1]
  • 現代 CSS 高階技巧,像 Canvas 一樣自由繪圖構建樣式![2]
  • 現代 CSS 高階技巧,完美的波浪進度條效果![3]

在上三篇中,我們詳細介紹了 CSS Painting API 是如何一步一步,實現自定義圖案甚至實現動畫效果的!

在這一篇中,我們將繼續探索,嘗試使用 CSS Painting API,去實現過往 CSS 中非常難以實現的一個點,那就是如何繪制不規則圖形的邊框。

CSS Painting API

再簡單快速的過一下,什么是 CSS Painting API。

CSS Painting API 是 CSS Houdini 的一部分。而 Houdini 是一組底層 API,它們公開了 CSS 引擎的各個部分,從而使開發人員能夠通過加入瀏覽器渲染引擎的樣式和布局過程來擴展 CSS。Houdini 是一組 API,它們使開發人員可以直接訪問 CSS 對象模型[4] (CSSOM),使開發人員可以編寫瀏覽器可以解析為 CSS 的代碼,從而創建新的 CSS 功能,而無需等待它們在瀏覽器中本地實現。

CSS Paint API 目前的版本是 CSS Painting API Level 1[5]。它也被稱為 CSS Custom Paint 或者 Houdini's Paint Worklet。

我們可以把它理解為 JS In CSS,利用 JavaScript Canvas 畫布的強大能力,實現過往 CSS 無法實現的功能。

過往 CSS 實現不規則圖形的邊框方式

CSS 實現不規則圖形的邊框,一直是 CSS 的一個難點之一。在過往,雖然我們有很多方式利用 Hack 出不規則圖形的邊框,我在之前的多篇文章中有反復提及過:

我們來看看這樣一個圖形:

圖片

利用 CSS 實現這樣一個圖形是相對簡單的,可以利用 mask 或者 background 中的漸變實現,像是這樣:

<div class="arrow-button"></div>
.arrow-button {
position: relative;
width: 180px;
height: 64px;
background: #f49714;

&::after {
content: "";
position: absolute;
width: 32px;
height: 64px;
top: 0;
right: -32px;
background:
linear-gradient(-45deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%),
linear-gradient(-135deg, transparent 0, transparent 22px, #f49714 22px, #f49714 100%);
background-size: 32px 32px;
background-repeat: no-repeat;
background-position: 0 bottom, 0 top;
}
}

但是,如果,要實現這個圖形,但是只有一層邊框,利用 CSS 就不那么好實現了,像是這樣:

圖片

在過往,有兩種相對還不錯的方式,去實現這樣一個不規則圖形的邊框:

  1. 借助 filter,利用多重drop-shadow()。
  2. 借助 SVG 濾鏡實現。

我們快速回顧一下這兩個方法。

借助 filter,利用多重 drop-shadow() 實現不規則邊框

還是上面的圖形,我們利用多重 drop-shadow(),可以大致的得到它的邊框效果。代碼如下:

div {
position: relative;
width: 180px;
height: 64px;
background: #fff;

&::after {
content: "";
position: absolute;
width: 32px;
height: 64px;
top: 0;
right: -32px;
background:
linear-gradient(-45deg, transparent 0, transparent 22px, #fff 22px, #fff 100%),
linear-gradient(-135deg, transparent 0, transparent 22px, #fff 22px, #fff 100%);
background-size: 32px 32px;
background-repeat: no-repeat;
background-position: 0 bottom, 0 top;
}
}
div {
filter:
drop-shadow(0px 0px .5px #000)
drop-shadow(0px 0px .5px #000)
drop-shadow(0px 0px .5px #000);
}

可以看到,這里我們通過疊加 3 層 drop-shadow(),來實現不規則圖形的邊框,雖然 drop-shadow() 是用于生成陰影的,但是多層值很小的陰影疊加下,竟然有了類似于邊框的效果:

圖片

借助 SVG 濾鏡實現實現不規則邊框

另外一種方式,需要掌握比較深的 SVG 濾鏡知識。通過實現一種特殊的 SVG 濾鏡,再通過 CSS 的 filter 引入,實現不規則邊框。

看看代碼:

<div></div>

<svg width="0" height="0">
<filter id="outline">
<feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="1"></feMorphology>
<feMerge>
<feMergeNode in="DILATED" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</svg>
div {
position: relative;
width: 180px;
height: 64px;
background: #fff;

&::after {
content: "";
position: absolute;
width: 32px;
height: 64px;
top: 0;
right: -32px;
background:
linear-gradient(-45deg, transparent 0, transparent 22px, #fff 22px, #fff 100%),
linear-gradient(-135deg, transparent 0, transparent 22px, #fff 22px, #fff 100%);
background-size: 32px 32px;
background-repeat: no-repeat;
background-position: 0 bottom, 0 top;
}
}
div {
filter: url(#outline);
}

簡單淺析一下這段 SVG 濾鏡代碼:

  1. <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="1"></feMorphology> 將原圖的不透明部分作為輸入,采用了 dilate 擴張模式且程度為 radius="1",生成了一個比原圖大 1px 的黑色圖塊。
  2. 使用 feMerge 將黑色圖塊和原圖疊加在一起。
  3. 可以通過控制濾鏡中的 radius="1" 來控制邊框的大小。

這樣,也可以實現不規則圖形的邊框效果:

圖片

CodePen Demo -- 3 ways to achieve unregular border[8]。

利用 CSS Painting API 實現不規則邊框

那么,到了今天,利用 CSS Painting API ,我們有了一種更為直接的方式,更好的解決這個問題。

還是上面的圖形,我們利用 clip-path 來實現一下。

<div></div>
div {
position: relative;
width: 200px;
height: 64px;
background: #f49714;
clip-path: polygon(85% 0%, 100% 50%, 85% 100%, 0% 100%, 0% 0%;);
}

我們可以得到這樣一個圖形:

圖片

當然,本文的主角是 CSS Painting API,既然我們有 clip-path 的參數,其實完全也可以利用 CSS Painting API 的 borderDraw 來繪制這個圖形。

我們嘗試一下,改造我們的代碼:

<div></div>
<script>
if (CSS.paintWorklet) {
CSS.paintWorklet.addModule('/CSSHoudini.js');
}
</script>
div {
position: relative;
width: 200px;
height: 64px;
background: paint(borderDraw);
--clipPath: 85% 0%, 100% 50%, 85% 100%, 0% 100%, 0% 0%;);
}

這里,我們將原本的 clip-path 的具體路徑參數,定義為了一個 CSS 變量 --clipPath,傳入我們要實現的 borderDraw 方法中。整個圖形效果,就是要利用 background: paint(borderDraw) 繪制出來。

接下來,看看,我們需要實現 borderDraw。核心的點在于,我們通過拿到 --clipPath 參數,解析它,然后通過循環函數利用畫布把這個圖形繪制出來。

// CSSHoudini.js 文件
registerPaint(
"borderDraw",
class {
static get inputProperties() {
return ["--clipPath"];
}

paint(ctx, size, properties) {
const { width, height } = size;
const clipPath = properties.get("--clipPath");
const paths = clipPath.toString().split(",");
const parseClipPath = function (obj) {
const x = obj[0];
const y = obj[1];
let fx = 0,
fy = 0;
if (x.indexOf("%") > -1) {
fx = (parseFloat(x) / 100) * width;
} else if (x.indexOf("px") > -1) {
fx = parseFloat(x);
}
if (y.indexOf("%") > -1) {
fy = (parseFloat(y) / 100) * height;
} else if (y.indexOf("px") > -1) {
fy = parseFloat(y);
}
return [fx, fy];
};

var p = parseClipPath(paths[0].trim().split(" "));
ctx.beginPath();
ctx.moveTo(p[0], p[1]);
for (var i = 1; i < paths.length; i++) {
p = parseClipPath(paths[i].trim().split(" "));
ctx.lineTo(p[0], p[1]);
}
ctx.closePath();
ctx.fill();
}
}
);

簡單解釋一下上述的代碼,注意其中最難理解的 parseClipPath() 方法的解釋。

  1. 首先我們,通過properties.get("--clipPath"),我們能夠拿到傳入的 --clipPath 參數。
  2. 通過spilt() 方法,將 --clipPath 分成一段段,也就是我們的圖形實際的繪制步驟。
  3. 這里有一點非常重要,也就是parseClipPath() 方法,由于我們的 -clipPath 的每一段可能是 100% 50% 這樣的構造,但是實際在繪圖的過程中,我們需要的實際坐標的絕對值,譬如在一個 100 x 100 的畫布上,我們需要將 50% 50% 的百分比坐標,轉化為實際的 50 50 這樣的絕對值。
  4. 在理解了parseClipPath() 后,剩下的就都非常好理解了,我們通過 ctx.beginPath()、ctx.move、ctx.lineTo 以及 ctx.closePath() 將整個 --clipPath 的圖形繪制出來。
  5. 最后,利用ctx.fill() 給圖形上色。

這樣,我們就得到了這樣一個圖形:

圖片

都拿到了完整的圖形了,那么我們只給這個圖形繪制邊框,不上色,不就得到了它的邊框效果了嗎?

簡單改造一些 JavaScript 代碼的最后部分:

// CSSHoudini.js 文件
registerPaint(
"borderDraw",
class {
static get inputProperties() {
return ["--clipPath"];
}
paint(ctx, size, properties) {
// ...
ctx.closePath();
// ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = "#000";
ctx.stroke();
}
}
);

這樣,我們就得到了圖形的邊框效果:

圖片

僅僅利用 background 繪制的缺陷

但是,僅僅利用 [bacg](background: paint(borderDraw "bacg")) 來繪制邊框效果,會有一些問題。

上述的圖形,我們僅僅賦予了 1px 的邊框,如果我們把邊框改成 5px 呢?看看會發生什么?

// CSSHoudini.js 文件
registerPaint(
"borderDraw",
class {
static get inputProperties() {
return ["--clipPath"];
}
paint(ctx, size, properties) {
// ...
ctx.lineWidth = 5;
ctx.strokeStyle = "#000";
ctx.stroke();
}
}
);

此時,整個圖形會變成:

圖片

可以看到,沒有展示完整的 5px 的邊框,這是由于整個畫布只有元素的高寬大小,而上述的代碼中,元素的邊框有一部分繪制到了畫布之外,因此,整個圖形并非我們期待的效果。

因此,我們需要換一種思路解決這個問題,繼續改造一下我們的代碼,僅僅需要改造 CSS 代碼即可:

div {
position: relative;
width: 200px;
height: 64px;
margin: auto;
clip-path: polygon(var(--clipPath));
--clipPath: 85% 0%, 100% 50%, 85% 100%, 0% 100%, 0% 0%;

&::before {
content:"";
position:absolute;
inset: 0;
mask: paint(borderDraw);
background: #000;
}
}

這里,我們的元素本身,還是利用了 clip-path: polygon(var(--clipPath)) 剪切了自身,同時,我們借助了一個偽元素,利用這個偽元素去實現具體的邊框效果。

這里其實用了一種內外切割的思想,去實現的邊框效果:

  1. 利用父元素的clip-path: polygon(var(--clipPath)) 剪切掉外圍的圖形。
  2. 利用給偽元素的 mask 作用實際的paint(borderDraw) 方法,把圖形的內部鏤空,只保留邊框部分。

還是設置 ctx.lineWidth = 5,再看看效果:

圖片

看上去不錯,但是實際上,雖然設置了 5px 的邊框寬度,但是實際上,上圖的邊框寬度只有 2.5px 的,這是由于另外一點一半邊框實際上被切割掉了。

因此,我們如果需要實現 5px 的效果,實際上需要 ctx.lineWidth =10。

當然,我們可以通過一個 CSS 變量來控制邊框的大小:

div {
position: relative;
width: 200px;
height: 64px;
margin: auto;
clip-path: polygon(var(--clipPath));
--clipPath: 85% 0%, 100% 50%, 85% 100%, 0% 100%, 0% 0%;
--borderWidth: 5;

&::before {
content:"";
position:absolute;
inset: 0;
mask: paint(borderDraw);
background: #000;
}
}

在實際的 borderDraw 函數中,我們將傳入的 --borderWidth 參數,乘以 2 使用就好:


registerPaint(
"borderDraw",
class {
static get inputProperties() {
return ["--clipPath", "--borderWidth"];
}
paint(ctx, size, properties) {
const borderWidth = properties.get("--borderWidth");
// ...
ctx.lineWidth = borderWidth * 2;
ctx.strokeStyle = "#000";
ctx.stroke();
}
}
);

這樣,我們每次都能得到我們想要的邊框長度:

圖片

CodePen Demo -- CSS Hudini & Unregular Custom Border[9]。

到這里,整個實現就完成了,整個過程其實有多處非常關鍵的點,會有一點點難以理解,具體可能需要自己實際調試一遍找到實現的原理。

具體應用

在掌握了上述的方法后,我們就可以利用這個方式,實現各類不規則圖形的邊框效果,我們只需要傳入對于的 ??clip-path?? 參數以及我們想要的邊框長度即可。

好,這樣,我們就能實現各類不同的不規則圖形的邊框效果了。

像是這樣:

div {
position: relative;
width: 200px;
height: 200px;
clip-path: polygon(var(--clipPath));
--clipPath: 0% 15%, 15% 15%, 15% 0%, 85% 0%, 85% 15%, 100% 15%, 100% 85%, 85% 85%, 85% 100%, 15% 100%, 15% 85%, 0% 85%;
--borderWidrh: 1;
--color: #000;

&::before {
content:"";
position:absolute;
inset: 0;
mask: paint(borderDraw);
background: var(--color);
}
}

div:nth-child(2) {
--clipPath: 50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%;
--borderWidrh: 2;
--color: #ffcc00;
}
div:nth-child(3) {
--clipPath: 90% 58%90% 58%, 69% 51%, 69% 51%, 50% 21%, 50% 21%, 39% 39%, 39% 39%, 15% 26%, 15% 26%, 15% 55%, 15% 55%, 31% 87%, 31% 87%, 14% 84%, 14% 84%, 44% 96%, 44% 96%, 59% 96%, 59% 96%, 75% 90%, 75% 90%, 71% 83%, 71% 83%, 69% 73%, 69% 73%, 88% 73%, 88% 73%, 89% 87%, 89% 87%, 94% 73%, 94% 73%;
--borderWidrh: 1;
--color: deeppink;
}
div:nth-child(4) {
--clipPath: 0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%;
--borderWidrh: 1;
--color: yellowgreen;
}
div:nth-child(5) {
--clipPath: 20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%;
--borderWidrh: 3;
--color: #c7b311;
}

得到不同圖形的邊框效果:

圖片

CodePen Demo -- CSS Hudini & Unregular Custom Border[10]。

又或者是基于它們,去實現各類按鈕效果,這種效果在以往使用 CSS 是非常非常難實現的:

圖片

它們的核心原理都是一樣的,甚至加上 Hover 效果,也是非常的輕松:

圖片

完整的代碼,你可以戳這里:CodePen Demo -- https://codepen.io/Chokcoco/pen/ExRLqdO[11]。

至此,我們再一次利用 CSS Painting API 實現了我們過往 CSS 完全無法實現的效果。這個也就是 CSS Houdini 的魅力,是 JS In CSS 的魅力。

兼容性?

好吧,其實上一篇文章也談到了兼容問題,因為可能有很多看到本篇文章并沒有去翻看前兩篇文章的同學。那么,CSS Painting API 的兼容性到底如何呢?

CanIUse - registerPaint[12] 數據如下(截止至 2022-11-23):

圖片

Chrome 和 Edge 基于 Chromium[13] 內核的瀏覽器很早就已經支持,而主流瀏覽器中,Firefox 和 Safari 目前還不支持。

CSS Houdini 雖然強大,目前看來要想大規模上生產環境,仍需一段時間的等待。讓我們給時間一點時間!

最后

好了,本文到此結束,希望本文對你有所幫助 :)

參考資料

[1]現代 CSS 之高階圖片漸隱消失術: https://juejin.cn/post/7167160342101884935?。

[2]現代 CSS 高階技巧,像 Canvas 一樣自由繪圖構建樣式!: https://juejin.cn/post/7168984450230353950?。

[3]現代 CSS 高階技巧,完美的波浪進度條效果!: https://juejin.cn/post/7170868201645932551?。

[4]CSS 對象模型: https://developer.mozilla.org/zh-CN/docs/Web/API/CSS_Object_Model?。

[5]CSS Painting API Level 1: https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope?。

[6]有意思!不規則邊框的生成方案: https://github.com/chokcoco/iCSS/issues/106?。

[7]CSS 奇技淫巧 | 巧妙實現文字二次加粗再加邊框: https://github.com/chokcoco/iCSS/issues/145?。

[8]CodePen Demo -- 3 ways to achieve unregular border: https://codepen.io/Chokcoco/pen/oNyyNQd?。

[9]CodePen Demo -- CSS Hudini & Unregular Custom Border: https://codepen.io/Chokcoco/pen/MWXXYgJ?。

[10]CodePen Demo -- CSS Hudini & Unregular Custom Border: https://codepen.io/Chokcoco/pen/KKeROeX?。

[11]CodePen Demo -- https://codepen.io/Chokcoco/pen/ExRLqdO: https://codepen.io/Chokcoco/pen/ExRLqdO?。

[12]CanIUse - registerPaint: https://caniuse.com/?search=registerPaint?。

[13]Chromium: https://www.google.com.hk/search?newwindow=1&rlz=1C5GCEM_enCN988CN988&q=Chromium&spell=1&sa=X&ved=2ahUKEwi3he2ensL7AhVaSmwGHdnzBxgQkeECKAB6BAgoEAE?。

責任編輯:姜華 來源: iCSS前端趣聞
相關推薦

2022-04-07 07:31:30

CSSCSS Reset前端

2023-09-11 07:11:04

CSSNesting

2022-04-19 06:27:13

CSS數學函數calc

2021-03-30 07:47:46

SVG 濾鏡 CSS技巧

2022-04-28 07:00:09

min()max()clamp()

2010-09-14 10:34:17

DIV CSS

2010-09-08 17:20:42

CSS

2022-12-13 07:41:43

CSSCSS Houdi

2021-02-22 18:08:38

農業物聯網IOT

2023-05-15 08:18:21

CSS技巧代碼

2009-12-25 10:20:28

WPF窗口

2023-12-13 11:37:32

2010-09-16 09:26:57

CSS display

2010-09-06 14:46:25

CSSXHTML

2015-07-17 13:31:20

按鈕單獨控制

2010-09-01 10:49:57

CSS水平居中垂直居中

2009-08-03 18:06:28

JS性能問題

2010-08-31 16:09:04

DIV+CSS

2010-09-06 13:51:38

CSS失效CSS

2010-08-26 14:00:28

CSSmargin
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美日韩三级在线观看 | 日韩精品成人av | 97人人草 | 一本一道久久a久久精品蜜桃 | 日韩国产欧美在线观看 | 欧美a区 | 精品国产黄色片 | 国产.com| 黄网站在线播放 | 超碰免费在线 | 亚洲激情在线观看 | 久久精品久久久久久 | 瑞克和莫蒂第五季在线观看 | 91精品久久久久 | 欧美久久久久 | 色综合99 | 久久er99热精品一区二区 | 日韩一二区 | 欧洲精品码一区二区三区免费看 | 日韩一区二区三区av | av一区二区在线观看 | 国产色爽 | 天天想天天干 | 精品国产伦一区二区三区观看方式 | 日韩在线 | 日本三级电影在线观看视频 | 成人精品一区二区三区中文字幕 | 欧美日韩中文在线 | 日韩中文在线视频 | 色婷婷一区二区三区四区 | 欧美一区二区三区电影 | 男人天堂网av | 日本成年免费网站 | 精品久| 欧美午夜在线 | 精品乱子伦一区二区三区 | 亚洲精品视频在线观看视频 | 成人午夜在线 | 一级毛片成人免费看a | 日本欧美国产在线 | 国产乱码精品一区二区三区五月婷 |