假设我们有以下 SVG 路径:
<path id="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" />
我想知道是否有一种方法可以仅填充与给定比率成比例的该形状的一部分区域。我确实需要精确地为像素数量等于比率的颜色着色。
例如,ratio = 0,6,则只需填充形状的 60% 像素(从底部开始)。
编辑:
我尝试实现评论中@RobertLongson 的提示。就这样吧:
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<linearGradient id="myGradient" gradientTransform="rotate(90)">
<stop offset="50%" stop-color="black" stop-opacity='0' />
<stop offset="50%" stop-color="black" />
</linearGradient>
<path id="heart" d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z" fill="url('#myGradient')" stroke="black" stroke-width="1" />
</svg>
但是,如果比例是50%,这个就不行了。 (即我们不对 50% 的像素着色)
如何用百分比体积填充
<path>
我最终选择了暴力方法;
检查形状中的每个 SVG 像素
isPointInFill
所有数据都可以在
filled
数组和 P.y
值中获得
filled
值是从路径形状的 bottom 到 top
使用本机 JavaScript Web 组件 (JSWC)需要对这些值进行更多处理才能绘制实心填充或渐变
50%、20% 和 80% <svg-area>
形状:
<style>
svg { --size: 180px; height: var(--size); background: pink }
path { fill: lightgreen; stroke: grey }
</style>
<svg-area percent=".5">
<svg viewBox="0 0 100 100">
<path d="M10,30A20,20,0,0,1,50,30A20,20,0,0,1,90,30Q90,60,50,90Q10,60,10,30z"></path>
</svg>
</svg-area>
<svg-area percent=".2">
<svg viewBox="0 0 100 100">
<path d="M60,10L90,90L10,60z"></path>
</svg>
</svg-area>
<svg-area percent=".8">
<svg viewBox="0 0 50 50">
<path d="m18 15q41 18 28-8l-14 30-29-15a1 1 0 0029 15z"></path>
</svg>
</svg-area>
<script>
customElements.define("svg-area", class extends HTMLElement {
connectedCallback() {
setTimeout(() => {// make sure Web Component can read parsed innerHTML
let svg = this.querySelector("svg");
let path = svg.querySelector("path");
let filled = [], percent = this.getAttribute("percent");
Array(svg.height.baseVal.value).fill().map((_, y) => {
Array(svg.width.baseVal.value).fill().map((_, x) => {
let P = svg.createSVGPoint(); P.x = x; P.y = y;
if (path.isPointInFill(P)) filled.push( P );
});
});
svg.append(
...filled.reverse()
.filter( (P,idx) => idx < filled.length * percent)
.map( P => this.circle(P.x, P.y, "green") ));
});
}
circle(cx, cy, fill) {
let circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", cx);
circle.setAttribute("cy", cy);
circle.setAttribute("fill", fill);
circle.setAttribute("r", .3);
return circle;
}
});
</script>
那么(解释算法,有人可以实现它)为什么不使用 turf.js 库?它已经实现了可以使用的面积计算。这会有点棘手,reqPct 代表请求的填充区域百分比:
get points of SVG path to an arrPoint
svgPolygon <- turf.polygon(arrPoint)
fullArea <- turf.area(svgPolygon)
tolerance <- 0.5
btmPct <- 0
topPct <- 100
divPct <- 0
curAreaPct <- 0
while (abs(reqPct-curAreaPct) > tolerance) do
divPct <- (topPct-btmPct)/2
rect <- turf.polygon with full width and divPct height of polygon
intersectPolygon <- turf.intersect(svgPolygon, rect);
interArea <- turf.area(intersectPolygon);
curAreaPct <- 100 * interArea/fullArea;
if (curAreaPct > reqPct)
topPct <- divPct
else
btmPct <- divPct
end while
fill the SVG with gradient color from bottom to divPct
这个想法是将路径的点(确定通过考虑曲线的算法解决了多少点)转换为多边形,并使用 turf 库来计算匹配相交多边形的面积(通过放置具有完整 SVG 宽度和 divPct 的矩形来找到)百分比乘以 SVG 高度 - 两者都可以通过循环中的 getBBox() 函数获得。查找该多边形是一种简单的二分搜索,以查找面积接近请求百分比的多边形。如果是这样,您只需使用从底部到找到的百分比(SVG 的高度,而不是填充区域百分比值)的渐变颜色填充 SVG。
我知道它看起来很复杂,一些基于画布的解决方案也可能有效,但这仍然可以快速计算并最终将 SVG 填充到其面积的所需百分比。