元素的复杂剪辑路径/SVG 背景
Complex clip-path / SVG background of an element
我需要创建如下图所示的内容:
其中:
- 黑色背景代表页面上的一个元素,例如
header
- 青色背景代表黑色背景下的整体背景
- 黑色背景元素必须被视为单个元素,因为它上面会覆盖一个图案,该图案必须留在黑色元素的边界内,而不是出现在它之外
用一些 :pseudo-elements
创建黑色元素会很容易,但是它上面的那个图案让整个事情停滞不前。
我一直在阅读有关 clip-path
道具的信息。但我不确定我是否能够创建像这样的复杂剪辑(或者它对我来说似乎很复杂)。
整个东西将用在 iOS 应用程序上,到目前为止,这个剪辑路径 属性 似乎与它兼容。
另外要提的是,黑色元素将具有 固定高度 ,但必须是其父元素的 100% 宽度。
我想我可以改用 svg
,但由于它需要固定高度,所以它在拉伸时看起来会变形。
更新:
右侧必须保持相同的宽度,我想也许在 <g>
标签内使用两个 svgs 并绝对定位它们,一个将是流动的,另一个将具有固定宽度。但是,我不确定 filter
是否会涵盖它们,或者 filter
是否可以完全应用于 svg
内的 <g>
标签
下面的 SVG 示例:
body {
background: cyan;
}
svg {
min-width: 100%;
height: 80px;
}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 452 170" preserveAspectRatio="none">
<rect x="1" y="14" width="438" height="142" />
<path d="M0 0v170h452V0H0zM448 166H4V4h444V166z" />
<rect y="14" width="438" height="142" />
</svg>
非常感谢任何提示或指示!
使用掩码可能是更好的解决方案?
#test {
font-size: 100px;
position: relative;
display: inline-block;
margin: 40px;
}
#test:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
z-index: -1;
background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
-webkit-mask-image: linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(transparent, transparent);
-webkit-mask-size: 5% 100%, 5% 100%, 100% 5%, 100% 5%, 80% 80%;
-webkit-mask-position: left top, right top, center top, center bottom, center center ;
-webkit-mask-repeat: no-repeat;
}
body {
background-color: lightgreen;
}
<div id="test">Transparent frame</div>
固定宽度的方法
用固定的像素值替换边框的宽度尺寸。对内部矩形使用 calc。
body, html {
width: 90%;
position: relative;
}
#test {
font-size: 100px;
position: relative;
display: inline-block;
margin: 40px;
width: 100%;
height: 40%;
}
#test:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
z-index: -1;
background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
-webkit-mask-image: linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(transparent, transparent);
-webkit-mask-size: 10px 100%, 10px 100%, 100% 10px, 100% 10px, calc(100% - 40px) calc(100% - 40px);
-webkit-mask-position: left top, right top, center top, center bottom, center center ;
-webkit-mask-repeat: no-repeat;
}
body {
background-color: lightgreen;
}
<div id="test">Transparent frame</div>
一个椭圆例子
#test {
font-size: 100px;
position: relative;
display: inline-block;
margin: 40px;
border-radius: 50%;
}
#test:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
z-index: -1;
border-radius: 50%;
background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
-webkit-mask-image: radial-gradient(ellipse, red 55%, transparent 56%, transparent 65%, red 66%);
}
body {
background: lightgreen;
}
<div id="test">Transparent frame</div>
这可以通过使用 Ana 在 CSS Tricks article 中描述的方法来实现。
使用CSS剪辑路径:
我们需要做的就是像下面的代码片段一样使用 polygon
进行裁剪并将其应用到容器上。这样的路径会在最外框和中间框之间的间隙显示背景图像,隐藏或剪切中间框和最内框之间的背景图像。
div {
height: 200px;
width: 100%;
background: url(http://lorempixel.com/800/200/abstract/6);
}
#css-pattern {
-webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0px 0px, 20px 20px, 20px calc(100% - 20px), calc(100% - 20px) calc(100% - 20px), calc(100% - 20px) 20px, 20px 20px, 40px 40px, 40px calc(100% - 40px), calc(100% - 40px) calc(100% - 40px), calc(100% - 40px) 40px, 40px 40px);
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0px 0px, 20px 20px, 20px calc(100% - 20px), calc(100% - 20px) calc(100% - 20px), calc(100% - 20px) 20px, 20px 20px, 40px 40px, 40px calc(100% - 40px), calc(100% - 40px) calc(100% - 40px), calc(100% - 40px) 40px, 40px 40px);
}
/* just for demo */
body {
background: radial-gradient(circle at center, aliceblue, mediumslateblue);
min-height: 100vh;
}
<h3>Pure CSS Clip-path with fixed width gap on all 4 sides</h3>
<div id='css-pattern'></div>
使用 SVG 剪辑路径:
SVG clip-path
提供比 CSS 版本更好的浏览器支持,因为 Firefox 也支持它。我们需要做的就是像下面的代码片段一样创建一个 path
并将其用于裁剪容器。
svg path {
fill: transparent;
stroke: black;
}
/* Just for fun */
path {
animation: draw 5s linear;
stroke-dasharray: 4450;
}
@keyframes draw {
from {
stroke-dashoffset: -4450;
}
to {
stroke-dashoffset: 0;
}
}
<svg width='600px' height='200px'>
<path d='M0,0 600,0 600,200 0,200 0,0 20,20 20,180 580,180 580,20 20,20 40,40 40,160 560,160 560,40 40,40 z' />
</svg>
如果容器的尺寸是静态的,SVG 实现会容易得多。由于它们不是静态的,因此不能使用分数值,因为该值会根据元素的实际宽度(无论 100%
对应什么)而有所不同。例如,对于 200 像素宽的元素,小数值 0.2 意味着 40 像素,对于 400 像素宽的元素则意味着 80 像素。您可以在代码段的第一个示例中看到这如何影响输出。
克服这个问题的一种方法是使用 JavaScript(或您喜欢的任何其他脚本库),获取元素的实际计算宽度(以像素为单位)并计算元素的坐标值基于它的路径的 d
属性。以下代码段中的第二个示例使用此方法。
注意: IE 不支持剪辑路径,但由于您正在创建一个 iOS 应用程序,我认为这对您来说不是主要问题。
window.onload = function() {
setPathCoords();
};
window.onresize = function() {
setPathCoords();
};
function setPathCoords() {
var output = [],
borderWidth = '20';
var el = document.getElementById('percentage-pattern'),
path = document.querySelector('#clipper2 > path'),
origPath = 'M0,0 1,0 1,1 0,1 0,0 ';
height = el.clientHeight;
width = el.clientWidth;
for (var x = 1; x < 3; x++) {
point1 = (borderWidth * x) / width + "," + (borderWidth * x) / height;
point2 = (borderWidth * x) / width + "," + (height - (borderWidth * x)) / height;
point3 = (width - (borderWidth * x)) / width + "," + (height - (borderWidth * x)) / height;
point4 = (width - (borderWidth * x)) / width + "," + (borderWidth * x) / height;
output.push(point1);
output.push(point2);
output.push(point3);
output.push(point4);
output.push(point1);
}
document.querySelector('#clipper2 > path').setAttribute('d', origPath + output.join(' ') + 'z');
}
div {
height: 200px;
width: 100%;
background: url(http://lorempixel.com/800/200/abstract/6);
}
#percentage-pattern {
-webkit-clip-path: url(#clipper);
clip-path: url(#clipper);
}
#js-pattern {
-webkit-clip-path: url(#clipper2);
clip-path: url(#clipper2);
}
/* just for demo */
body {
background: radial-gradient(circle at center, aliceblue, mediumslateblue);
min-height: 100vh;
}
<svg width='0' height='0'>
<defs>
<clipPath id='clipper' clipPathUnits='objectBoundingBox'>
<path d='M0,0 1,0 1,1 0,1 0,0 0.1,0.1 0.1,0.9 0.9,0.9 0.9,0.1 0.1,0.1 0.2,0.2 0.2,0.8 0.8,0.8 0.8,0.2 0.2,0.2z' />
</clipPath>
<clipPath id='clipper2' clipPathUnits='objectBoundingBox'>
<path d='M0,0 1,0 1,1 0,1 0,0 ' />
</clipPath>
</defs>
</svg>
<h3>Output with JS</h3>
<div id='js-pattern'></div>
<h3>Pure SVG Clip-path</h3>
<div id='percentage-pattern'></div>
我需要创建如下图所示的内容:
其中:
- 黑色背景代表页面上的一个元素,例如
header
- 青色背景代表黑色背景下的整体背景
- 黑色背景元素必须被视为单个元素,因为它上面会覆盖一个图案,该图案必须留在黑色元素的边界内,而不是出现在它之外
用一些 :pseudo-elements
创建黑色元素会很容易,但是它上面的那个图案让整个事情停滞不前。
我一直在阅读有关 clip-path
道具的信息。但我不确定我是否能够创建像这样的复杂剪辑(或者它对我来说似乎很复杂)。
整个东西将用在 iOS 应用程序上,到目前为止,这个剪辑路径 属性 似乎与它兼容。
另外要提的是,黑色元素将具有 固定高度 ,但必须是其父元素的 100% 宽度。
我想我可以改用 svg
,但由于它需要固定高度,所以它在拉伸时看起来会变形。
更新:
右侧必须保持相同的宽度,我想也许在 <g>
标签内使用两个 svgs 并绝对定位它们,一个将是流动的,另一个将具有固定宽度。但是,我不确定 filter
是否会涵盖它们,或者 filter
是否可以完全应用于 svg
<g>
标签
下面的 SVG 示例:
body {
background: cyan;
}
svg {
min-width: 100%;
height: 80px;
}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 452 170" preserveAspectRatio="none">
<rect x="1" y="14" width="438" height="142" />
<path d="M0 0v170h452V0H0zM448 166H4V4h444V166z" />
<rect y="14" width="438" height="142" />
</svg>
非常感谢任何提示或指示!
使用掩码可能是更好的解决方案?
#test {
font-size: 100px;
position: relative;
display: inline-block;
margin: 40px;
}
#test:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
z-index: -1;
background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
-webkit-mask-image: linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(transparent, transparent);
-webkit-mask-size: 5% 100%, 5% 100%, 100% 5%, 100% 5%, 80% 80%;
-webkit-mask-position: left top, right top, center top, center bottom, center center ;
-webkit-mask-repeat: no-repeat;
}
body {
background-color: lightgreen;
}
<div id="test">Transparent frame</div>
固定宽度的方法
用固定的像素值替换边框的宽度尺寸。对内部矩形使用 calc。
body, html {
width: 90%;
position: relative;
}
#test {
font-size: 100px;
position: relative;
display: inline-block;
margin: 40px;
width: 100%;
height: 40%;
}
#test:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
z-index: -1;
background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
-webkit-mask-image: linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(red, red),
linear-gradient(transparent, transparent);
-webkit-mask-size: 10px 100%, 10px 100%, 100% 10px, 100% 10px, calc(100% - 40px) calc(100% - 40px);
-webkit-mask-position: left top, right top, center top, center bottom, center center ;
-webkit-mask-repeat: no-repeat;
}
body {
background-color: lightgreen;
}
<div id="test">Transparent frame</div>
一个椭圆例子
#test {
font-size: 100px;
position: relative;
display: inline-block;
margin: 40px;
border-radius: 50%;
}
#test:after {
content: "";
position: absolute;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
z-index: -1;
border-radius: 50%;
background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
-webkit-mask-image: radial-gradient(ellipse, red 55%, transparent 56%, transparent 65%, red 66%);
}
body {
background: lightgreen;
}
<div id="test">Transparent frame</div>
这可以通过使用 Ana 在 CSS Tricks article 中描述的方法来实现。
使用CSS剪辑路径:
我们需要做的就是像下面的代码片段一样使用 polygon
进行裁剪并将其应用到容器上。这样的路径会在最外框和中间框之间的间隙显示背景图像,隐藏或剪切中间框和最内框之间的背景图像。
div {
height: 200px;
width: 100%;
background: url(http://lorempixel.com/800/200/abstract/6);
}
#css-pattern {
-webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0px 0px, 20px 20px, 20px calc(100% - 20px), calc(100% - 20px) calc(100% - 20px), calc(100% - 20px) 20px, 20px 20px, 40px 40px, 40px calc(100% - 40px), calc(100% - 40px) calc(100% - 40px), calc(100% - 40px) 40px, 40px 40px);
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0px 0px, 20px 20px, 20px calc(100% - 20px), calc(100% - 20px) calc(100% - 20px), calc(100% - 20px) 20px, 20px 20px, 40px 40px, 40px calc(100% - 40px), calc(100% - 40px) calc(100% - 40px), calc(100% - 40px) 40px, 40px 40px);
}
/* just for demo */
body {
background: radial-gradient(circle at center, aliceblue, mediumslateblue);
min-height: 100vh;
}
<h3>Pure CSS Clip-path with fixed width gap on all 4 sides</h3>
<div id='css-pattern'></div>
使用 SVG 剪辑路径:
SVG clip-path
提供比 CSS 版本更好的浏览器支持,因为 Firefox 也支持它。我们需要做的就是像下面的代码片段一样创建一个 path
并将其用于裁剪容器。
svg path {
fill: transparent;
stroke: black;
}
/* Just for fun */
path {
animation: draw 5s linear;
stroke-dasharray: 4450;
}
@keyframes draw {
from {
stroke-dashoffset: -4450;
}
to {
stroke-dashoffset: 0;
}
}
<svg width='600px' height='200px'>
<path d='M0,0 600,0 600,200 0,200 0,0 20,20 20,180 580,180 580,20 20,20 40,40 40,160 560,160 560,40 40,40 z' />
</svg>
如果容器的尺寸是静态的,SVG 实现会容易得多。由于它们不是静态的,因此不能使用分数值,因为该值会根据元素的实际宽度(无论 100%
对应什么)而有所不同。例如,对于 200 像素宽的元素,小数值 0.2 意味着 40 像素,对于 400 像素宽的元素则意味着 80 像素。您可以在代码段的第一个示例中看到这如何影响输出。
克服这个问题的一种方法是使用 JavaScript(或您喜欢的任何其他脚本库),获取元素的实际计算宽度(以像素为单位)并计算元素的坐标值基于它的路径的 d
属性。以下代码段中的第二个示例使用此方法。
注意: IE 不支持剪辑路径,但由于您正在创建一个 iOS 应用程序,我认为这对您来说不是主要问题。
window.onload = function() {
setPathCoords();
};
window.onresize = function() {
setPathCoords();
};
function setPathCoords() {
var output = [],
borderWidth = '20';
var el = document.getElementById('percentage-pattern'),
path = document.querySelector('#clipper2 > path'),
origPath = 'M0,0 1,0 1,1 0,1 0,0 ';
height = el.clientHeight;
width = el.clientWidth;
for (var x = 1; x < 3; x++) {
point1 = (borderWidth * x) / width + "," + (borderWidth * x) / height;
point2 = (borderWidth * x) / width + "," + (height - (borderWidth * x)) / height;
point3 = (width - (borderWidth * x)) / width + "," + (height - (borderWidth * x)) / height;
point4 = (width - (borderWidth * x)) / width + "," + (borderWidth * x) / height;
output.push(point1);
output.push(point2);
output.push(point3);
output.push(point4);
output.push(point1);
}
document.querySelector('#clipper2 > path').setAttribute('d', origPath + output.join(' ') + 'z');
}
div {
height: 200px;
width: 100%;
background: url(http://lorempixel.com/800/200/abstract/6);
}
#percentage-pattern {
-webkit-clip-path: url(#clipper);
clip-path: url(#clipper);
}
#js-pattern {
-webkit-clip-path: url(#clipper2);
clip-path: url(#clipper2);
}
/* just for demo */
body {
background: radial-gradient(circle at center, aliceblue, mediumslateblue);
min-height: 100vh;
}
<svg width='0' height='0'>
<defs>
<clipPath id='clipper' clipPathUnits='objectBoundingBox'>
<path d='M0,0 1,0 1,1 0,1 0,0 0.1,0.1 0.1,0.9 0.9,0.9 0.9,0.1 0.1,0.1 0.2,0.2 0.2,0.8 0.8,0.8 0.8,0.2 0.2,0.2z' />
</clipPath>
<clipPath id='clipper2' clipPathUnits='objectBoundingBox'>
<path d='M0,0 1,0 1,1 0,1 0,0 ' />
</clipPath>
</defs>
</svg>
<h3>Output with JS</h3>
<div id='js-pattern'></div>
<h3>Pure SVG Clip-path</h3>
<div id='percentage-pattern'></div>