I'm making a data visualization like so:
and it would be convenient to use viewBox to scale the SVG so that the width of the graphic is scaled to be the width of the data range.
My data point in this example is 12,549, and so I want the range to run from 0 to 14,000, and render this in a viewBox of width 14,000 using a rect of width 12,549. In other words, to use natural units for my data.
But when I do this, the font on my axis also scales, and a 10px font also scales, and becomes so small that it can't be seen.
So what I need is a way to scale the drawing units without scaling the font units, but I can't see a way to do this.
Update
Here is the code in question:
<svg width="960" height="50" class="bullet2" style="margin-top: 10px;">
<svg viewBox="0 0 14000 25" preserveAspectRatio="none" width="100%" height="25" class="bars">
<rect x="0" y="0" width="14000" height="25" class="background"/>
<rect x="0" y="0" height="12.5" width="12549" class="item player"/>
<rect x="0" y="12.5" height="12.5" width="3750" class="item team-average"/>
<line x1="2000" x2="2000" y1="0" y2="25" class="marker" style="stroke-width: 29.1667px;"/>
</svg>
<g class="axis">
<g transform="translate(0, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">0</text>
</g>
<g transform="translate(140, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">2000</text>
</g>
<g transform="translate(280, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">4000</text>
</g>
<g transform="translate(420, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">6000</text>
</g>
<g transform="translate(560, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">8000</text>
</g>
<g transform="translate(700, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">10000</text>
</g>
<g transform="translate(840, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">12000</text>
</g>
<g transform="translate(980, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">14000</text>
</g>
</g>
</svg>
Note the viewBox on the inner svg element scaling it as described. I tried this with the same scaling on the outer svg element, which also includes the axis, but when I do this, the image looks like this:
The axis labels are there, but they are also scaled, and a 10px font out of a 14000 pixel scaling is so small that you can't see it.
This is my solution to your question. I recommend reading about SVGPoint and createSVGPoint. I consider especially useful the information found in this book: Using SVG with CSS3 and HTML5: Vector Graphics for Web Design
function init(){
// a function called on resize
// the function get the new font-size and set the new value of the "font-size"
let fontSize = 25;
let newSize = getValue(fontSize);
// reset the font size
theText.setAttributeNS(null,"font-size",newSize.x)
}
setTimeout(function() {
init();
addEventListener('resize', init, false);
}, 15);
// a function used to recalculate the font size on resize
function getValue(size){
var p = svg.createSVGPoint();
p.x = size;
p.y = 0;
p = p.matrixTransform(svg.getScreenCTM().inverse());
return p
}
svg{border:1px solid;}
<svg id="svg" viewBox="0 0 1000 100">
<line x1="50" x2="950" y1="50" y2="50" stroke-width="20" stroke="skyblue" />
<line x1="500" x2="500" y1="30" y2="70" stroke-width="1" stroke="#999" />
<text id="theText" dominant-baseline="middle" text-anchor="middle" x="500" y="85" font-size="16" >middle</text>
</svg>
Update
Can you explain how this solution works?
There are comments in the code. I'm also adding a quote from Using SVG with CSS3 and HTML5: Vector Graphics for Web Design:
Every SVG element that can take a transform [....] has a getScreenCTM() method. The CMT stands for cumulative transformation matrix. The screen CMT [....] defines how you can convert points in that element coordinate system back into the original, unscaled, untransformed coordinate system of the document window. It includes transformations on this element and it's ancestors plus viewBox scaling.
I hope this is useful. However I think you need to read the whole chapter 18 in the book to fully understand the code.
Please give a Minimal, Complete, and Verifiable example.
There are two approaches to your vague/general scaling problem:
1) Put the scaling SVG inside a container SVG? Stacking; and never scale the container which houses the fonts, relative to the percentage of the child SVG within.
2) It states here that fonts can be scaled using absolute px or relative em units. Please see here for valid length.
I do not see if the rem unit can be set, but if so, try if that works?
(I work with SVGs on occassion and CSS a lot, so there may be gaps in my suggestions)
Related
I just have this very simple svg below;
<div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">
<g stroke="#ccc">
<line x1="133" y1="50" x2="140" y2="40" stroke-width="2"></line>
</g>
<g stroke="#ccc">
<line x1="140" y1="40" x2="200" y2="40" stroke-width="2"></line>
</g>
<circle cx="100" cy="100" r="57" class="border" fill="#eee" stroke="none" stroke-width="15" stroke-dasharray="360" transform="rotate(-90,100,100)"></circle>
<circle class="circle" cx="100" cy="100" r="57" fill="none" stroke="#3498DB" stroke-width="5" stroke-dasharray="180, 20000" transform="rotate(-90,100,100)"></circle>
<text text-anchor="middle" x="100" y="110" class="icon" style="font-size: 40px" fill="#3498DB"></text>
<text class="timer" text-anchor="middle" x="175" y="35" style="font-size: 22px; undefined;" fill="#aaa">50%</text>
</svg>
</div>
FIDDLE HERE
The viewbox of the svg element is viewBox="0 0 194 300" and the y attribute on the icon is y="110" , my question is , is the Y attibute intentionally 110 ??
I.E. if i wanted to center the icon i would do the following:
(186/2)+ (height of icon/2) // 186 is the height of the viewbox
Am i right in assuming that the value of 110 is added in respect to the height of the viewbox ?
The y value of 110 will have been chosen by the author in order to vertically centre the text in the circle. It is not directly related to the viewBox. It is related to the centre of the circle, the size of the text (22) and to some extent the shape of the glyphs in whatever font is being used. For instance, the height of capital letters may vary for different fonts given the same font size.
SVG does not provide any way to automatically centre text vertically. So you have to position text manually.
Nope
dominant-baseline="central"
x="100"
100 is the vertical center of the circle (ry in the circle element) and dominant-baseline sets the text Y coordinate to the vertical center of the text.
I have the following really simple SVG code
<div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">
<g stroke="#ccc">
<line x1="133" y1="50" x2="140" y2="40" stroke-width="2"></line>
</g>
<g stroke="#ccc">
<line x1="140" y1="40" x2="200" y2="40" stroke-width="2"></line>
</g>
<circle cx="100" cy="100" r="57" class="border" fill="#eee" stroke="none" stroke-width="15" stroke-dasharray="360" transform="rotate(-90,100,100)"></circle>
<circle class="circle" cx="100" cy="100" r="57" fill="none" stroke="#3498DB" stroke-width="5" stroke-dasharray="180, 20000" transform="rotate(-90,100,100)"></circle>
<text text-anchor="middle" x="100" y="110" class="icon" style="font-size: 40px" fill="#3498DB"></text>
<text class="timer" text-anchor="middle" x="175" y="35" style="font-size: 22px; undefined;" fill="#aaa">50%</text>
</svg>
</div>
FIDDLE HERE, My difficulty is regarding the following svg element and how the text-anchor attribute works with it:
<text class="timer" text-anchor="middle" x="175" y="35" style="font-size: 22px; undefined;" fill="#aaa">50%</text>
Now if i change text-anchor="start" , the text element does't really move to the start of the svg element, it rather moves to the start of the line below it , why ? can anybody explain why text-anchor="start" , is not working as expected ?
text-anchor is used to decide whether the X position of the text should be at the start, the end or in the middle of the text. To move the text position change it's X and Y coordinates.
To put the text at the start of the svg:
x="0"
text-anchor="start"
To put the text at the end of the svg:
x="194" //Width of the svg
text-anchor="end"
To put the text in the middle of the svg:
x="97" //Half of the width of the svg
text-anchor="middle"
I have the following simple SVG:
<div class="svg-wrapper">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">
<g stroke="#ccc">
<line x1="133" y1="50" x2="140" y2="40" stroke-width="2" />
</g>
<g stroke="#ccc">
<line x1="140" y1="40" x2="200" y2="40" stroke-width="2" />
</g>
<circle cx="100" cy="100" r="57" class="border" fill="#eee" stroke="none" stroke-width="15" stroke-dasharray="360" transform="rotate(-90,100,100)" />
<circle class="circle" cx="100" cy="100" r="57" class="border" fill="none" stroke="#3498DB" stroke-width="5" stroke-dasharray="0,20000" transform="rotate(-90,100,100)" />
<text text-anchor="middle" x="100" y="110" class="icon" style="font-size: 40px" fill="#3498DB"></text>
<text class="timer" text-anchor="middle" x="175" y="35" style="font-size: 22px; undefined;" fill="#aaa">0%</text>
</div>
Now if you notice the SVG carefully, you will see that there are attibutes like the following:
x1="133" y1="50" x2="140" y2="40"
And the following for the circle:
cx="100" cy="100" r="57" ,
I just have one question about this, are these points relative to the document? Now I tried the following:
.svg-wrapper {
position: relative;
top: 200px;
left: 200px;
max-width: 200px;
}
I.E. Moved the container a bit by using position:relative, and the circle moved too, now I beleive if those points where relative to the document the circle should't have moved, but it clearly did and so it means that the following points are not relative to the document:
cx="100" cy="100" r="57"
And
x1="133" y1="50" x2="140" y2="40"
If not the document, then what are these points relative to? Can anybody explain?
The coordinate values are relative to the origin of the SVG, which is the top left of the SVG viewport.
The viewPort is defined by the <svg> element's width and height attributes. If you move the SVG by moving the container it is in, you move the SVG. Just the same as if it was an <img>, <canvas> etc.
It looks like the points are relative to the container, which would be in line with normal HTML in the sense that if you place an element within a container it's position then becomes relative to the container rather than the entire document.
I have the following very simple SVG:
<div id="circli" class="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">
<g stroke="#ccc">
<line x1="133" y1="50" x2="140" y2="40" stroke-width="2"></line>
</g>
<g stroke="#ccc">
<line x1="140" y1="40" x2="200" y2="40" stroke-width="2"></line>
</g>
<circle cx="100" cy="100" r="57" class="border" fill="#eee" stroke="none" stroke-width="15" stroke-dasharray="360" transform="rotate(-90,100,100)"></circle>
<circle class="circle" cx="100" cy="100" r="57" fill="none" stroke="#3498DB" stroke-width="5" stroke-dasharray="180, 20000" transform="rotate(-90,100,100)"></circle>
<text text-anchor="middle" x="100" y="110" class="icon" style="font-size: 40px" fill="#3498DB"></text>
<text class="timer" text-anchor="middle" x="175" y="35" style="font-size: 22px; undefined;" fill="#aaa">50%</text>
</svg>
</div>
My question is pertaining to the icon and how it is being centered, I am not quite understanding the logic behind how exactly the author of this code has centered the icon. The code for the icon and the circle looks like below:
<circle cx="100" cy="100" r="57" class="border" fill="#eee" stroke="none" stroke-width="15" stroke-dasharray="360" transform="rotate(-90,100,100)"></circle>
<circle class="circle" cx="100" cy="100" r="57" fill="none" stroke="#3498DB" stroke-width="5" stroke-dasharray="180, 20000" transform="rotate(-90,100,100)"></circle>
<text text-anchor="middle" x="100" y="110" class="icon" style="font-size: 40px" fill="#3498DB"></text>
Notice the x, y attributes on the <text> element like so:
x="100" y="110"
Now if you play around in the console, you'll notice that any value apart from that will make the icon no longer centered.
I am not quite understanding the logic for centering the icon here. Can anybody explain please?
P.S. I believe the y value is 110 to counter the size of the icon, something like negative margin when vertically aligning a absolutely positioned element.
FIDDLE HERE Can somebody please explain this to me?
The icon is centered due to two attribute-value pairs:
It is horizontally centered because of the text-anchor = 'middle' attribute. When text anchor is set to middle, the text is positioned such that the center of the text is placed at the x coord that was given as attribute for the text tag. Below is what MDN says about this property-value pair.
middle
The rendered characters are aligned such that the middle of the text string is at the current text position. (For text on a path, conceptually the text string is first laid out in a straight line. The midpoint between the start of the text string and the end of the text string is determined. Then, the text string is mapped onto the path with this midpoint placed at the current text position.)
Here the circle is created such that its center is at (100,100) and hence setting the text x='100' and setting text-anchor = 'middle' positions the text at the horizontal center of the circle.
It is vertically centered because of the y attribute's value. This involves trial and error based on the font-size and the font of the text. In this particular case, the font-size: 40px means that the height of the box is roughly 26 units. This means that setting y = '113' will actually be setting the text at vertical middle (to be precise). The value of y = '100' doesn't put it at exact center.
In the below snippet we can see how the font-size impacts the value of y attribute. Basically the value for y should be set in such a way that half the text will be above the center the rest will be below the center. So, y is roughly center point + (height of text / 2).
As explained by Lea Verou in this article, there is a property called dominant-baseline: middle; which will center text vertically without the need for such calculations but the browser support is poor.
.svg-container {
max-width:200px;
}
<div id="circli" class="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">
<g stroke="#ccc">
<line x1="133" y1="50" x2="140" y2="40" stroke-width="2"></line>
</g>
<g stroke="#ccc">
<line x1="140" y1="40" x2="200" y2="40" stroke-width="2"></line>
</g>
<circle cx="100" cy="100" r="57" class="border" fill="#eee" stroke="none" stroke-width="15" stroke-dasharray="360" transform="rotate(-90,100,100)"></circle>
<circle class="circle" cx="100" cy="100" r="57" fill="none" stroke="#3498DB" stroke-width="5" stroke-dasharray="180, 20000" transform="rotate(-90,100,100)"></circle>
<text text-anchor="middle" x="100" y="113" class="icon" style="font-size: 40px" fill="#3498DB"></text>
<text class="timer" text-anchor="middle" x="175" y="35" style="font-size: 22px; undefined;" fill="#aaa">50%</text>
</svg>
</div>
<div id="circlj" class="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 194 186" class="circliful">
<g stroke="#ccc">
<line x1="133" y1="50" x2="140" y2="40" stroke-width="2"></line>
</g>
<g stroke="#ccc">
<line x1="140" y1="40" x2="200" y2="40" stroke-width="2"></line>
</g>
<circle cx="100" cy="100" r="57" class="border" fill="#eee" stroke="none" stroke-width="15" stroke-dasharray="360" transform="rotate(-90,100,100)"></circle>
<circle class="circle" cx="100" cy="100" r="57" fill="none" stroke="#3498DB" stroke-width="5" stroke-dasharray="180, 20000" transform="rotate(-90,100,100)"></circle>
<text text-anchor="middle" x="100" y="109.5" class="icon" style="font-size: 30px;" fill="#3498DB"></text>
<text class="timer" text-anchor="middle" x="175" y="35" style="font-size: 22px; undefined;" fill="#aaa">50%</text>
</svg>
</div>
I have the following SVG file I am working on.
http://jsfiddle.net/slayerofgiants/9y3qc/8/
If you notice from the fiddle file, the horizontal scrollbar extends to about twice the width of the SVG drawing. There is nothing but white space beyond the edge of the SVG. If I remove the <g> and <text> elements, then horizontal scrollbar goes away and is the size of the width of the screen.
You can see this on this 2nd jsfiddle
http://jsfiddle.net/slayerofgiants/m5zPv/
The code below is the SVG with no g elements containing the paired text elements.
<svg version="1.1" width="90%" id="ex1-3rds-quarter-s" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" viewBox="0 0 323.333 55.333" enable-background="new 0 0 323.333 55.333"
xml:space="preserve" preserveAspectRatio="xMinYMid meet">
<font horiz-adv-x="1000">
<g id="fullTAB" transform="scale(1.1,1)" >
<g id="tablines" display="block">
<line fill="none" id="lowE" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round" x1="6" y1="45" x2="262" y2="45"/>
<line fill="none" id="Astring" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round" x1="6" y1="38.5" x2="262" y2="38.5"/>
<line fill="none" id="Dstring" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round" x1="6" y1="32" x2="262" y2="32"/>
<line fill="none" id="Gstring" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round" x1="6" y1="25.5" x2="262" y2="25.5"/>
<line fill="none" id="Bstring" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round" x1="6" y1="19" x2="262" y2="19"/>
<line fill="none" id="highE" stroke="#000000" stroke-width="0.1" stroke-linecap="round" stroke-linejoin="round" x1="6" y1="12.5" x2="262" y2="12.5"/>
</g>
<rect x="258" y="12" display="inline" width="0.829" height="32.73"/>
<rect x="260" y="12" display="inline" width="2.618" height="32.73"/>
<rect x="78" y="12" display="inline" width="0.829" height="32.73"/>
<rect x="6" y="12" display="inline" width="1" height="32.73"/>
<rect x="204" y="12" display="inline" width="0.829" height="32.73"/>
<rect x="142" y="12" display="inline" width="0.829" height="32.73"/>
<text x="13" y="21" style="text-anchor: middle" display="inline" font-family="'MyriadPro-Bold'" font-size="8.3685">T</text>
<text x="13" y="31" style="text-anchor: middle" display="inline" font-family="'MyriadPro-Bold'" font-size="8.3685">A</text>
<text x="13" y="41" style="text-anchor: middle" display="inline" font-family="'MyriadPro-Bold'" font-size="8.3685">B</text>
<g id="fretinformation">
</g><!--Fret Information -->
</g><!--Full Tab -->
</svg>
The code below is the g element with the paired text elements I am using which when inserted into the document causes the scrollbar to extend to about twice the width of the drawing size. I have a number of these with different x values. They are in the #fretinformation g element which is empty in the code above.
<g display="inline">
<text x="248" y="35" style="text-anchor: middle" fill="#324DCE" font-family="'MyriadPro-Bold'" font-size="8.3685">2</text>
<text x="248" y="41.5" style="text-anchor: middle" fill="#324DCE" font-family="'MyriadPro-Bold'" font-size="8.3685">3</text>
</g>
I had thought the g element had no width value and the text element width would collapse to only the width of the text it contained. What are the default widths/margin/padding of the g and text elements? And how can I change this behavior so there is no horizontal scrollbar?
Thanks--Christopher
EDIT
What appears to be happening is the text element is getting a width of 90% of the window size and as the text element is transformed along the x axis, it increases the overall width of the SVG file to be almost twice what it should be. As I remove text elements from right to left, the amount of whitespace to the right of the SVG file decreases.
SVG does not use a box layout model like HTML/CSS.
Putting display="inline" on an SVG element is basically meaningless. SVG elements are placed and drawn where you explicitly tell them.
The thing that determines the width and height of an SVG document is the width and height attributes (and sometimes the viewBox) of the root <svg> element.
However there are some differences (ie. bugs) in the way browsers lay out SVG elements. The fact that you are getting a scrollbar is an IE bug. You can use GCyrillus' overflow hidden trick to fix that.
It is render as an inline-boxe without any extra padding or margins.
I noticed a bug in chrome and another in IE11. wich one bothers you ?
It is somehow a bug that you can fix with : height:1%;(chrome) and overflow:hidden(IE) in your case : DEMO
svg {
display:block;
border:solid;
width:90%;
height:1%; /* fix webkit */
overflow:hidden;/* fix IE */
}
jsfiddle re-edited from IE11 : http://jsfiddle.net/9y3qc/13/ to check once again your comment