如何在svg中获得标记结束位置?

时间:2016-07-06 07:23:14

标签: javascript css svg

下面是您可以运行的代码,并查看输出,该输出是黑色线,其末端位置带有标记。

<svg width="600px" height="200px">
  <defs>
    <marker id="arrow" markerWidth="10" markerHeight="10" refx="0" refy="3" orient="auto" markerUnits="strokeWidth">
      <path d="M0,0 L0,6 L9,3 z" fill="#000" />
    </marker>
  </defs>

  <line x1="50" y1="50" x2="250" y2="150" stroke="#000" stroke-width="5" marker-end="url(#arrow)" />
</svg>

我想知道是否可以计算标记结束位置的值,如下图enter image description here所示,红色圆圈指向红色的圆圈? 是否有可能在数学上计算标记结束的位置?

1 个答案:

答案 0 :(得分:1)

您唯一需要做的就是使用以下公式来获得红色圆圈的正确位置(所以箭头的终点):

this.point.x = r * Math.cos(rad) + this.line.endX;
this.point.y = r * Math.sin(rad) + this.line.endY;

基本上对于位置x:

posX = arrowSizeX[width of the arrow * line stroke width] * Math.cos(rad)[rad = angle of the line in radian] + lineEndX[Position of the starting point of the arrow]

以下完整代码: JSFiddle

var vue = new Vue({
  el: '#container',
  data: {
    svg: {
      width: 400,
      height: 200
    },
    line: {
      startX: 50,
      startY: 50,
      endX: 250,
      endY: 150
    },
    point: {
      x: 0,
      y: 0
    },
    arrow: {
      sizeX: 9,
      sizeY: 6
    },
    strokeWidth: 5
  },
  ready: function() {
    this.calculatePosition();
  },
  methods: {
    calculatePosition: function() {
      // Calculate the angle of the arrow in radian
      var rad = Math.atan2(this.line.endY - this.line.startY, this.line.endX - this.line.startX);

      // Calculate the radius (the length of the arrow)
      // Note: Your arrow size depends on the the 'strokeWidth' attribute of your line
      var r = this.arrow.sizeX * this.strokeWidth;

      // Calculate the position of the point
      this.point.x = r * Math.cos(rad) + this.line.endX;
      this.point.y = r * Math.sin(rad) + this.line.endY;
    }
  }
});

vue.$watch('arrow.sizeX', vue.calculatePosition);
vue.$watch('arrow.sizeY', vue.calculatePosition);
vue.$watch('line.startX', vue.calculatePosition);
vue.$watch('line.startY', vue.calculatePosition);
vue.$watch('line.endX', vue.calculatePosition);
vue.$watch('line.endY', vue.calculatePosition);
vue.$watch('strokeWidth', vue.calculatePosition);
input,
label,
button {
  display: block;
}
#toolbar {
  display: inline-block;
  width: 150px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="container">
  <div id="toolbar">
    <label>Arrow size x:</label>
    <input type="range" min="1" max="10" v-model="arrow.sizeX" v-on:change="calculatePosition()" />
    <label>Arrow size y:</label>
    <input type="range" min="1" max="10" v-model="arrow.sizeY" />
    <label>Line start x:</label>
    <input type="range" min="0" max="{{svg.width}}" v-model="line.startX" />
    <label>Line start y:</label>
    <input type="range" min="0" max="{{svg.height}}" v-model="line.startY" />
    <label>Line end x:</label>
    <input type="range" min="0" max="{{svg.width}}" v-model="line.endX" />
    <label>Line end y:</label>
    <input type="range" min="0" max="{{svg.height}}" v-model="line.endY" />
    <label>Stroke width:</label>
    <input type="range" min="1" max="10" v-model="strokeWidth" />
  </div>
  <svg width="{{svg.width}}" height="{{svg.height}}">
    <defs>
      <marker id="arrow" markerWidth="10" markerHeight="10" refx="0" refy="{{arrow.sizeY / 2}}" orient="auto" markerUnits="strokeWidth">
        <path d="M0,0 L0,{{arrow.sizeY}} L{{arrow.sizeX}},{{arrow.sizeY / 2}} z" fill="#000" />
      </marker>
    </defs>
    <circle cx="{{point.x}}" cy="{{point.y}}" r="3" fill="red"></circle>
    <line x1="{{line.startX}}" y1="{{line.startY}}" x2="{{line.endX}}" y2="{{line.endY}}" stroke="#000" stroke-width="{{strokeWidth}}" marker-end="url(#arrow)" />
  </svg>
</div>