Reputation: 2886
Would anyone have an idea whether/how it is possible to draw along a line between points while keeping a certain distance from the end points in SVG?
In case the line is horizontal (e.g. from (40,40)
to (100,40)
), you could easily draw a line that keeps a distance of about 20
to the points as follows
<line x1="40" y1="40" x2="100" y2="40" desc="directional line" />
<line x1="60" y1="40" x2="80" y2="40" desc="actual part of line" />
For diagonal lines, however, it is a bit harder. To draw an (easy) diagonal line from (40,40)
to (100,100)
, you would need cos(pi/4) = sin(pi/4) = sqrt(2)
to scale the distance you want to stay away from the end point (in this case: 20*sqrt(2) = 14.1
)
<line x1="40" y1="40" x2="100" y2="100" desc="directional line" />
<line x1="54.1" y1="54.1" x2="85.9" y2="85.9" desc="actual part of line" />
A demonstration of this code can be found in this fiddle
It thus seems possible to do it, when calculating
Is this the only way or is SVG capable of specifying parts of lines or are there smarter, more convenient ways of doing this?
Upvotes: 1
Views: 1276
Reputation: 6171
Once hacky way to do it is cheating with a circular pattern scaled to the size of your line. Not perfect but depends on your use case:
<svg width="200" height="200" viewbox="0 0 200 200">
<defs>
<pattern id="patt" width="1" height="1" patternContentUnits="objectBoundingBox">
<rect x="0" y="0" width="1" height="1" fill="cyan" />
<circle cx=".5" cy=".5" r=".4" fill="blue" />
</pattern>
</defs>
<g id="hand-drawn">
<line x1="40" y1="40" x2="100" y2="100" stroke="red" stroke-width="2" />
<line x1="54.1" y1="54.1" x2="85.9" y2="85.9" stroke="green" stroke-width="2" />
</g>
<g id="circle-pattern">
<line x1="80" y1="60" x2="200" y2="100" stroke="url(#patt)" stroke-width="2" />
<line x1="150" y1="10" x2="100" y2="120" stroke="url(#patt)" stroke-width="2" />
<line x1="0" y1="100" x2="150" y2="100" stroke="url(#patt)" stroke-width="2" />
<line x1="0" y1="100" x2="150" y2="101" stroke="url(#patt)" stroke-width="2" />
<g>
</svg>
Of course this only gives you a way to show a line that is a specific % from the ends, not an exact pixel value. Hope this gives you some ideas.
Note this is a bit buggy - it does not work for horizontal or vertical lines ... you would have to render them as rectangles or paths and use fill instead of stroke.
Upvotes: 0
Reputation: 7536
I'm not sure if this is smart or convinient, but one way to do this without script is the following.
You can use a rect as marker (marker-start and marker-end) and with the markerWidth and markerHeight property in combination with the stroke-width of the line, you can controll the size of the marker.
markerWidth * stroke-width = real width
<svg width="220" height="220">
<marker id="m1" orient="auto" viewBox="0 0 20 6" markerWidth="10" markerHeight="3" refX="20" refY="3">
<rect x="0" y="0" width="20" height="6" fill="red" opacity="0.5" />
</marker>
<marker id="m2" orient="auto" viewBox="0 0 20 6" markerWidth="10" markerHeight="3" refX="0" refY="3">
<rect x="0" y="0" width="20" height="6" fill="red" opacity="0.5" />
</marker>
<line id="l2" x1="20" y1="20" x2="200" y2="80" marker-end="url(#m1)" marker-start="url(#m2)" stroke="black" stroke-width="2" />
</svg>
now imagine we use a white rect, then the marker will overlap the line with a fixed width, and we would have a fixed distance to the endpoints.
But this is not really what we want. To really "cut" the line by the marker, you can then use a mask. So draw your line as a mask, with white stroke, but with black markers.
apply this mask to your line (without marker)... there you go: a line with a visible stroke that has a fixed distance to the endpoints.
pros: no javascript involved
cons: you have to draw your line twice
function redraw() {
var x1 = Math.random() * 200
var y1 = Math.random() * 200
var x2 = Math.random() * 200
var y2 = Math.random() * 200
l1.setAttribute("x1", x1)
l1.setAttribute("y1", y1)
l1.setAttribute("x2", x2)
l1.setAttribute("y2", y2)
l2.setAttribute("x1", x1)
l2.setAttribute("y1", y1)
l2.setAttribute("x2", x2)
l2.setAttribute("y2", y2)
c1.setAttribute("cx", x1)
c1.setAttribute("cy", y1)
c2.setAttribute("cx", x2)
c2.setAttribute("cy", y2)
}
line {
stroke-width: 2px
}
<svg width="220" height="220">
<marker id="m1" orient="auto" viewBox="0 0 20 6" markerWidth="10" markerHeight="3" refX="20" refY="3">
<rect x="0" y="0" width="20" height="6" fill="black" />
</marker>
<marker id="m2" orient="auto" viewBox="0 0 20 6" markerWidth="10" markerHeight="3" refX="0" refY="3">
<rect x="0" y="0" width="20" height="6" fill="black" />
</marker>
<mask id="mask">
<line id="l2" x1="20" y1="20" x2="200" y2="80" marker-end="url(#m1)" marker-start="url(#m2)" stroke="white" />
</mask>
<circle id="c1" cx="200" cy="80" r="5" fill="blue" />
<circle id="c2" cx="20" cy="20" r="5" fill="blue" />
<line id="l1" x1="20" y1="20" x2="200" y2="80" mask="url(#mask)" stroke="black" />
</svg>
<button onclick="redraw()">redraw</button>
Upvotes: 1