【问题标题】:Scaling the filling portion of an SVG path缩放 SVG 路径的填充部分
【发布时间】:2021-06-07 14:05:45
【问题描述】:

假设我们有以下 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% 的像素着色)

【问题讨论】:

  • @RobertLongsonthanx。我是否需要像我在上面发布的示例中那样使用偏移量(即stop offset="60%" )?仍然不确定这是否选择了 60% 的区域(即填充像素的数量),或者它是否只是在 Y 坐标的 60% 处停止。
  • 不知道是不是我记错了,但是线性渐变似乎不起作用(见帖子中的sn-p)
  • 明确一点:报告的重复帖子都没有回答这个问题
  • 如果你真的需要精确填充,比如说,area 的 60%(而不仅仅是 height 的 60%),那么线性渐变方法将不起作用,因为该算法严重依赖于路径的形状。您的问题很有趣,但是关闭它的人错误地忽略了面积和高度之间的差异。您会考虑创建一个更强调面积与高度差异的新模型吗?

标签: javascript svg d3.js


【解决方案1】:

虽然“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 填充到所需的区域百分比。

【讨论】:

    【解决方案2】:

    在 cmets 中我试图对https://jsfiddle.net/dannye/h69dfu0j/ 太聪明了

    我最终选择了蛮力方法;

    使用isPointInFill检查形状中的每个 SVG 像素

    所有数据都在filled 数组和P.y 值中可用

    filled 值是从路径形状的底部顶部

    需要对这些值进行更多处理才能绘制实心填充或渐变

    50% 、20% 和 80% &lt;svg-area&gt; 形状:

    <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" xmlns="http://www.w3.org/2000/svg">
        <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" xmlns="http://www.w3.org/2000/svg">
        <path d="M60,10L90,90L10,60z"></path>
      </svg>
    </svg-area>
    <svg-area percent=".8">
      <svg viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
        <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 );
              });
            });
            filled = filled.reverse()
                           .filter( (P,idx) => idx < filled.length * percent)
                           .map( P => this.circle(P.x, P.y, "green") );
            svg.append(...filled);
          });
        }
        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>

    【讨论】:

    • 哦,好的!如何使用像素方法来获得梯度的近似偏移量?这将使每个形状的颜色更好。例如,一个函数将路径和要着色的区域部分作为输入,并返回估计的偏移量,以后可以在公共渐变中使用。
    • 这可以通过绿色圆圈的P.y 值来完成。圆圈是从下往上画的......[我已经花了足够的时间]
    • 是的,我不想占用你的时间。这只是对读者的评论。谢谢!
    • 这非常有用,谢谢。我试图看看我是否可以操纵它来填充形状中定义的 Y 高度值,而不是形状的百分比。我没有运气,但试图检查数组中的当前圆是否超过了 Y 阈值。
    • 实际上,我已经对此进行了排序:这只是比较您已经使用的 y 值以查看它是否达到某个阈值的情况。我遇到了一些我无法理解的问题,但我非常确信逻辑是正确的,而且我在其他地方也有错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    • 2020-03-14
    • 2018-07-18
    相关资源
    最近更新 更多