Arc rendering bug? - svg

This is my first question so please be gentle.
I'm not a programmer and not very knowledgeable about these things.
I have been messing around with svg and raphael.js and have encountered a "problem" with rendering of arcs.
To simplify things let's forget about raphael and consider the code below:
<html>
<body>
<svg width="600px" height="400px" viewBox="0 0 600 400">
<circle cx="200" cy="200" r="180" style="stroke: red; fill: none;"/>
<line x1="200" y1="220" x2="200" y2="180" style="stroke: black"/>
<line x1="180" y1="200" x2="220" y2="200" style="stroke: black"/>
<path d="M200,200 L20,208 A180,180 0 0,1 378,170 z" style="stroke: blue; fill:none"/>
<path d="M200,200 L20,208 A180,180 0 0,0 378,170 z" style="stroke: green; fill:none"/>
</svg>
</body>
</html>
Assuming that the circle has been rendered correctly, I expected the two arcs to fall right on top of the circle, instead they appear offset. (Don't have enough rep to post picture.)
I have tested this in Chrome 43, Firefox 40 and Opera 30.They all produce the same outcome.
Have I misunderstood how the arc spec works?
I also noticed that if we change the "A180,180" to "A100,100" it does not affect the result which I find rather odd.
Update
I'm updating the question as the information given is incomplete.
Here's the code with Raphael:
<html>
<head>
<script type="text/javascript" src="raphael.js"></script>
</head>
<body>
<div id="pie" style="width:600px; height:400px;"></div>
<script type="text/javascript">
var colorArr = ["#468966", "#FFF0A5", "#FFB03B"];
var sectorAngles = [87.513812, 173.038674, 99.447514];
var paper = Raphael("pie");
var startAngle = 0;
var endAngle = 90;
var pieX = 200;
var pieY = 200;
var pieRadius = 180;
var x1, y1, x2, y2;
var arcDir = 1; //clockwise
for (var i = 0; i < sectorAngles.length; i++) {
startAngle = endAngle;
endAngle = startAngle + sectorAngles[i];
var flag = (endAngle - startAngle) > 180.0;
x1 = Math.round( pieX + pieRadius * Math.cos(Math.PI * startAngle / 180.0) );
y1 = Math.round( pieY + pieRadius * Math.sin(Math.PI * startAngle / 180.0) );
x2 = Math.round( pieX + pieRadius * Math.cos(Math.PI * endAngle / 180.0) );
y2 = Math.round( pieY + pieRadius * Math.sin(Math.PI * endAngle / 180.0) );
var d = "M" + pieX + "," + pieY + " L" + x1 + "," + y1 + " A" + pieRadius + "," + pieRadius + " 0 " + +flag + "," + arcDir + " " + x2 + "," + y2 + " z";
var arc = paper.path(d);
arc.attr("fill", colorArr[i]);
arc.attr("stroke-width", 0.1);
}
paper.circle( pieX, pieY, pieRadius ).attr({
stroke: "red", "stroke-width": 0.2
});
</script>
</body>
</html>

The red circle is centred at 200,200 and has a radius of 180.
For the two arcs to be perfectly superimposed, their endpoints (20,208 and 378,170) would need to sit exactly on the circumference of that circle.
To check, lets calculate the distance of those two points from the centre of the circle.
sqrt((20-200)^2 + (208-200)^2) = 180.177
sqrt((378-200)^2 + (170-200)^2) = 180.510
Those radius differences are enough to cause the misalignment. If you use more accurate coordinates in your arcs, you will get better superimposition.
Note also, the green arc should have its "large-arc-flag" set because it is over 180 degrees.
<svg width="600px" height="400px" viewBox="0 0 600 400">
<circle cx="200" cy="200" r="180" style="stroke: red; fill: none;"/>
<line x1="200" y1="220" x2="200" y2="180" style="stroke: black"/>
<line x1="180" y1="200" x2="220" y2="200" style="stroke: black"/>
<path d="M200,200 L20.178,208 A180,180 0 0,1 377.482,170 z" style="stroke: blue; fill:none"/>
<path d="M200,200 L20.178,208 A180,180 0 1,0 377.482,170 z" style="stroke: green; fill:none"/>
</svg>

Related

How to draw arrow between ellipses

I'm trying to programmatically draw an arrow from one ellipse to another, like below. How do I calculate where on ellipse B's edge the arrow needs to point? Or is there an easier way to do this in SVG? I am given the x and y coordinates of each ellipse along with their horizontal and vertical radiuses. From those coordinates and radiuses, I need to draw an arrow.
In SVG you can draw an arrow at the end of a line by using a marker.
In order to know the starting and ending points of the line you need to calculate them as points on the ellipse at a given angle.
Please read the comments in the code.
//object with the first ellipse's attributes
let e1 = { cx: 20, cy: 50, rx: 10, ry: 5 };
//object with the second ellipse's attributes
let e2 = { cx: 120, cy: 20, rx: 10, ry: 5 };
//the distance in x between ellipses
let dx = e2.cx - e1.cx;
//the distance in y between ellipses
let dy = e2.cy - e1.cy;
//the angle
let angle = Math.atan2(dy, dx);
//the starting point of the line as a point on the first ellipse
let p1 = {
x: e1.cx + e1.rx * Math.cos(angle),
y: e1.cy + e1.ry * Math.sin(angle)
};
//the ending point of the line as a point on the second ellipse
let p2 = {
x: e2.cx + e2.rx * Math.cos(angle + Math.PI),
y: e2.cy + e2.ry * Math.sin(angle + Math.PI)
};
//setting the attributes of the line
l1.setAttribute("x1", p1.x);
l1.setAttribute("x2", p2.x);
l1.setAttribute("y1", p1.y);
l1.setAttribute("y2", p2.y);
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<marker id="mk" viewBox="0 0 4 4" markerWidth="4" markerHeight="4" refX="4" refY="2" orient="auto">
<polygon points="0,0 4,2 0,4" />
</marker>
<ellipse cx="20" cy="50" rx="10" ry="5" />
<ellipse cx="120" cy="20" rx="10" ry="5" />
<line id="l1" stroke="black" marker-end="url(#mk)" />
</svg>

Correct tileable pattern at D3.JS / SVG

Having an random set of paths with different dimensions. The task is to set a uniform even pattern on them. By now, it's looks like:
Needs to be
I have played with different preserveAspectRatio attribute as well as viewBox one, but without any success.
It could be done by cloning start along the screen and apply shapes as clip-paths, the task is to do everything with patterns.l
const data = {
poly0: [{"x":107,"y":392.2},{"x":186,"y":361},{"x":257,"y":355.3},{"x":257,"y":333.9},{"x":107,"y":372.4}],
poly1: [{"x":107,"y":406.5},{"x":257,"y":368},{"x":257,"y":355.3},{"x":186,"y":361},{"x":107,"y":392.2}],
poly2: [{"x":107,"y":463.8},{"x":107,"y":521},{"x":257,"y":467},{"x":360,"y":527},{"x":488,"y":486},{"x":621,"y":540},{"x":677,"y":486},{"x":677,"y":453},{"x":677,"y":420.1},{"x":621,"y":433.9},{"x":488,"y":370.9},{"x":360,"y":435.5},{"x":257,"y":368},{"x":107,"y":406.5}],
poly3: [{"x":257,"y":368},{"x":360,"y":435.5},{"x":488,"y":370.9},{"x":488,"y":338.5},{"x":360,"y":363.5},{"x":257,"y":333.9}],
poly4: [{"x":488,"y":354.5},{"x":621,"y":386.3},{"x":677,"y":388.5},{"x":677,"y":362.5},{"x":621,"y":338.5},{"x":488,"y":338.5}],
poly5: [{"x":488,"y":370.9},{"x":621,"y":433.9},{"x":677,"y":420.1},{"x":677,"y":388.5},{"x":621,"y":386.3},{"x":488,"y":354.5}]
};
let svg, g2, EPS = 1E-5, curve = d3.line().curve(d3.curveCatmullRom).x(d_ => { return d_.x; }).y(d_ => { return d_.y; });
svg = d3.select("#scene");
let colors = ["#005f73", "#0a9396", "#94d2bd", "#e9d8a6", "#ee9b00", "#ca6702", "#bb3e03", "#ae2012"];
let paths = svg.selectAll(".path")
.data(Object.keys(data))
.enter()
.append("path")
.attr("id", (d_) => { return `path_${d_}`; })
.attr("class", "path")
.attr("d", (d_) => { return generatePathFromPoints(data[d_], true); })
.attr("stroke", "#000000")
.attr("fill", "url(#star)");
function generatePathFromPoints(points_, closed_){
let d = `M${points_[0].x} ${points_[0].y}`;
for(let i = 1; i < points_.length; i++) { d += `L${points_[i].x} ${points_[i].y}`; }
if(closed_) { d += "Z"; }
return d;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="scene" viewBox="0 0 800 800" preserveAspectRatio="xMinYMin meet">
<defs>
<pattern id="star" viewBox="0 0 10 10" width="10%" height="10%" preserveAspectRatio="none">
<polygon points="0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2" fill="#000000"/>
</pattern>
</defs>
</svg>
Basically the behavior comes from the SVG pattern width="10%" height="10%" preserveAspectRatio="none", which instruct the pattern to be 10% the size of what it fills (paint).
While you probably needs it to be 10% the size of the whole SVG, which can be achieved by setting the patternUnits attribute to userSpaceOnUse. So it becomes:
<pattern id="star" patternUnits="userSpaceOnUse" viewBox="0 0 10 10" width="10%" height="10%" preserveAspectRatio="none">
<polygon points="0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2" fill="#000000"/>
</pattern>

How to rewrite a circular radial gradient starting at origo from objectboundingbox to userspaceonuse?

Here I have a radial gradient expressed in objectBoundingBox coordinates:
<svg width="800" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<radialGradient id="myGradient3" cx="75%" fx="75%" cy="25%" fy="25%" r="50%" fr="0%">
<stop offset="25%" stop-color="gold" />
<stop offset="75%" stop-color="red" />
</radialGradient>
</defs>
<circle cx="100" cy="100" r="100" fill="url('#myGradient3')" />
</svg>
Consider it an example of a radial gradient with the following invariants:
The radial gradient is circular (not elliptical)
Only one shape refers to the radial gradient.
The attributes cx, cy, fx, fy, fr, r are set
What should I recalculate the attributes to if I want to convert it to use userSpaceOnUseCoordinates?
Here is my best attempt in pseudo code
const shapeBox = shape.getBBox() // {x: 0, y: 0, width: 200, height: 200}
userspaceCX = cx * bbox.width // 150 = 75% * 200
userspaceFX = fx * bbox.width // 150 = 75% * 200
userspaceCY = cy * bbox.height // 50 = 25% * 200
userspaceFY = fy * bbox.height // 50 = 25% * 200
userspaceR = r * sqrt(bbox.height^2 + bbox.width^2) // 70.71 = 50% * sqrt(5000) = 50% * sqrt(200^2 + 200^2)
userspaceFR = fr * sqrt(bbox.height^2 + bbox.width^2) // 0 = 0% * sqrt(5000) = 50% * sqrt(200^2 + 200^2)
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="200">
<defs>
<radialGradient id="myGradient3__1" cx="150" fx="150" cy="50" fy="50" r="70.71" fr="0" gradientUnits="userSpaceOnUse">
<stop offset="25%" stop-color="#ffd700"/>
<stop offset="75%" stop-color="#f00"/>
</radialGradient>
</defs>
<circle cx="100" cy="100" r="100" fill="url(#myGradient3__1)"/>
</svg>
But as you can see if you run the code snippets, the converted image is a little different from the original image.
What am I missing to make this work?
The calculation for diagonal length should be
sqrt(bbox.height^2 + bbox.width^2) / sqrt(2)
instead of
sqrt(bbox.height^2 + bbox.width^2)

SVG - create path for textpath dynamically?

I want to have a curved text along a path (half circle) in SVG. I have followed this tutorial, which is great: https://css-tricks.com/snippets/svg/curved-text-along-path/
The problem is that the path presented there works only for this specific text - Dangerous Curves Ahead. If you leave only Dangerous word that's what happens: https://codepen.io/anon/pen/pqqVGa - it no longer works (the text is no more evenly spreaded across the path).
I want to have it work regardless of text length. How to achieve that?
Using the attributes lengthAdjust and textLength you can adjust the length of the text and the height of the letters, thereby placing the text of the desired length on a segment of a fixed length
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="300" viewBox="0 0 500 300">
<path id="path1" fill="none" stroke="black" d="M30,151 Q215,21 443,152 " />
<text id="txt1" lengthAdjust="spacingAndGlyphs" textLength="400" font-size="24">
<textPath id="result" method="align" spacing="auto" startOffset="1%" xlink:href="#path1">
<tspan dy="-10"> very long text very long text very long text </tspan>
</textPath>
</text>
</svg>
Using the attribute startOffset =" 10% " you can adjust the position of the first character of the phrase
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="300" viewBox="0 0 500 300" >
<path id="path1" fill="none" stroke="black" d="M30,151 Q215,21 443,152 " />
<text id="txt1" lengthAdjust="spacingAndGlyphs" textLength="400" font-size="24">
<textPath id="result" method="align" spacing="auto" startOffset="15%" xlink:href="#path1">
<tspan dy="-10"> very long text very long text very long text </tspan>
</textPath>
</text>
</svg>
and make animation using this attribute (click canvas)
<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="500" height="300" viewBox="0 0 500 300">
<path id="path1" fill="none" stroke="black" d="M30,151 Q215,21 443,152 " />
<text id="txt1" lengthAdjust="spacingAndGlyphs" textLength="200" font-size="24">
<textPath id="result" method="align" spacing="auto" startOffset="-100%" xlink:href="#path1">
<tspan dy="-10"> Very long text Very long text Very long text </tspan>
<animate
begin="svg1.click"
dur="15s"
attributeName="startOffset"
values="-100%;1%;1%;100%;1%;1%;-100%"
repeatCount="5"/>
</textPath>
</text>
<text x="200" y="150" font-size="24" fill="orange" >Click me </text>
</svg>
This is assuming that the initial text size (35) is too small.
let curveLength = curve.getTotalLength();
let fs = 35;//the initial font size
test.setAttributeNS(null, "style", `font-size:${fs}px`)
while(test.getComputedTextLength() < curveLength){
fs++
test.setAttributeNS(null, "style", `font-size:${fs}px`)
}
body {
background-color: #333;
}
text {
fill: #FF9800;
}
<svg viewBox="0 0 500 500">
<path id="curve" d="M73.2,148.6c4-6.1,65.5-96.8,178.6-95.6c111.3,1.2,170.8,90.3,175.1,97" />
<text id="test">
<textPath xlink:href="#curve">
Dangerous
</textPath>
</text>
</svg>
UPDATE
The OP is commenting:
Thanks for the response. Instead of adjusting the font size, I would prefer to create a new path that is longer / smaller and matches the text width. Not sure how to do this tho. – feerlay
Please read the comments in the code. In base of the length of the text I'm calculating the new path, but I'm assuming a lot of things: I'm assuming the new path starts in the same point as the old one.
let textLength = test.getComputedTextLength();
// the center of the black circle
let c = {x:250,y:266}
// radius of the black circle
let r = 211;
// the black arc starts at point p1
let p1 = {x:73.2,y:150}
// the black arc ends at point p2
let p2 = {x:426.8,y:150}
// distance between p1 and p2
let d = dist(p1, p2);
// the angle of the are begining at p1 and ending at p2
let angle = Math.asin(.5*d/r);
// the radius of the new circle
let newR = textLength / angle;
// the distance between p1 and the new p2
let newD = 2 * Math.sin(angle/2) * newR;
// the new attribute c for the path #curve
let D = `M${p1.x},${p1.y} A`
D += `${newR}, ${newR} 0 0 1 ${p1.x + newD},${p1.y} `
document.querySelector("#curve").setAttributeNS(null,"d",D);
// a function to calculate the distance between two points
function dist(p1, p2) {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
body {
background-color: #333;
}
text {
fill: #FF9800;
};
<svg viewBox="0 0 500 500">
<path id="black_circle" d="M73.2,148.6c4-6.1,65.5-96.8,178.6-95.6c111.3,1.2,170.8,90.3,175.1,97" />
<path id ="curve" d="M73.2,150 A 211,211 0 0 1 426.8,150" fill="#777" />
<text id="test">
<textPath xlink:href="#curve">
Dangerous curves
</textPath>
</text>
</svg>

Block arrow using SVG

I need to draw nice stroked block arrow using SVG from one point (x0,y0) to another (x1,y1), like the one on the picture.
The only way I can imagine is to use a line (two lines basically to simulate stroke and fill) with marker, but it looks kind of ugly due to overlaping strokes.
Ideally both line and marker should be filled with the same color and should have the same stroke color, and overall arrow width can be fixed (but if I could parametrize that as well it would be cool). Basically it should look the same as on picture provided and should be able to be drawn by just providing coordinates of two points.
Is it even possible?
I was bored, so here you go. I have written a function to generate a path of the right shape.
You just need to give it the "from" and "to" coords, the line width, arrowhead width, and arrowhead length.
Enjoy!
var from = {x: 50, y: 250};
var to = {x: 250, y: 100};
var lineWidth = 30;
var arrowheadWidth = 60;
var arrowheadLength = 50;
var svg = document.getElementById("test");
drawArrow(svg, from, to, lineWidth, arrowheadWidth, arrowheadLength);
function drawArrow(svg, from, to, lineWidth, arrowheadWidth, arrowheadLength)
{
var dx = to.x - from.x;
var dy = to.y - from.y;
// Calculate the length of the line
var len = Math.sqrt(dx * dx + dy * dy);
if (len < arrowheadLength) return;
// The difference between the line width and the arrow width
var dW = arrowheadWidth - lineWidth;
// The angle of the line
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
// Generate a path describing the arrow. For simplicity we define it as a
// horizontal line of the right length, and starting at 0,0. Then we rotate
// and move it into place with a transform attribute.
var d = ['M', 0, -lineWidth/2,
'h', len - arrowheadLength,
'v', -dW / 2,
'L', len, 0,
'L', len - arrowheadLength, arrowheadWidth / 2,
'v', -dW / 2,
'H', 0,
'Z' ];
var path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("d", d.join(' '));
path.setAttribute("transform", "translate("+from.x+","+from.y+") rotate("+angle+")");
path.setAttribute("class", "arrow-line");
svg.appendChild(path);
}
.arrow-line {
fill: gold;
stroke: black;
stroke-width: 6;
}
<svg id="test" width="300" height="300">
</svg>
the easiest way to do this is to just use script to create an arrow.
Here i simply determine the length and the angle of the arrow from the two points p1 and p2, and then create a simple path in the correct length and rotate it by the calculated angle:
svgns="http://www.w3.org/2000/svg"
function arrow(p1,p2){
var h1=15 // line thickness
var h2=35 // arrow height
var w2=22 // arrow width
var deg = Math.atan2(p1.y - p2.y, p1.x - p2.x) * (180 / Math.PI);
var len = Math.sqrt(Math.pow(p1.y - p2.y,2)+Math.pow(p1.x - p2.x,2))
var arr = document.createElementNS(svgns,"path")
var d = `M${p1.x} ${p1.y-h1/2}v${h1}h${h2/2-len}v${(h2-h1)/2}l${-w2} ${-h2/2}l${w2} ${-h2/2}v${(h2-h1)/2}z`
arr.setAttribute("d",d)
arr.setAttribute("transform",`rotate(${deg} ${p1.x} ${p1.y})`)
arr.classList.add("arrow")
return arr
}
var a1 = arrow({x:50,y:50},{x:200,y:200})
var a2 = arrow({x:450,y:50},{x:300,y:200})
var a3 = arrow({x:450,y:450},{x:300,y:300})
var a4 = arrow({x:50,y:450},{x:200,y:300})
svg.appendChild(a1)
svg.appendChild(a2)
svg.appendChild(a3)
svg.appendChild(a4)
.arrow{stroke-width:3px}
.arrow:nth-of-type(1){fill:green;stroke:lime}
.arrow:nth-of-type(2){fill:red;stroke:orange}
.arrow:nth-of-type(3){fill:blue;stroke:turquoise}
.arrow:nth-of-type(4){fill:violet;stroke:pink}
<svg id="svg" viewBox="0 0 500 500" width="400" height="400">
</svg>
if you try to be fancy and find a scriptless solution, there will be a lot of loop you have to hop...
you will need at least 4 arrows each pointing from top left to bottom right, from top right to bottom left, from bottom left to top right and from bottom right to top left...
here is a prove of concept that it is doable, but i strongly advice against it...
svg{overflow:visible;}
<svg width="200" height="200" style="overflow:visible" stroke="red" color="orange" opacity="0.5">
<marker id="ah" viewBox="0 0 10 10" orient="auto" refX="10" refY="5" overflow="visible">
<path d="M0 0L10 5L0 10z" stroke-width="1"/>
</marker>
<marker id="ah2" viewBox="0 0 10 10" orient="auto" refX="10" refY="5">
<path d="M0 0L10 5L0 10z" fill="currentColor" stroke="none"/>
</marker>
<marker id="block" viewBox="0 0 10 10" orient="auto" refX="9" refY="5">
<rect x="0" y="0" width="10" height="10" stroke="white" stroke-width="1"/>
</marker>
<marker id="block2" viewBox="0 0 10 10" orient="auto" refX="9" refY="5">
<rect x="0" y="0" width="10" height="10" stroke-width="5"/>
</marker>
<mask id="m1">
<rect x="-10%" y="-10%" width="110%" height="110%" fill="white"/>
<line x1="99.999%" y1="99.999%" x2="100%" y2="100%" stroke-width="20" marker-end="url(#block)"/>
</mask>
<line x1="0.001%" y1="0.001%" x2="0%" y2="0%" stroke-width="8" marker-end="url(#block2)"/>
<line x1="0" y1="0" x2="100%" y2="100%" stroke-width="25" mask="url(#m1)"/>
<line x1="99.999%" y1="99.999%" x2="100%" y2="100%" stroke-width="20" marker-end="url(#ah)"/>
<line x1="0" y1="0" x2="100%" y2="100%" stroke-width="20" stroke="currentColor" mask="url(#m1)"/>
<line x1="99.999%" y1="99.999%" x2="100%" y2="100%" stroke-width="20" marker-end="url(#ah2)"/>
</svg>
After sitting a few hours of triple checking all my math:
Created an normalized arrow in the SVG defs tag
Then scaling the arrow after the provided coordinates.
(Added a static height XD)
document.addEventListener("DOMContentLoaded", function(event) {
var svgDoc = document.getElementById("arrowSvg");
var useArrow = svgDoc.getElementById("customArrow");
var extraData = useArrow.getAttribute("extra:data");
extraData = extraData.split(" ");
var x1 = parseInt(extraData[0]);
var x2 = parseInt(extraData[1]);
var y1 = parseInt(extraData[2]);
var y2 = parseInt(extraData[3]);
var arrowHeight = 15;
//Calculate the rotation needed
var deltaY = y1 - y2;
var deltaX = x2 - x1;
var angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
//Distance between the two points.
var distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
useArrow.setAttribute("transform",
"translate("+(x1+(deltaX/2))+" "+(y1-(deltaY/2))+") "+
"rotate(" + -1*angle +") " +
"matrix("+distance+", 0, 0, "+arrowHeight+", "+(0.5-distance*0.5)+","+(0.5-arrowHeight* 0.5)+")");
});
svg {
width: 50%;
border: 1px solid black;
}
.arrow {
stroke: black;
stroke-width: 0.05;
fill: yellow;
}
<svg id="arrowSvg" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:extra="ExtraNameSpace">>
<defs>
<path id="idArrow" class="arrow" d="M0,0.25 0.60,0.25
0.60,0 1,0.5 0.60,1
0.60,0.75 0,0.75z" />
</defs>
<!--- Extra Data Param: x1 x2 y1 y2--->
<use id="customArrow" xlink:href="#idArrow" extra:data="10 90 90 5" />
</svg>

Resources