我想在 svg 路径上模拟“内部笔画”。 我有一个 svg 地图,其中包含多个复杂路径(国家/地区),每个路径都有不同的填充颜色描边。 我想在第一个中添加一个“假内划”。 我设法使用内阴影技巧(使用高斯模糊滤镜)完成了一些事情,但无法将其设置为“非模糊”。
理想的解决方案是作为 svg 过滤器,这样我就可以通过 JS 动态应用它,而无需更改路径或操作 dom。
非常感谢! 编辑1: 到目前为止,我尝试过这个技巧,但假阴影有时会在笔划上并且总是模糊,所以我不确定这是否是最好的方法......
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="150" height="150" style="transform:scale(2);transform-origin:0 0 ">
<defs>
<filter id='inset' x='-50%' y='-50%' width='200%' height='200%'>
<feFlood fill-color="black"/>
<feComposite in2="SourceAlpha" operator="out"/>
<feGaussianBlur stdDeviation='10' edgeMode="none" />
<feOffset dx='0' dy='0' result='offsetblur'/>
<feFlood flood-color='#00ff00' result='color'/>
<feComposite in2='offsetblur' operator='in'/>
<feComposite in2='SourceAlpha' operator='in' />
<feMerge>
<feMergeNode in='SourceGraphic'/>
<feMergeNode/>
</feMerge>
</filter>
</defs>
<path class="st0" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1
l2.5-0.3l4,3.4l12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7
l2.2,9.3l4.6,1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1
l-8.8-13l-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z" stroke-width="1" fill="#00ffff" stroke="#FF0000" filter="url(#inset)"/>
</svg>
如果你想要清晰的形状,你应该使用 SVG 变换而不是对 svg 元素应用 CSS 变换。
当您绘制“内部笔划”时,feMorphorogy 元素很有用。这会减少(或增加)目标形状的绘画区域,因此您可以绘制“假内/外”笔划。
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300">
<defs>
<filter id='inset' x='-50%' y='-50%' width='200%' height='200%'>
<!--outside-stroke-->
<feFlood flood-color="red" result="outside-color"/>
<feMorphology in="SourceAlpha" operator="dilate" radius="2"/>
<feComposite in="outside-color" operator="in" result="outside-stroke"/>
<!--inside-stroke-->
<feFlood flood-color="blue" result="inside-color"/>
<feComposite in2="SourceAlpha" operator="in" result="inside-stroke"/>
<!--fill-area-->
<feMorphology in="SourceAlpha" operator="erode" radius="2"/>
<feComposite in="SourceGraphic" operator="in" result="fill-area"/>
<!--merge graphics-->
<feMerge>
<feMergeNode in="outside-stroke"/>
<feMergeNode in="inside-stroke"/>
<feMergeNode in="fill-area"/>
</feMerge>
</filter>
</defs>
<g transform="scale(2)">
<path class="st0" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1
l2.5-0.3l4,3.4l12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7
l2.2,9.3l4.6,1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1
l-8.8-13l-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z"
fill="#00ffff" filter="url(#inset)"/>
</g>
</svg>
这就是你想要的。请注意,这取决于笔划是与任何填充颜色不同的单一颜色(在本例中为 100% 红色 - 您可以将笔划颜色更改为您想要的任何颜色,但过滤器会变得更加复杂)。
您可以通过更改最终 feColorMatrix 最后一列中的值来调整“假”内描边的颜色。现在,它是 100% 蓝色。 (您也可以使用 feMorphology 来创建它 - 正如 def 的答案一样 - 但该方法不会保留原始的斜接。)
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="150" height="150" style="transform:scale(2);transform-origin:0 0 ">
<defs>
<filter id='fake-stroke' x='-50%' y='-50%' width='200%' height='200%' color-interpolation-filters="sRGB">
<!-- select just the red outline and zero out the opacity of everything that's not 100% red. -->
<feColorMatrix type="matrix" values="1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
255 -255 -255 -254 0" result="outline-only"/>
<feGaussianBlur stdDeviation="1"/>
<!-- select just the blur - not the original stroke. -->
<feComposite operator="out" in2="outline-only"/>
<!-- select just the blur that overlaps the original content -->
<feComposite operator="in" in2="SourceGraphic" />
<!-- increase its opacity to 100% except the most blurred - to fake anti-aliasing -->
<feComponentTransfer>
<feFuncA type="table" tableValues="0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1"/>
</feComponentTransfer>
<!-- change the color of the fake stroke to the desired value -->
<feColorMatrix type="matrix" values ="0 0 0 0 0
0 0 0 0 0
0 0 0 0 1
0 0 0 1 0"/>
<!-- put it on top of the original -->
<feComposite operator="over" in2="SourceGraphic"/>
</filter>
</defs>
<path class="st0" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1
l2.5-0.3l4,3.4l12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7
l2.2,9.3l4.6,1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1
l-8.8-13l-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z" stroke-width="2" fill="#00ffff" stroke="#FF0000" filter="url(#fake-stroke)"/>
</svg>
我尝试了两个现有的答案,发现它们以混乱的方式改变了内部轮廓的形状。我的下面的解决方案涵盖了所有要求,但该解决方案最好使用 SVG 过滤器。我使用了
clip-path
功能,因为它允许我保留正确的斜角,并且不会导致模糊的插图。
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="300" viewbox="0 0 300 150">
<defs>
<clipPath id="inset1">
<path id="area1" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,
126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1l2.5-0.3l4,3.4l
12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l
-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7l2.2,9.3l4.6,
1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6
-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1l-8.8-13l
-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,
5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z"/>
</clipPath>
</defs>
<use href="#area1" stroke-width="6" fill="cyan" stroke="blue" clip-path="url(#inset1)"/>
<use href="#area1" stroke-width="2" fill="none" stroke="red"/>
</svg>
从 Don 的想法迭代,在这里您可能需要进一步分离每条线,甚至添加不重叠的填充 - 也许是因为您需要一些透明度或纹理。
如果在给定的用例中可以将形状移动到
defs
并且交互性不是严格必要的,则可以进一步从中导出分离的 masks,每个遮罩都覆盖不同的区域:“外边界”(海滩) ,“内边界”(悬崖),甚至“填充”(内陆):
:root {
background: dimgray;
color: snow;
}
svg {
--a: darkslategray;
--b: teal;
background-image: repeating-conic-gradient(var(--a) 0 25%, var(--b) 0 50%);
background-size: 1em 1em;
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
width="600" height="300" viewbox="0 0 300 150">
<use
href="#canvas"
mask="url(#beach)"
fill="darkblue"
opacity=".6"
/>
<use
href="#canvas"
mask="url(#cliffs)"
fill="red"
opacity=".4"
/>
<use
href="#canvas"
mask="url(#inland)"
fill="lime"
opacity=".3"
/>
<defs>
<!--
Both "stroke halves" in a single stroke-width:
-->
<g id="coast">
<use
href="#shoreline"
stroke-width="10"
/>
</g>
<!--
Area with the outer half of the stroke:
-->
<mask id="beach">
<use
href="#coast"
stroke="white"
/>
<use
href="#shoreline"
fill="black"
/>
</mask>
<!--
For cutting the outer half of the stroke:
-->
<clipPath id="sea">
<use
href="#shoreline"
/>
</clipPath>
<!--
Area with the inner half of the stroke:
-->
<mask id="cliffs">
<use
href="#coast"
stroke="white"
clip-path="url(#sea)"
/>
</mask>
<!--
Area inside inner stroke:
-->
<mask id="inland">
<use
href="#coast"
stroke="black"
fill="white"
/>
</mask>
<!--
Viewport cover:
-->
<rect
id="canvas"
width="100%"
height="100%"
/>
<!--
The shape:
-->
<path
id="shoreline"
d="m144.7 126.2-2.8 8.8-3.9-2.3-2-7.7 1.7-4.3 5.5-4.4
1.5 9.9zm-51.2-102 6 6.3 4.4-1 7.5 6 1.9 1.1 2.5-.3
4 3.4 12.3 2.4-4.3 8.9-1.1 9.1-2.4 2.2-3.9-1.2.3
3.2-6.3 7-.1 5.6 4.1-1.9 2.9 5.4-.3 3.6 2.5 4.6-3
3.7 2.2 9.3 4.6 1.5-1 5.1-7.8 6.6-16.9-3.2-12.5
3.8-1 7-9.9 1.5-9.6-5.3-3.1 2.5-15.8-5.3-3.4-4.6
4.4-7.1L52.3 80l-8.8-13-6.3-6.4-13.1-4.9-.9-9.4
11.1-2.8L48.9 47l-2.7-14.8 8.1 5.7 20-10.3 2.6-11
7.5-2.8 1.3 4.8 4 .2 3.8 5.4z"
/>
</defs>
</svg>
(鸣谢:)看不错 在 SVG 中绘制内部/外部笔画(剪辑和蒙版) Alex Chan 的文章演示了这项技术。