我正在尝试使用SVG在多个点之间绘制一条扇形路径,就像为矩形here绘制但在多个点之间一样。期望通过扇形线连接两个或更多两个或更多选定点。

但是我面临的问题是

  • 扇贝的大小不对称也不随机。 -我解决了这个
  • 单击多个点后,扇贝向上和向下。如下图所示。

    javascript - 在多个点之间绘制扇形多边形-LMLPHP

  • 即使答案是在html5 canvas上下文中给出的,我也完全可以。我会进行调整。我缺少一些额外的计算,但无法弄清楚是什么。

    请在结果页面中单击多次以查看当前绘制的扇贝

    var strokeWidth = 3;
    
    function distance(x1, y1, x2, y2) {
      return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }
    
    function findNewPoint(x, y, angle, distance) {
      var result = {};
      result.x = Math.round(Math.cos(angle) * distance + x);
      result.y = Math.round(Math.sin(angle) * distance + y);
      return result;
    }
    
    function getAngle(x1, y1, x2, y2) {
      return Math.atan2(y2 - y1, x2 - x1);
    }
    
    function scapolledLine(points, strokeWidth) {
      var that = this;
      var scallopSize = strokeWidth * 8;
      var path = [],
        newP = null;
      path.push("M", points[0].x, points[0].y);
      points.forEach(function(s, i) {
        var stepW = scallopSize,
          lsw = 0;
        var e = points[i + 1];
        if (!e) {
          path.push('A', stepW / 2, stepW / 2, "0 0 1", s.x, s.y);
          return;
        }
        var args = [s.x, s.y, e.x, e.y];
        var dist = that.distance.apply(that, args);
        if (dist === 0) return;
        var angle = that.getAngle.apply(that, args);
        newP = s;
        // Number of possible scallops between current points
        var n = dist / stepW,
          crumb;
    
        if (dist < (stepW * 2)) {
          stepW = (dist - stepW) > (stepW * 0.38) ? (dist / 2) : dist;
        } else {
          n = (n - (n % 1));
          crumb = dist - (n * stepW);
          /*if((stepW - crumb) > (stepW * 0.7)) {
              lsw = crumb;
          } else {
              stepW += (crumb / n);
          }*/
          stepW += (crumb / n);
        }
    
        // Recalculate possible scallops.
        n = dist / stepW;
        var aw = stepW / 2;
        for (var i = 0; i < n; i++) {
          newP = that.findNewPoint(newP.x, newP.y, angle, stepW);
          if (i === (n - 1)) {
            aw = (lsw > 0 ? lsw : stepW) / 2;
          }
          path.push('A', aw, aw, "0 0 1", newP.x, newP.y);
        }
        // scallopSize = stepW;
      });
      return path.join(' ');
      // return path.join(' ') + (points.length > 3 ? 'z' : '');
    }
    
    var points = [];
    var mouse = null;
    var dblclick = null,
      doneEnding = false;
    
    window.test.setAttribute('stroke-width', strokeWidth);
    
    function feed() {
      if (dblclick && doneEnding) return;
      if (!dblclick && (points.length > 0 && mouse)) {
        var arr = points.slice(0);
        arr.push(mouse);
        var str = scapolledLine(arr, strokeWidth);
        window.test.setAttribute('d', str);
      } else if (dblclick) {
        points.push(points[0]);
        doneEnding = true;
        var str = scapolledLine(points, strokeWidth);
        window.test.setAttribute('d', str);
      }
    }
    
    document.addEventListener('mousedown', function(event) {
      points.push({
        x: event.clientX,
        y: event.clientY
      });
      feed();
    });
    
    document.addEventListener('dblclick', function(event) {
      dblclick = true;
      feed();
    });
    
    document.addEventListener('mousemove', function(event) {
      if (points.length > 0) {
        mouse = {
          x: event.clientX,
          y: event.clientY
        }
        feed();
      }
    });
    body,
    html {
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0
    }
    svg {
      height: 100%;
      width: 100%
    }
    <svg id="svgP">
      <path id="test" style="stroke: RGBA(212, 50, 105, 1.00); fill: none" />
    </svg>

    最佳答案

    以下代码段通过分析相邻段来确定每个段的方向(CW,CCW)。对于线段A,如果两个相邻线段都在A的同一侧(或者如果A仅具有一个相邻线段),则没有歧义,线段A的扇贝在由这些线段形成的凸形的外侧。但是,如果相邻段位于锯齿形的A的相对两侧,则选择距段A最远的相邻段来设置段A的方向。

    function distance(x1, y1, x2, y2) {
        return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    }
    
    function findNewPoint(x, y, angle, distance) {
        var result = {};
        result.x = Math.round(Math.cos(angle) * distance + x);
        result.y = Math.round(Math.sin(angle) * distance + y);
        return result;
    }
    
    function getAngle(x1, y1, x2, y2) {
        return Math.atan2(y2 - y1, x2 - x1);
    }
    
    function getSeparationFromLine(lineOrigin, lineAngle, pt) {
        x = pt.x - lineOrigin.x;
        y = pt.y - lineOrigin.y;
        return -x * Math.sin(lineAngle) + y * Math.cos(lineAngle);
    }
    
    function getDirection(pts, index, closed) {
        var last = pts.length - 1;
        var start = index;
        var end = (closed && start == last) ? 0 : index + 1;
        var prev = (closed && start == 0) ? last : start - 1;
        var next = (closed && end == last) ? 0 : end + 1;
    
        var isValidSegment = 0 <= start && start <= last && 0 <= end && end <= last && end !== start;
    
        if (!isValidSegment) {
            return 1;
        }
    
        var pt1 = pts[start];
        var pt2 = pts[end];
    
        var pt, x, y;
        var ccw = 0.0;
        var theta = Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x);
    
        if (0 <= prev && prev <= last) {
            ccw += getSeparationFromLine(pt1, theta, pts[prev]);
        }
    
        if (0 <= next && next <= last) {
            ccw += getSeparationFromLine(pt1, theta, pts[next]);
        }
    
        return ccw > 0 ? "1" : "0";
    }
    
    function scapolledLine(pts, closed, strokeWidth) {
        var that = this;
        var scallopSize = strokeWidth * 8;
        var lastIndex = pts.length - 1;
        var path = [], newP = null;
        path.push("M", pts[0].x, pts[0].y);
    
        pts.forEach(function (s, currentIndex) {
            var stepW = scallopSize, lsw = 0;
            var isClosingSegment = closed && currentIndex == lastIndex;
            var nextIndex = isClosingSegment ? 0 : currentIndex + 1;
            var e = pts[nextIndex];
            if (!e) {
                return;
            }
    
            var direction = getDirection(pts, currentIndex, closed);
            var args = [s.x, s.y, e.x, e.y];
            var dist = that.distance.apply(that, args);
            if (dist === 0) {
                return;
            }
    
            var angle = that.getAngle.apply(that, args);
            newP = s;
    
            // Number of possible scallops between current pts
            var n = dist / stepW, crumb;
    
            if (dist < (stepW * 2)) {
                stepW = (dist - stepW) > (stepW * 0.38) ? (dist / 2) : dist;
            } else {
                n = (n - (n % 1));
                crumb = dist - (n * stepW);
                stepW += (crumb / n);
            }
    
            // Recalculate possible scallops.
            n = dist / stepW;
            var aw = stepW / 2;
            for (var i = 0; i < n; i++) {
                newP = that.findNewPoint(newP.x, newP.y, angle, stepW);
                if (i === (n - 1)) {
                    aw = (lsw > 0 ? lsw : stepW) / 2;
                }
                path.push('A', aw, aw, "0 0 " + direction, newP.x, newP.y);
            }
    
            if (isClosingSegment) {
                path.push('A', stepW / 2, stepW / 2, "0 0 " + direction, e.x, e.y);
            }
        });
        return path.join(' ');
    }
    
    var strokeWidth = 3;
    var points = [];
    var mouse = null;
    var isClosed = false;
    
    window.test.setAttribute('stroke-width', strokeWidth);
    
    function feed(isDoubleClick) {
        if (isClosed) {
            return;
        }
        if (!isDoubleClick && (points.length > 0 && mouse)) {
            var arr = points.slice(0);
            arr.push(mouse);
            var str = scapolledLine(arr, isClosed, strokeWidth);
            window.test.setAttribute('d', str);
        } else if (isDoubleClick) {
            isClosed = true;
            points.pop();
            var str = scapolledLine(points, isClosed, strokeWidth);
            window.test.setAttribute('d', str);
        }
    }
    
    document.addEventListener('mousedown', function (event) {
        points.push({
            x: event.clientX,
            y: event.clientY
        });
        feed(false);
    });
    
    document.addEventListener('dblclick', function (event) {
        feed(true);
    });
    
    document.addEventListener('mousemove', function (event) {
        if (points.length > 0) {
            mouse = {
                x: event.clientX,
                y: event.clientY
            }
            feed(false);
        }
    });
    body,
    html {
      height: 100%;
      width: 100%;
      margin: 0;
      padding: 0
    }
    svg {
      height: 100%;
      width: 100%
    }
    <svg id="svgP">
      <path id="test" style="stroke: RGBA(212, 50, 105, 1.00); fill: none" />
    </svg>

    09-26 19:11