SVG路径的填充部分如何进行缩放

3

假设我们有以下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" />

我想知道是否有一种方法可以按比例填充形状的部分区域,使其与给定的比例成比例。 我确实需要准确地着色等于该比例的像素数量。

例如,比率= 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%的像素进行上色)

1
@RobertLongson 谢谢。我需要像我在上面发布的示例中那样使用偏移量吗(例如 stop offset="60%")?我还不确定这是否选择了区域的60%(即填充像素的数量),还是只是在Y坐标的60%处放置一个停止点。 - floatingpurr
1
另请参阅:StackOverflow: 在SVG图形中显示百分比 - Danny '365CSI' Engelman
1
不确定我是不是弄错了,但是线性渐变似乎不起作用(请参见帖子中的代码片段) - floatingpurr
1
只是为了明确一点:所报告的重复帖子都没有回答这个问题。 - floatingpurr
1
如果你真的需要填充恰好60%的“面积”(而不仅仅是60%的“高度”),那么线性渐变方法将无法奏效,因为算法在很大程度上依赖于路径的形状。你的问题非常有趣,但是那些关闭它的人可能误解了面积和高度之间的区别。你是否考虑重新提出一个更加强调面积与高度差异的问题? - Rodrigo Divino
显示剩余7条评论
2个回答

3

如果"Danny '365CSI' Engelman"解决了凹形区域的问题,那么他已经走在了正确的道路上,但我会对此进行一些改变(因为我总是寻找现有的解决方案以便尽可能地利用)。

所以(解释算法,某人可以实现它),为什么不使用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到其面积的所需百分比。


3
如何使用百分比填充<path>
最终我采用了一种蛮力法的方法;
使用isPointInFill检查每个SVG像素是否在形状内。
所有数据都可以在filled数组和P.y值中找到。 filled值从路径形状的底部到顶部。
使用本地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>


哦,好的!那么使用像素方法来获取渐变的近似偏移量如何?这样可以更好地给每个形状上色。例如,一个函数以路径和要着色的区域部分作为输入,并返回估计的偏移量,稍后可以在常规渐变中使用。 - floatingpurr
1
可以通过绿色圆圈的“P.y”值来完成。这些圆圈是从底部向顶部绘制的... [我已经花了足够的时间在这上面] - Danny '365CSI' Engelman
是的,我不想浪费你的时间。这只是为读者做出的一条评论。谢谢! - floatingpurr
非常有用,谢谢。我正在尝试看是否可以将其调整为填充到形状的一个定义的Y高度值,而不是形状的百分比。但是我一直没有成功,我正在尝试检查数组中的当前圆是否越过了那个Y阈值。 - biscuitstack
实际上,我已经整理好了:只需比较您已经使用的Y值,看它是否达到了某个阈值。我在这方面遇到了一些问题,无法理清楚,但我相当确信逻辑是正确的,并且错误可能出现在其他地方。 - biscuitstack
请将您的代码发布在一个新问题中,这样所有的SVG专家都可以参与讨论。现在只有我一个人能收到您的评论。 - Danny '365CSI' Engelman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接