ashokgct ashokgct - 1 month ago 18
Javascript Question

How to get the marker-end position in svg?

Below is the code you can run and see the output which is a black coloured line with a marker at its end position.



<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>





I want to know whether its is possible to calculate the value of marker end position as shown in the image belowenter image description here with a circle in red color which is pointed by the arrow in red color?
Is it possible mathematically to calculate the position of marker end?

Answer

The only thing you have to do is use the following formulas to get the correct position of the red circle (so the end point of the arrow):

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

Basically for position 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]

Full code below: (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>

Comments