To vertically align a tspan element inside a text element in SVG, the CSS properties alignment-baseline and dominant-baseline work great in Chrome and in FF, respectively. So far so good.
With Internet Explorer it gets a bit crazy:
an open bug report asserts that these properties do not work for IE9-11 ...
... but the official documentation states that alignment-baseline is supported
CSS feature-sniffing in IE9 & IE11 reports that they support alignment-baseline as well as dominant-baseline for tspan, but they do not work with any values
to add confusion to frustration, this MSDN dev page simply says both properties are currently unsupported
This wouldn't be such an issue for IE9 (one could simply hack the desired alignment), but since I want to get away from browser detection, I would like to know:
is there a workable cross-browser solution?
how come even IE11 doesn't support this basic SVG styling property and how to work around that?
Thanks!
I have no idea why IE doesn't support alignment-baseline, let alone why you're getting such mixed information.
You can sort of hack the same behaviour using the dy attribute and font-based units ("em" and "ex"). It works pretty well if you just want to center a specific text element on a point.
<text x="50%" y="50%" dy="0.5ex" text-anchor="middle">
This text will be centered in your coordinate system!
</text>
But the problem with dy is that -- unless y is also set explicitly for the same element -- it is calculated relative to the position of the previous character. So if you want to center a text span relative to the surrounding spans, you have to first adjust for any previous offset and then set the new offset. The resulting code isn't pretty:
<text x="50%" y="25%" font-size="150%">
<tspan dy="0.5ex">Large font with</tspan><tspan
dy="-0.5ex"> <tspan
font-size="50%" dy="0.5ex">small font<tspan
dy="-0.5ex"> </tspan></tspan></tspan><tspan
dy="0.5ex">embedded.</tspan>
</text>
<text x="50%" y="75%" font-size="75%">
<tspan dy="0.5ex">Small font with</tspan><tspan
dy="-0.5ex"> <tspan
font-size="200%" dy="0.5ex">large font<tspan
dy="-0.5ex"> </tspan></tspan></tspan><tspan
dy="0.5ex">embedded.</tspan>
http://fiddle.jshell.net/awj49/
Rendering in IE11:
(gray lines mark the reference y coordinate)
If you can, it makes much cleaner code to just explicitly set the y attribute on each tspan:
<text x="50%" font-size="150%">
<tspan y="25%" dy="0.5ex">Large font with</tspan>
<tspan font-size="50%" y="25%" dy="0.5ex">small font</tspan>
<tspan y="25%" dy="0.5ex">embedded.</tspan>
</text>
<text x="50%" y="75%" font-size="75%">
<tspan y="75%" dy="0.5ex">Small font with</tspan>
<tspan font-size="200%" y="75%" dy="0.5ex">large font</tspan>
<tspan y="75%" dy="0.5ex">embedded.</tspan>
</text>
http://fiddle.jshell.net/awj49/1/
The final rendering is the same:
IE doesn't seem to support any vertical alignment CSS properties. A tspan is rendered with a default of what approximates alignment-baseline: central. Therefore, browser detection and polyfilling seems to be the only option just now to have your elements render the same across all browsers.
I adapted AmeliaBR's brilliant answer by manually adding to the y attribute of the parent text element to the desired difference of the font height in pixels (+50% of the font height = hanging, -50% = before-edge ...). Like so:
d3.selectAll('text').each( function()
var text = d3.select(this);
var dy = text.attr('y') || 0;
var h = text.style('font-size').replace('px', '') * 1;
var nh = (dy * 1) + (h / 2);
text.attr('y', nh);
});
I've used d3 here, but obviously this works with any JS library or pure JS DOM-selection.
Related
I have created an SVG with a base64 background image and two text areas (top and bottom text). These text areas can be updated via two input forms.
This functions correctly apart from on Internet Explorer and Edge, whereby the bottom text is aligned to the left instead of the center and the position of the bottom text is also incorrect. Top text is displaying correctly.
I am currently only trying to resolve this issue for Edge. If you view the link below I have a demo of the SVG and two fields.
https://jsfiddle.net/znhs955p/1/
For excepted behavior view on chrome or firefox.
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.58850098px;line-height:1.25;font-family:sans-serif;letter-spacing:2px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.43085253"
id="bottom"
transform="matrix(0.91651557,0,0,0.91221872,8.953221,18.767226)"
x="1.5895385"
y="3.9430504">
<textPath
xlink:href="#ellipse4592"
startOffset="50%"
id="bottom-text"
style="font-size:21px;letter-spacing:2px;stroke-width:1.43085253">
Bottom Text
</textPath>
</text>
<text
id="text4630"
style="font-style:normal;font-weight:normal;font-size:13.35012245px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.66707253"
xml:space="preserve"
transform="matrix(-0.90287605,0,0,-0.90624359,513.44619,329.63062)">
<textPath
xlink:href="#path5204"
startOffset="50%"
id="top-text"
style="font-size:18px;stroke-width:1.66707253">
Top Text
</textPath>
</text>
Remove the x and y attributes from the <text> element that wraps your <textPath>.
I.e. change:
<text ...
x="1.5895385"
y="3.9430504">
<textPath ... id="bottom-text" ...>Bottom Text</textPath>
</text>
to
<text ...>
<textPath ... id="bottom-text" ...>Bottom Text</textPath>
</text>
Those x and y attributes seem to be confusing IE.
https://jsfiddle.net/znhs955p/2/
Say I have a very basic SVG:
<svg viewBox="0 0 100 50">
<path transform="translate(X,Y)" fill="none" stroke="red" d="M11,11.1c-3.9,1.2-12.4,9-10.4,12.2s14.9-0.8,15.5-2.1s-4-2.7-4-2.7"></path>
<text transform="translate(10,25)">Hello World.</text>
</svg>
I know the height and width of the <path> (from some other algorithm) and the position of the <text> (from the transform attribute shown above).
How can I know the X and Y coordinates of the letter W?
My goal here is to set the translation of the path so that it appears just below the letter W.
To give you a better idea of the end goal, here's what I'm trying to do: I want to automatically replace all <text> in a svg by paths that I've drawn to match the font (Permanent Marker). This way I can create a realistic handwriting effect (see here for a live demo: whiteboard-comics.com/good_time_for_drinking=f(week_day))
So far, I start with a <text> and my images look like this:
Then I replace by hand, letter by letter, in Illustrator, the entire text. And it end up looking like this:
And when I do it algorithmically (just based on the coordinates of the <text> element and the width of each hand drawn character as a <path>), it becomes this:
I'm using a circular clip path for my nodes (in d3.js) as follows:
<g class="node" id="140" transform="translate(392.3261241288836,64.3699106556645)">
<image class="mainImage" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="images/manual-story-140.jpg" width="100" height="116.66705555685185" x="-50" y="-50" clip-path="url(#140)">
</image>
<clipPath class="clipPath" id="140">
<circle class="clipPathCircle" stroke-width="1" r="42"></circle>
</clipPath>
<circle class="outlinecircle" stroke="#0099FF" fill="url(#myLinearGradient1)" stroke-width="3" r="42"></circle>
</g>
But in Firefox the images don't load because the circle element within the clipPath element doesn't inherit the position of the node (i.e. from the g element).
In Chrome/Safari, everything works great and when I open up the console and hover over the circle element that's within the clipPath element it clearly shows the circle in the correct place (with dimensions of 84x84 since the radius is 42).
But in Firefox I see no images, and when I hover over the circle using the console I see it's positioned at the top left of the screen with dimensions 0x0.
Any ideas what I'm doing wrong here? Do I have to give an absolute position of the circle for firefox or should it already understand from the g-element it's in?
Apologies for the false alarm, but the problem here (as you can see in my original code) was that I was using the same id on my parent g element as I was to reference my clipPath! I changed the "id" attribute for my clip path to start with the string "clipPath-" and now it works on Firefox. Not sure why that would affect different browsers differently (which is why I kinda went 'round the houses trying to troubleshoot the bug), but thankfully enough it's quite a trivial fix!
I have some circles made in d3. I need to add more circles when these circles are clicked and make the existing circles disappear(they should not be removed as I will use them again).
The way I'm doing this right now is through a listener to a click event on the original circles. (these are created with .selectAll('circle.nodes')
.on("click",function(d){
//do stuff}).duration(1000);
populateSubCircles);
I want the sub circles to appear around a center (I looked at my calculations and they seem to be correct).
var populateSubCircles = function(){
var subCircles = nodesG.selectAll("circles.subNodes").data(....
This correctly adds the secondary circles, and cx and cy seem to be correct(not too far off from the center circle). However, they don't seem to appear on the page(they appear at the top left at 0,0). Why is that happening? How do I fix that?
Thanks.
EDIT-
A picture or two may help.
It's likely that you don't have the radius set on the circles (it must be set explicitly on the circle element, and cannot be set via CSS, as only style properties such as fill, stroke, etc., can be set) .
As you can see, one svg circle shows normally when set with a radius (r) of 25, and the other does not, and its size is correctly reported as 0px x 0px. (The tooltip reports size, not position). Here's the SVG I used:
<svg>
<circle cx="100" cy="100" fill="#ffdf00"
stroke="#222222" stroke-width="2px" />
<circle cx="50" cy="50" fill="#ffdf00" r="25"
stroke="#222222" stroke-width="2px" />
</svg>
This image shows the second of two circles, using inspector in Chrome web tools, it correctly reports the size:
Again, using inspector in Chrome, highlighted the first SVG element where no radius was set:
It shows the size as 0px x 0px and shows the circle as if it were in the upper left corner of the SVG document.
http://jsfiddle.net/wiredprairie/gNrZ3/
I am currently working with SVG element of JavaScript.
Here is my scenario.
I have an svg element in which I have two text element like this
<svg>
<g transform = "translate(0, 20)">
<text style="font-size: 0.5em;" x="0" y="-4"> text </text>
<text style="font-size: 0.5em;" x="70" y="-4"> value </text>
</g>
<svg>
It's appearing fine like this
text value
I have used font-size 0.5em because I want to my text to resize when I resize my svg.(by using jquery resizable)
Its working fine, but the problem is that when I resize my svg the space between text decreases and at some point the text becomes so big that both texts overlap with each other because of the fixed x and y attributes.
Is there a way to avoid this problem. A method by which I can keep the space between the two texts constant or the text can be relatively placed.
Converting to a single text value would seem to meet the use case you've outlined above. If you need text relatively placed you can convert one to a tspan and then use dx and dy e.g. <text>text <tspan dx="1em" dy="1em">value</tspan></text>