SVG marker is not displaying completely - svg

I'm trying to get an arrow marker to display at the end of a line, but for some reason, this doesn't work in any browser I have or any graphics viewer or editor, except for Inkscape.
Can anyone tell me what I'm doing wrong?
<?xml version="1.0" encoding="UTF-8" ?>
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 500 500" width="500" height="500"
style="stroke: rgb(0, 0, 0); fill: none; stroke-linecap: round; stroke-linejoin: round;">
<defs>
<marker id="arrow" refX="-2.33" refY="0" orient="auto">
<path d="m 0,0 -5,2 q 1.5,-2 0,-4 z"
style="fill: rgb(0, 0, 0); stroke-width: 1;"
transform="scale(0.6667 0.6667)" />
</marker>
</defs>
<g transform="translate(20 20)">
<path d="m 0,0 100,0" style="stroke-width: 3; marker-end: url(#arrow);" />
</g>
</svg>
Update
The right viewBox (without scaling the path inside the marker) is "-5.5 -2.5 6 5". The 4 parameters of the viewBox are min-x, min-y, width, and height. They have to take into account whatever stroke-width the marker's elements may have, hence the extra 0.5 that I needed on either side.
Turns out, markerHeight and markerWidth are important to set, because the default value for both is 3. That's why the arrow was coming out too small in my earlier attempt. For 1-to-1 scaling, they should be the same as the last two numbers in the viewBox.
All of the viewBox, markerHeight, and markerWidth need to be adjusted accordingly when scaling elements inside the marker.
Here's a complete solution based on the answer and the comments for posterity:
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 30 10" width="300" height="100"
style="stroke: #000000; fill: none; stroke-linecap: round; stroke-linejoin: round;">
<defs>
<marker id="arrow" refX="-1.333" refY="0"
orient="auto" markerUnits="strokeWidth"
viewBox="-3.667 -1.667 4 3.333"
markerWidth="4" markerHeight="3.333"
>
<path d="m 0,0 -5,2 q 1.5,-2 0,-4 z"
style="fill: #000000; stroke-width: 1;"
transform="scale(0.667)"
/>
</marker>
</defs>
<g transform="translate(5 5)">
<path d="m 0,0 10,0" style="stroke-width: 1.5; marker-end: url(#arrow);" />
</g>
<g transform="translate(25 5)">
<path d="m 0,0 -5,2 q 1.5,-2 0,-4 z"
style="fill: rgb(0, 0, 0); stroke-width: 1;" />
</g>
</svg>

The simplest thing is probably to add a viewBox to the marker so the shape is actually drawn in the marker canvas and not outside it.
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 500 500" width="500" height="500"
style="stroke: rgb(0, 0, 0); fill: none; stroke-linecap: round; stroke-linejoin: round;">
<defs>
<marker id="arrow" refX="-2.33" refY="0" orient="auto" viewBox="-3 -3 5 5">
<path d="m 0,0 -5,2 q 1.5,-2 0,-4 z"
style="fill: rgb(0, 0, 0); stroke-width: 1;"
transform="scale(0.6667 0.6667)" />
</marker>
</defs>
<g transform="translate(20 20)">
<path d="m 0,0 100,0" style="stroke-width: 3; marker-end: url(#arrow);" />
</g>
</svg>

Related

Chartist.js, SVG circle element is not showing up in the container

I'm using Chartist.js to work on a pie chart.
I am trying to add a simple circle in the background, but for some reason the circle element just ends up in the top left corner with 0x0 dimensions.
Here is the SVG:
<div class="contents" id="chart-container">
<svg xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="100%" height="100%" class="ct-chart-pie" style="width: 100%; height: 100%;" viewBox="0 0 359 219">
<g class="basecircle">
<cirlce class="basecircleelement" cx="10" cy="10" fill="black" cr="181.76999999999998">
</cirlce>
</g>
<g ct:series-name="value" class="ct-series ct-series-a">
<path d="M147.071,10.108A104.375,104.375,0,1,0,179.325,5L179.325,109.375Z" class="ct-slice-pie" ct:value="95" style="fill: gray; stroke: white; stroke-width: #fff;"></path>
</g>
<g ct:series-name="rest" class="ct-series ct-series-b">
<path d="M179.325,5A104.375,104.375,0,0,0,146.725,10.222L179.325,109.375Z" class="ct-slice-pie" ct:value="5" style="fill: transparent;"></path>
</g>
</svg>
</div>
I expected the "g.basecircle" and "circle.basecircleelement" should be showing up(It's supposed to be in the center of the container but I didn't calculate the cx and cy yet) with radius of 181.76999999999998 -this value is calculated to be half of the clientWidth-, but it ends up in the upper left corner of the SVG element with dimension of 0 x 0
Is there anything I am missing?
this value is calculated to be half of the clientWidth-, but it ends
up in the upper left corner of the SVG element with dimension of 0 x 0
Typos must be fixed:
circle instead ofcirlce
Radius designation -r instead ofcr
Svg canvas borders are bounded by a red square style = "border: 1px solid red;"
It is very convenient to visually see the borders of SVG. When you finish debugging your application, this line can be removed.
To place the black circle in the center of the svg canvas, you need to set the coordinates cx =" 359/2 " cy =" 219/2 "
<div class="contents" id="chart-container">
<svg xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="100%" height="100%" class="ct-chart-pie" viewBox="0 0 359 219" style="border:1px solid red;">
<g class="basecircle">
<circle class="basecircleelement" cx="179.5" cy="109.5" fill="black" r="179.5">
</circle>
</g>
<g ct:series-name="value" class="ct-series ct-series-a">
<path d="M147.071,10.108A104.375,104.375,0,1,0,179.325,5L179.325,109.375Z" class="ct-slice-pie" ct:value="95" style="fill: gray; stroke: white; stroke-width: #fff;"></path>
</g>
<g ct:series-name="rest" class="ct-series ct-series-b">
<path d="M179.325,5A104.375,104.375,0,0,0,146.725,10.222L179.325,109.375Z" class="ct-slice-pie" ct:value="5" style="fill: transparent;"></path>
</g>
</svg>
</div>
Update
If you want the black circle to not form and completely fit on the canvas svg you need to set its radius equal to half the height of the canvas r= 219 / 2 = 109.5
<div class="contents" id="chart-container">
<svg xmlns:ct="http://gionkunz.github.com/chartist-js/ct" width="100%" height="100%" class="ct-chart-pie" viewBox="0 0 359 219" style="border:1px solid red;">
<g class="basecircle">
<circle class="basecircleelement" cx="179.5" cy="109.5" fill="black" r="109.5">
</circle>
</g>
<g ct:series-name="value" class="ct-series ct-series-a">
<path d="M147.071,10.108A104.375,104.375,0,1,0,179.325,5L179.325,109.375Z" class="ct-slice-pie" ct:value="95" style="fill: gray; stroke: white; stroke-width: #fff;"></path>
</g>
<g ct:series-name="rest" class="ct-series ct-series-b">
<path d="M179.325,5A104.375,104.375,0,0,0,146.725,10.222L179.325,109.375Z" class="ct-slice-pie" ct:value="5" style="fill: transparent;"></path>
</g>
</svg>
</div>

Why does my circle's marker not appear fully?

source: https://codepen.io/joondoe/pen/yLBMXPX?editors=1100
I would know why my circle fails to appear entirely since there is space place for it to display fully.
#import url('https://fonts.googleapis.com/css?family=PT+Sans');
body {
font-family: 'PT Sans', sans-serif;
padding: 2rem;
}
<h2>Beginning SVG: Drawing paths and marker</h2>
<svg width="1000" height="1000" >
<defs>
<marker
id="circle1" markerWidth="385" markerHeight="359"
refX="15" refY="5"
orient="auto"
>
<circle cx="15" cy="5" r="13" fill="black"/>
<circle cx="15" cy="5" r="12" fill="lightgray"/>
<path d="M 4,3.5 L 6.5,5 L4,6.5 Z" fill="slategray"/>
</marker>
</defs>
<line
x1="100" y1="220" x2="250" y2="200"
stroke="black" stroke-width="5"
marker-end="url(#circle1)"
/>
</svg>
Markers have a rectangular clipping area at the bounds of their tile. That's 0,0 to 385, 359 in your case so you need to draw the marker within that rect.
A circle with a cy="5" and r="13" will render above the y axis and therefore outside the clipping rect.
We can simply translate the marker contents and then move the marker refY to compensate. I've set the markerWidth and markerHeight at more sensible values too as having them too large prevents the browser from optimising marker rendering.
#import url('https://fonts.googleapis.com/css?family=PT+Sans');
body {
font-family: 'PT Sans', sans-serif;
padding: 2rem;
}
<h2>Beginning SVG: Drawing paths and marker</h2>
<svg width="1000" height="1000" viewBox="0 0 2000 2000">
<defs>
<marker
id="circle1" markerWidth="40" markerHeight="40"
refX="15" refY="25"
orient="auto"
>
<g transform="translate(0,20)">
<circle cx="15" cy="5" r="13" fill="black"/>
<circle cx="15" cy="5" r="12" fill="lightgray"/>
<path d="M 4,3.5 L 6.5,5 L4,6.5 Z" fill="slategray"/>
</g>
</marker>
</defs>
<line
x1="100" y1="220" x2="250" y2="200"
stroke="black" stroke-width="5"
marker-end="url(#circle1)"
/>
</svg>

Centering text in SVG path

I am trying to center horizontally and vertically text in path with the
startOffset and text-anchor attributes, but centering doesn't work.
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
<svg id="line-vis" width="640" height="300" viewBox="0 0 640 300" preserveAspectRatio="none" style="cursor: pointer; overflow: visible; position: relative;">
<g>
<g class="line" pointer-events="none" transform="translate(365.1979064941406,134.5977783203125)">
<path id="tooltip-line-vis" d="M120 60 L120 0 L0 0 L0 60 L54 60 L60 66 66 60" fill="#FFFFFF" stroke="#D2DBE9" transform="translate(-60,-75)" style="opacity: 1;"></path>
<text text-anchor="middle" style="opacity: 1;">
<textPath class="yLabelVal" xlink:href="#tooltip-line-vis" startOffset="50%" style="opacity: 1;font-size: 16px;font-weight: 500;line-height: 20px;fill: rgb(8, 40, 101);">$5,104.90</textPath>
</text>
</g>
<rect width="640" height="300" fill="none" pointer-events="all"></rect>
</g>
</svg>
In order to get what you need I've changed the points of the path. Now the path begins at 0,0. Also I've removed all the transforms. If you need your path in a different position you may use transforms on #tooltip as I did in my code.
Please note the attribute dy="35" for the text. This is moving your text upward & in the center.
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
<svg id="line-vis" width="640" height="300" viewBox="0 0 640 300" preserveAspectRatio="none" style="cursor: pointer; overflow: visible; position: relative;">
<g id="tooltip" transform="translate(50,10)">
<g class="line" pointer-events="none" >
<!--<path id="tooltip-line-vis" d="M0,60 L54,60 60,66 66,60 120,60 120,0 0,0 0,60" stroke="#D2DBE9" style="opacity: 1;"></path>-->
<path id="tooltip-line-vis" d="M0,0 L120,0 120,60 66,60 60,66 54,60 0,60 0,0" stroke="#D2DBE9" style="opacity: 1;"></path>
<text text-anchor="middle" style="opacity: 1;" fill="gold" dy="35">
<textPath class="yLabelVal" xlink:href="#tooltip-line-vis" startOffset="60" >$5,104.90</textPath>
</text>
</g>
<rect width="640" height="300" fill="none" pointer-events="all"></rect>
</g>
</svg>

What is the expected image for this SVG file?

I am trying to make a plot with each data point labelled on the x-axis with a text string written vertically. It all seems to work as expected except that the labels appear to be shifted left by one unit. I thought it was a bug in my c++ generating code but finally made a test case. In the following, I wanted the red and blue lines to underline the text but they
seem to go through the text. The green lines, offset by one, appear where
I thought the others should be relative to the text. What do I need to do here? The center of rotation does appear as expected. Thanks. Right now I'm just kluging the code to fix the offset but would like to fix it lol.
<svg style="overflow: hidden; -moz-user-select: none; cursor: default; position: relative; background-color: rgb(255, 255, 255);" xmlns="http://www.w3.org/2000/svg" width="100" version="1.1" height="100"><desc> test foo</desc>
<rect fill-opacity="1" stroke-width="1" stroke-opacity="1" style="" stroke="#00808080" fill="#00808080" ry="0" rx="0" r="0" height="100" width="100" y="0" x="0"></rect> <g fill="none" stroke="red" stroke-width="1000" />
<path d="M0 0 L50 0 " stroke="black" stroke-width="1"/>
<text font-weight="normal" font-size="5" font-family="Verdana" fill="#00ffffff" y="10" x="10" style="dominant-baseline: alphabetic; text-anchor: start;">unidentified</text>
<text transform="rotate(90,10,10)" font-weight="normal" font-size="5" font-family="Verdana" fill="#00ffffff" y="10" x="10" style="dominant-baseline: alphabetic; text-anchor: start;">unidentified</text>
<text transform="rotate(45,10,10)" font-weight="normal" font-size="5" font-family="Verdana" fill="#00ffffff" y="10" x="10" style="dominant-baseline: alphabetic; text-anchor: start;">unidentified</text>
<path d="M0 10 L100 10 " stroke="red" stroke-width=".1"/>
<path d="M0 11 L100 11 " stroke="green" stroke-width=".1"/>
<path d="M10 0 L10 100 " stroke="blue" stroke-width=".1"/>
<path d="M9 0 L9 100 " stroke="green" stroke-width=".1"/>
</svg>

SVG filter relative to bounding box with possibly zero length side, calc() alternative?

This is somewhat of a continuation of Humongous height value for <filter> not preventing cutoff: I am still trying to apply a <filter>on a <path> but I am having problems with things being clipped.
The problem was solved in the other thread by moving the center of the filter canvas using the x/y attributes on the <filter>, still everything was in percent and therefore relative to the size of the thing you are trying to apply the effect on, but the problem is that a side length can be 0 even in cases where you see something, see e.g. the top line in the following examples:
.pathWrapper path {
stroke: grey;
fill: none;
stroke-width: 1.5;
marker-start: url(#circle);
marker-end: url(#arrow);
}
.pathWrapper:hover {
filter: url(#colorFilter);
}
<svg style="height:400px;width:100%;background-color:LightCyan">
<defs>
<filter id="colorFilter" x="-300%" y="-300%" width="600%" height="600%">
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix>
<feMerge>
<feMergeNode in="lightenedBlur"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;">
<path d="M0,-5L10,0L0,5"></path>
</marker>
<marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker>
</defs>
<g transform="scale(2)">
<g class="pathWrapper" transform="translate(70,20)">
<path d="M52,10L45,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(70,50)">
<path d="M42,20L35,20L30,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(200,20)">
<path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path>
</g>
</g>
</svg>
Hover over the lines to see the filter effect applied there. You will see that the top line becomes invisible on hovering. This is because the bounding box height is 0, stroke width and marker knickknacks do not count:
I could use absolute units instead, e.g. like in the following example:
.pathWrapper path {
stroke: grey;
fill: none;
stroke-width: 1.5;
marker-start: url(#circle);
marker-end: url(#arrow);
}
.pathWrapper:hover {
filter: url(#colorFilter);
}
<svg style="height:400px;width:100%;background-color:LightCyan">
<defs>
<filter id="colorFilter" filterUnits="userSpaceOnUse" x="-125" y="-125" width="250" height="250">
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix>
<feMerge>
<feMergeNode in="lightenedBlur"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;">
<path d="M0,-5L10,0L0,5"></path>
</marker>
<marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker>
</defs>
<g transform="scale(2)">
<g class="pathWrapper" transform="translate(70,20)">
<path d="M52,10L45,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(70,50)">
<path d="M42,20L35,20L30,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(200,20)">
<path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path>
</g>
</g>
</svg>
The problem is that in my use case the sizes of the elements which have the effect applied to can vary quite a lot, so I would have to put an enormous value in there or otherwise there is always the change of it being not large enough (as shown in the example for the line spanning the largest height).
I found would that using CSS calc() could be a solution:
#colorFilter {
width: calc(100% + 100);
height: calc(100% + 100);
x: calc(-50% - 50);
y: calc(-50% - 50);
}
.pathWrapper path {
stroke: grey;
fill: none;
stroke-width: 1.5;
marker-start: url(#circle);
marker-end: url(#arrow);
}
.pathWrapper:hover {
filter: url(#colorFilter);
}
<svg style="height:400px;width:100%;background-color:LightCyan">
<defs>
<filter id="colorFilter" filterUnits="userSpaceOnUse">
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix>
<feMerge>
<feMergeNode in="lightenedBlur"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;">
<path d="M0,-5L10,0L0,5"></path>
</marker>
<marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker>
</defs>
<g transform="scale(2)">
<g class="pathWrapper" transform="translate(70,20)">
<path d="M52,10L45,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(70,50)">
<path d="M42,20L35,20L30,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(200,20)">
<path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path>
</g>
</g>
</svg>
This works in current versions of Google Chrome and Mozilla Firefox but does not appear to work in Micosoft Edge or IE11 (and from a bit of searching it seems as if calc() support is already quite touchy even for HTML content, see the Known issues section at https://caniuse.com/#search=calc).
So does an better alternative to the calc() approach exist?
(Maybe it's worth noting that I'm working with dynamically d3.js generated content.)
As you are working with generated content in d3, the best approach would be to insert an invisible rect into g.pathWrapper that covers the needed filter area.
I'm winging it here, you may have to adapt this to your logic (and maybe exchange the ES6 constructs for their ES5 equivalents). Suppose you have a list of points you are generating your path data from:
var points = [[52,10], [45,10], [-30,10], [-37,10]];
// get the min/max values
var x1 = d3.min(points,(p) => p[0]),
x2 = d3.max(points,(p) => p[0]),
y1 = d3.min(points,(p) => p[1]),
y2 = d3.max(points,(p) => p[1]);
// construct the path
var path = d3.path();
path.moveto(...points[0]);
for (var point of points.slice(1)) {
path.lineto(...point);
}
// wrapper
var wrapper = d3.select('svg > g').append('g')
.classed('pathWrapper');
// invisible rect with +5px in each direction as filter region
wrapper.append('rect')
.attr('fill', 'none')
.attr('x', x1 - 5)
.attr('y', y1 - 5)
.attr('width', x2 - x1 + 10)
.attr('height', y2 - y1 + 10);
// and the path itself
wrapper.append('path')
.attr('d', path);
After that, the defaults for the filter effects region should work as-is.
Just because you are using userSpaceOnUse values in the filter, it doesn't mean you can't still use percentages. It's just that the percentages are relative to the width and height of the SVG, instead of the width and height of the element.
Of course this does mean that the filter is being applied to a lot of extra, unnecessary, pixels. The filter is being applied to an area the size of the SVG instead of an area the size of the element. But since you are just filtering one element on hover, any slowness due to the extra work the browser is doing shouldn't be noticeable.
.pathWrapper path {
stroke: grey;
fill: none;
stroke-width: 1.5;
marker-start: url(#circle);
marker-end: url(#arrow);
}
.pathWrapper:hover {
filter: url(#colorFilter);
}
<svg style="height:400px;width:100%;background-color:LightCyan">
<defs>
<filter id="colorFilter" filterUnits="userSpaceOnUse">
<feGaussianBlur in="SourceAlpha" stdDeviation="1" result="blur"></feGaussianBlur>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 3 0" result="lightenedBlur"></feColorMatrix>
<feMerge>
<feMergeNode in="lightenedBlur"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<marker id="arrow" viewBox="0 -5 10 10" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto" style="fill: grey;">
<path d="M0,-5L10,0L0,5"></path>
</marker>
<marker id="circle" viewBox="0 -4 8 8" refX="0" refY="0" markerWidth="8" markerHeight="8" orient="auto-start-reverse" style="fill: grey;"><circle r="4" cx="4"></circle></marker>
</defs>
<g transform="scale(2)">
<g class="pathWrapper" transform="translate(70,20)">
<path d="M52,10L45,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(70,50)">
<path d="M42,20L35,20L30,10L-30,10L-37,10"></path>
</g>
<g class="pathWrapper" transform="translate(200,20)">
<path d="M42,140L35,140L-30,70L-30,10L-30,10L-37,10"></path>
</g>
</g>
</svg>

Resources