我有几个用于社交媒体链接的SVG图标。我希望他们有白色背景。在社区的帮助下,我发现可以通过在SVG代码<path fill="white" d="XXX">
中添加XXX来完成,其中XXX等于d=""
代码的第一部分,直到第一个m
。例如,有以下SVG代码:
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill='rgb(255, 64, 0)' viewBox="0 0 24 24"><path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm3.445 17.827c-3.684 1.684-9.401-9.43-5.8-11.308l1.053-.519 1.746 3.409-1.042.513c-1.095.587 1.185 5.04 2.305 4.497l1.032-.505 1.76 3.397-1.054.516z"/></svg>
要用白色填充内部,需要更改代码,如下所示:
<svg xmlns="http://www.w3.org/2000/svg" width='24' height='24' fill='rgb(255, 64, 0)' viewBox="0 0 24 24">
<path fill="white" d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12z"/>
<path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm3.445 17.827c-3.684 1.684-9.401-9.43-5.8-11.308l1.053-.519 1.746 3.409-1.042.513c-1.095.587 1.185 5.04 2.305 4.497l1.032-.505 1.76 3.397-1.054.516z"/>
</svg>
这就是添加的内容:
<path fill="white" d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12z"/>
此方法可以正常工作,直到出现这样的SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M15.233 5.488c-.843-.038-1.097-.046-3.233-.046s-2.389.008-3.232.046c-2.17.099-3.181 1.127-3.279 3.279-.039.844-.048 1.097-.048 3.233s.009 2.389.047 3.233c.099 2.148 1.106 3.18 3.279 3.279.843.038 1.097.047 3.233.047 2.137 0 2.39-.008 3.233-.046 2.17-.099 3.18-1.129 3.279-3.279.038-.844.046-1.097.046-3.233s-.008-2.389-.046-3.232c-.099-2.153-1.111-3.182-3.279-3.281zm-3.233 10.62c-2.269 0-4.108-1.839-4.108-4.108 0-2.269 1.84-4.108 4.108-4.108s4.108 1.839 4.108 4.108c0 2.269-1.839 4.108-4.108 4.108zm4.271-7.418c-.53 0-.96-.43-.96-.96s.43-.96.96-.96.96.43.96.96-.43.96-.96.96zm-1.604 3.31c0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-1.473 1.194-2.667 2.667-2.667s2.667 1.194 2.667 2.667zm4.333-12h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm.952 15.298c-.132 2.909-1.751 4.521-4.653 4.654-.854.039-1.126.048-3.299.048s-2.444-.009-3.298-.048c-2.908-.133-4.52-1.748-4.654-4.654-.039-.853-.048-1.125-.048-3.298 0-2.172.009-2.445.048-3.298.134-2.908 1.748-4.521 4.654-4.653.854-.04 1.125-.049 3.298-.049s2.445.009 3.299.048c2.908.133 4.523 1.751 4.653 4.653.039.854.048 1.127.048 3.299 0 2.173-.009 2.445-.048 3.298z"/></svg>
我尝试添加其他路径。这个仅填充内圆和圆点:
M15.233 5.488c-.843-.038-1.097-.046-3.233-.046s-2.389.008-3.232.046c-2.17.099-3.181 1.127-3.279 3.279-.039.844-.048 1.097-.048 3.233s.009 2.389.047 3.233c.099 2.148 1.106 3.18 3.279 3.279.843.038 1.097.047 3.233.047 2.137 0 2.39-.008 3.233-.046 2.17-.099 3.18-1.129 3.279-3.279.038-.844.046-1.097.046-3.233s-.008-2.389-.046-3.232c-.099-2.153-1.111-3.182-3.279-3.281z
这个填充了一点点,但是仍然不能完全填充:
M15.233 5.488c-.843-.038-1.097-.046-3.233-.046s-2.389.008-3.232.046c-2.17.099-3.181 1.127-3.279 3.279-.039.844-.048 1.097-.048 3.233s.009 2.389.047 3.233c.099 2.148 1.106 3.18 3.279 3.279.843.038 1.097.047 3.233.047 2.137 0 2.39-.008 3.233-.046 2.17-.099 3.18-1.129 3.279-3.279.038-.844.046-1.097.046-3.233s-.008-2.389-.046-3.232c-.099-2.153-1.111-3.182-3.279-3.281zm4.271-7.418c-.53 0-.96-.43-.96-.96s.43-.96.96-.96.96.43.96.96-.43.96-.96.96zm4.333-12h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm.952 15.298c-.132 2.909-1.751 4.521-4.653 4.654-.854.039-1.126.048-3.299.048s-2.444-.009-3.298-.048c-2.908-.133-4.52-1.748-4.654-4.654-.039-.853-.048-1.125-.048-3.298 0-2.172.009-2.445.048-3.298.134-2.908 1.748-4.521 4.654-4.653.854-.04 1.125-.049 3.298-.049s2.445.009 3.299.048c2.908.133 4.523 1.751 4.653 4.653.039.854.048 1.127.048 3.299 0 2.173-.009 2.445-.048 3.298z
然后有一个这样的SVG,它根本无法填充:
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 12.713l-11.985-9.713h23.97l-11.985 9.713zm0 2.574l-12-9.725v15.438h24v-15.438l-12 9.725z"/></svg>
如果您能帮助我填补那些棘手的SVG,我将不胜感激。谢谢!
P.S。 用于编码SVG和实时预览的出色工具-here。
答案 0 :(得分:1)
概述
SVG具有丰富的DOM API,因此,如果存在无法填充的路径,那么如何构造一个可以做到的路径呢?
提出的方法包括以下步骤:
保证CH路径填充。由于SVG通过应用绘制程序的算法进行渲染,因此可以将CH路径用作O路径的背景,为O路径后面的抽象画布着色,并模拟大多数用户直观地认为路径的填充。 / p>
这个想法看似过大,但在野外遇到的大多数路径定义中都应该是可靠的(有关概念上的警告,请参见“限制”部分)。可以很容易地扩展它,以考虑用于绘制目标图标的一组路径元素。
限制
CH路径根据定义将覆盖O路径。相反,仅适用于凸形O路径。如果O形路径不是凸形的,则要根据情况逐个确定该解决方案是否仍然有用(例如,像吃豆人这样的图标比星形具有更好的几率)。
详细信息
计算凸包(CH)
点集的CH计算为完全覆盖这些点的最小凸点集。使用getPointAtLength
元素的DOM API的getTotalLength
和path
方法,无需分析路径规范即可获得合适的点集,因为这些方法允许以编程方式沿着路径移动,并且路径上的采样点(在下面的代码中,等距间隔为0.1个距离单位长度)。然后将所得的点集输入到CH算法中。
从CH路径构建路径
CH算法以正确的顺序返回CH点,以通过将相邻点与直线段连接来绘制边界。使用绝对坐标中的d
(移至)和path
(行至)路径命令,可以将其转换为M
元素的L
属性。
将CH路径插入到OG路径之前的SVG中。
当将SVG元素的DOM API的方法insertAdjacentElement
应用于O路径时,就可以解决问题。
用所需的颜色填充CH路径,保持O路径透明
使用fill
属性。
在线演示
该演示为了更好的可见性而对图标路径进行了转换,并绘制了CH边界。实际计算是在原始路径坐标上执行的。 CH用红色绘制。生产代码将利用某些库来计算CH。该演示显示了邮件图标,但包括电话和instagram图标的路径定义-超出了相应的use
元素以使用它们。
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="250" viewBox="0 0 2000 1000">
<defs>
<g
id="icon_instagram"
transform="translate(500,400) scale(20,20) translate(-10,-10)"
>
<path d="M15.233 5.488c-.843-.038-1.097-.046-3.233-.046s-2.389.008-3.232.046c-2.17.099-3.181 1.127-3.279 3.279-.039.844-.048 1.097-.048 3.233s.009 2.389.047 3.233c.099 2.148 1.106 3.18 3.279 3.279.843.038 1.097.047 3.233.047 2.137 0 2.39-.008 3.233-.046 2.17-.099 3.18-1.129 3.279-3.279.038-.844.046-1.097.046-3.233s-.008-2.389-.046-3.232c-.099-2.153-1.111-3.182-3.279-3.281zm-3.233 10.62c-2.269 0-4.108-1.839-4.108-4.108 0-2.269 1.84-4.108 4.108-4.108s4.108 1.839 4.108 4.108c0 2.269-1.839 4.108-4.108 4.108zm4.271-7.418c-.53 0-.96-.43-.96-.96s.43-.96.96-.96.96.43.96.96-.43.96-.96.96zm-1.604 3.31c0 1.473-1.194 2.667-2.667 2.667s-2.667-1.194-2.667-2.667c0-1.473 1.194-2.667 2.667-2.667s2.667 1.194 2.667 2.667zm4.333-12h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm.952 15.298c-.132 2.909-1.751 4.521-4.653 4.654-.854.039-1.126.048-3.299.048s-2.444-.009-3.298-.048c-2.908-.133-4.52-1.748-4.654-4.654-.039-.853-.048-1.125-.048-3.298 0-2.172.009-2.445.048-3.298.134-2.908 1.748-4.521 4.654-4.653.854-.04 1.125-.049 3.298-.049s2.445.009 3.299.048c2.908.133 4.523 1.751 4.653 4.653.039.854.048 1.127.048 3.299 0 2.173-.009 2.445-.048 3.298z"/>
</g>
<g
id="icon_mail"
transform="translate(500,400) scale(40,40) translate(-12,-12.713)"
>
<path d="M12 12.713l-11.985-9.713h23.97l-11.985 9.713zm0 2.574l-12-9.725v15.438h24v-15.438l-12 9.725z"/>
</g>
<g
id="icon_skype"
transform="translate(500,400) scale(20,20) translate(-12, -12)"
>
<path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm3.445 17.827c-3.684 1.684-9.401-9.43-5.8-11.308l1.053-.519 1.746 3.409-1.042.513c-1.095.587 1.185 5.04 2.305 4.497l1.032-.505 1.76 3.397-1.054.516z"/>
</g>
</defs>
<!-- only one 'use' element should be active at any one time -->
<use href="#icon_mail"/>
<!--use href="#icon_instagram"/-->
<!--use href="#icon_skype"/-->
<text x="1100" y="150" width="200" height="100" style="font-size:300%;">Click on the filled area of the icon to the left.</text>
<script type="text/javascript">
<![CDATA[
const SVG_XLINK = "http://www.w3.org/1999/xlink";
const SVG_NS = 'http://www.w3.org/2000/svg';
function _cmp ( a, b ) {
let n_cmp
;
n_cmp =
(a.x > b.x)
? 1
: ((a.x < b.x)
? -1
: ((a.y > b.y)
? 1
: ((a.y < b.y) ? -1 : 0)
)
)
;
return n_cmp;
} // _cmp
// Get convex Hull of point set
function getConvexHull ( pa_points ) {
console.log(`getConvexHull: started, point set size: ${pa_points.length};`); // `
pa_points.sort ( _cmp );
console.log(`getConvexHull: done: sort;`);
let a_ch = []
, m_base
, n_base = 0
, n_idxCurrentCandidate // index of current candidate for the next CH point
, n_count = 0
;
// !!! Reminder: svg y axis oriented downwards !
// Sweep 1/2: x -> x+ (lower CH)
let i = 0;
while (pa_points[i].x === pa_points[0].x) {
//***console.log(`getConvexHull: pushing point #${i}.`);
a_ch.push ( pa_points[i] ); // yep, points have been lexicographically ordered
i++;
}
n_idxCurrentCandidate = i-1;
while (n_idxCurrentCandidate + 1 < pa_points.length) { // guard against degenerate case
n_base = n_idxCurrentCandidate;
m_base = {
dx: (pa_points[n_base+1].x - pa_points[n_base].x)
, dy: (pa_points[n_base+1].y - pa_points[n_base].y)
};
n_idxCurrentCandidate = n_base+1;
if (n_base+2 < pa_points.length) {
let k = n_base+2;
do {
let n_fac = (pa_points[k].x - pa_points[n_base].x) / m_base.dx
;
if ((pa_points[k].y - pa_points[n_base].y) > n_fac * m_base.dy) {
// steeper slope detected.
n_idxCurrentCandidate = k;
m_base.dx = pa_points[k].x - pa_points[n_base].x;
m_base.dy = pa_points[k].y - pa_points[n_base].y;
}
k++;
} while ( k < pa_points.length );
}
// We will get the end point in the upper CH sweep.
if (n_idxCurrentCandidate !== pa_points.length-1) {
//***console.log(`getConvexHull: pushing point #${n_idxCurrentCandidate}.`);
a_ch.push ( pa_points[n_idxCurrentCandidate] );
}
n_count++;
} // lower CH
console.log(`getConvexHull: lower CH completed after ${n_count} slope checks, ${a_ch.length} CH points found so far;`);
// Sweep 2/2: x -> x- (upper CH)
i = pa_points.length - 1;
while (pa_points[i].x === pa_points[pa_points.length-1].x) {
//***console.log(`getConvexHull: pushing point #${i}.`);
a_ch.push ( pa_points[i] ); // yep, points have been lexicographically ordered
i--;
}
n_idxCurrentCandidate = i+1;
while (n_idxCurrentCandidate - 1 >= 0) { // guard against degenerate case
n_base = n_idxCurrentCandidate;
m_base = {
dx: (pa_points[n_base-1].x - pa_points[n_base].x)
, dy: (pa_points[n_base-1].y - pa_points[n_base].y)
};
n_idxCurrentCandidate = n_base-1;
if (n_base-2 >= 0) {
let k = n_base-2;
do {
let n_fac = (pa_points[k].x - pa_points[n_base].x) / m_base.dx
;
if ((pa_points[k].y - pa_points[n_base].y) < n_fac * m_base.dy) {
// shallower slope detected.
n_idxCurrentCandidate = k;
m_base.dx = pa_points[k].x - pa_points[n_base].x;
m_base.dy = pa_points[k].y - pa_points[n_base].y;
}
k--;
} while ( k >= 0 );
}
// We got the start point already in the lower CH sweep.
if (n_idxCurrentCandidate !== 0) {
//***console.log(`getConvexHull: pushing point #${n_idxCurrentCandidate}.`);
a_ch.push ( pa_points[n_idxCurrentCandidate] );
}
n_count++;
} // upper CH
console.log(`getConvexHull: upper CH completed. TL of ${n_count} slope checks, ${a_ch.length} CH points found.`);
return a_ch;
} // getConvexHull
// Get convex hull of path
function getPathHull ( e_path ) {
console.log(`getPathHull: started;`);
let a_CH
, a_pointset = []
, n_pathLengthCurrent = 0
, n_pathLengthTotal = e_path.getTotalLength()
, n_steps = 10 * Math.floor(n_pathLengthTotal)
;
console.log(`getPathHull: path length = ${n_pathLengthTotal};`);
// Get point coords along the path.
// Sliding [-1,+1]-window to eliminate interior points on straight line segments.
let o_svgpoint_prevprev
, o_svgpoint_prev
, o_svgpoint
;
a_pointset.push( e_path.getPointAtLength(0) );
for (let i = 0; i < n_steps; i++) {
o_svgpoint_prevprev = o_svgpoint_prev;
o_svgpoint_prev = o_svgpoint;
o_svgpoint = e_path.getPointAtLength(i * n_pathLengthTotal / n_steps);
if (i > 1) {
// Optimizing away points on straight line axis-parallel segments
if (!(
(
(o_svgpoint_prevprev.x === o_svgpoint_prev.x)
&& (o_svgpoint_prev.x === o_svgpoint.x)
)
|| (
(o_svgpoint_prevprev.y === o_svgpoint_prev.y)
&& (o_svgpoint_prev.y === o_svgpoint.y)
)
)) {
a_pointset.push( o_svgpoint_prev );
}
}
}
a_pointset.push( o_svgpoint ); // the last one - always included
console.log(`getPathHull: pointset size = ${n_steps}, after straight line segment optimization ${a_pointset.length};`);
let n_checkAt = Math.floor(Math.random() * a_pointset.length)
;
console.log(`getPathHull: @${Number(100 * n_checkAt / n_steps).toFixed(2)}%: (x,y) = (${Number(a_pointset[n_checkAt].x).toFixed(2)}, ${Number(a_pointset[n_checkAt].y).toFixed(2)});`);
// Compute the Convex Hull for the point set
a_CH = getConvexHull ( a_pointset );
// Build a path from the CH. The CH point set is ordered counter-clockwise
a_CH = a_CH.map ( (po_svgpoint) => {
return { x: Number(po_svgpoint.x).toFixed(3), y: Number(po_svgpoint.y).toFixed(3) };
});
return a_CH;
} // getPathHull
//
// paintPathHull
//
function paintPathHull ( eve, ps_id ) {
console.log(`paintPathHull: started; eve.target.parentElement.id = '${eve.target.id}', ps_id = '${ps_id}'`);
let a_CH
, e_g = eve.target.parentElement
, e_hull
, e_path = document.querySelector(`#${e_g.getAttribute('id')} > path`)
, e_svg = document.querySelector('svg')
, s_attr_d
;
a_CH = getPathHull(e_path);
console.log ( `a_CH - first 10 coords: ${JSON.stringify(a_CH.slice(0,9))};`);
s_attr_d =
`M${a_CH[0].x},${a_CH[0].y} `
+ a_CH.map( ( po_coords, pn_idx ) => {
return `L${po_coords.x},${po_coords.y}`;
}).join(' ')
;
e_hull = document.createElementNS(SVG_NS, 'path');
e_hull.setAttribute('d', s_attr_d);
e_hull.setAttribute('fill', 'green');
e_hull.setAttribute('stroke', 'red');
e_hull.setAttribute('stroke-width', '0.1');
e_path.insertAdjacentElement('beforebegin', e_hull);
} // paintPathHull
document.querySelector('#icon_instagram').addEventListener ( 'click', (eve) => { paintPathHull(eve, 'icon_instagram'); } );
document.querySelector('#icon_mail').addEventListener ( 'click', (eve) => { paintPathHull(eve, 'icon_mail'); } );
document.querySelector('#icon_skype').addEventListener ( 'click', (eve) => { paintPathHull(eve, 'icon_skype'); } );
]]>
</script>
</svg>