Get left and top of a text element inside an SVG? - css

I want to get the (top, left) coordinate of the text element relative to the svg element, expressed in the same unit/greatness as the y and x attributes of the text element.
I think that the example below is close, but there is a problem. getBoundingClientRect returns different values for top, left as the view port size changes, while of course the y, x attribute values of the text element are always the same. Thus the unit/greatness of top, left and y, x does not match.
I tried setting display: flex on the body to force the svg to not scale, but then it got the size 0 x 0.
const svgRect = document.querySelector("svg").getBoundingClientRect()
const textRect = document.querySelector("text").getBoundingClientRect()
const textBoxPosRelativeToSvgExpressedInSvgCoordinates = {top: textRect.y - svgRect.y, left: textRect.x - svgRect.x}
console.log(textBoxPosRelativeToSvgExpressedInSvgCoordinates)
<body>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 400">
<g id="main">
<line x1="10" y1="10" x2="100" y2="200" stroke="#00c" stroke-width="5" stroke-linecap="round"/>
<polyline points="580,10 560,390 540,200 520,390 400,390" stroke="#c00" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
<polygon points="350,75 379,161 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,161" stroke="#ff0" stroke-width="10" fill="#ffc"/>
<rect x="100" y="10" width="150" height="100" rx="10" ry="20" stroke="#060" stroke-width="8" fill="#0f0"/>
<circle cx="100" cy="300" r="80" stroke="#909" stroke-width="10" fill="#f6f"/>
<ellipse cx="450" cy="50" rx="80" ry="30" stroke="#0cc" stroke-width="10" fill="#0ff"/>
<text x="240" y="390" font-family="sans-serif" font-size="50" fill="#00f">SVG</text>
</g>
</svg>
</body>

Related

background color not appying on Text tag inside svg [duplicate]

I want to color the background of svg text similar to background-color in css
I was only able to find documentation on fill, which colors the text itself
Is it even possible?
You could use a filter to generate the background.
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" result="bg" />
<feMerge>
<feMergeNode in="bg"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">solid background</text>
</svg>
No this is not possible, SVG elements do not have background-... presentation attributes.
To simulate this effect you could draw a rectangle behind the text attribute with fill="green" or something similar (filters). Using JavaScript you could do the following:
var ctx = document.getElementById("the-svg"),
textElm = ctx.getElementById("the-text"),
SVGRect = textElm.getBBox();
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", SVGRect.x);
rect.setAttribute("y", SVGRect.y);
rect.setAttribute("width", SVGRect.width);
rect.setAttribute("height", SVGRect.height);
rect.setAttribute("fill", "yellow");
ctx.insertBefore(rect, textElm);
The solution I have used is:
<svg>
<line x1="100" y1="100" x2="500" y2="100" style="stroke:black; stroke-width: 2"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.6em">Hello World!</text>
<text x="150" y="105" style="fill:black">Hello World!</text>
</svg>
A duplicate text item is being placed, with stroke and stroke-width attributes. The stroke should match the background colour, and the stroke-width should be just big enough to create a "splodge" on which to write the actual text.
A bit of a hack and there are potential issues, but works for me!
Instead of using a <text> tag, the <foreignObject> tag can be used, which allows for XHTML content with CSS.
No, you can not add background color to SVG elements. You can do it programmatically with d3.
var text = d3.select("text");
var bbox = text.node().getBBox();
var padding = 2;
var rect = self.svg.insert("rect", "text")
.attr("x", bbox.x - padding)
.attr("y", bbox.y - padding)
.attr("width", bbox.width + (padding*2))
.attr("height", bbox.height + (padding*2))
.style("fill", "red");
Answer by Robert Longson (#RobertLongson) with modifications:
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow"/>
<feComposite in="SourceGraphic" operator="xor"/>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50"> solid background </text>
<text x="20" y="50" font-size="50">solid background</text>
</svg>
and we have no bluring and no heavy "getBBox" :)
Padding is provided by white spaces in text-element with filter.
It's worked for me
Going further with #dbarton_uk answer, to avoid duplicating text you can use paint-order=stroke style:
<svg>
<line x1="100" y1="100" x2="350" y2="100" style="stroke:grey; stroke-width: 100"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.5em; fill:black; paint-order:stroke; stroke-linejoin:round">Hello World!</text>
</svg>
Note the stroke-linejoin:round which is needed to avoid seeing spikes for the W sharp angle.
You can combine filter with the text.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter x="0" y="0" width="1" height="1" id="bg-text">
<feFlood flood-color="white"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<!-- something has already existed -->
<rect fill="red" x="150" y="20" width="100" height="50" />
<circle cx="50" cy="50" r="50" fill="blue"/>
<!-- Text render here -->
<text filter="url(#bg-text)" fill="black" x="20" y="50" font-size="30">text with color</text>
<text fill="black" x="20" y="50" font-size="30">text with color</text>
</svg>
</body>
</html>
this is my favorite hack (not sure it should work). It refer an element that is not yet displayed, and it works pretty well
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 620 40" preserveAspectRatio="xMidYMid meet">
<defs>
<filter x="-0.02" y="0" width="1.04" height="1.1" id="removebackground">
<feFlood flood-color="#00ffff"/>
</filter>
</defs>
<!--Draw the text-->
<use xlink:href="#mygroup" filter="url(#removebackground)" />
<g id="mygroup">
<text id="text1" x="9" y="20" style="text-anchor:start;font-size:14px;">custom text with background</text>
<line x1="200" y1="18" x2="200" y2="36" stroke="#000" stroke-width="5"/>
<line x1="120" y1="27" x2="203" y2="27" stroke="#000" stroke-width="5"/>
</g>
</svg>
For those wondering how to apply padding to a text element when it has a background like in the Robert's answer, do the following:
<svg>
<defs>
<filter x="-0.1" y="-0.1" width="1.2" height="1.2" id="solid">
<feFlood flood-color="#171717"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">Hello</text>
</svg>
In the example above, filter's x and y positions can be used as transform: translate(-10%, -10%) would, and width and height values can be read as 120% and 120%. So we made background 20% bigger, and offsetted it -10%, so background is now 10% bigger on each side of the text.
The previous answers relied on doubling up text and lacked sufficient whitespace.
By using atop and I was able to get the results I wanted.
This example also includes arrows, a common use case for SVG text labels:
<svg viewBox="-105 -40 210 234">
<title>Size Guide</title>
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="white"></feFlood>
<feComposite in="SourceGraphic" operator="atop"></feComposite>
</filter>
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z"></path>
</marker>
</defs>
<g id="garment">
<path id="right-body" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 0 l30 0 l0 154 l-30 0"></path>
<path id="right-sleeve" d="M30 0 l35 0 l0 120 l-35 0" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></path>
<use id="left-body" href="#right-body" transform="scale(-1,1)"></use>
<use id="left-sleeve" href="#right-sleeve" transform="scale(-1,1)"></use>
<path id="collar-right-top" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 -6.5 l11.75 0 l6.5 6.5"></path>
<use id="collar-left-top" href="#collar-right-top" transform="scale(-1,1)"></use>
<path id="collar-left" fill="white" stroke="black" stroke-width="1" stroke-linejoin="round" d="M-11.75 -6.5 l-6.5 6.5 l30 77 l6.5 -6.5 Z"></path>
<path id="front-right" fill="white" stroke="black" stroke-width="1" d="M18.25 0 L30 0 l0 154 l-41.75 0 l0 -77 Z"></path>
<line x1="0" y1="0" x2="0" y2="154" stroke="black" stroke-width="1" stroke-dasharray="1 3"></line>
<use id="collar-right" href="#collar-left" transform="scale(-1,1)"></use>
</g>
<g id="dimension-labels">
<g id="dimension-sleeve-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="85" y1="0" x2="85" y2="120" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="85" y="60" class="dimension" text-anchor="middle" dominant-baseline="middle"> 120 cm</text>
</g>
<g id="dimension-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-85" y1="0" x2="-85" y2="154" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="-85" y="77" text-anchor="middle" dominant-baseline="middle" class="dimension"> 154 cm</text>
</g>
<g id="dimension-sleeve-to-sleeve">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-65" y1="-20" x2="65" y2="-20" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="-20" text-anchor="middle" dominant-baseline="middle" class="dimension"> 130 cm </text>
</g>
<g title="Back Width" id="dimension-back-width">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-30" y1="174" x2="30" y2="174" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="174" text-anchor="middle" dominant-baseline="middle" class="dimension"> 60 cm </text>
</g>
</g>
</svg>
An obvious workaround to the problem of the blur produced by the filter effect is to render the <text> two times: once for the background (with transparent characters) and once for the characters (without a background filter).
For me, this was the only way to make the text readable in Safari.
<svg width="100%" height="100%">
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" />
</filter>
<g transform="translate(20, 50)" font-size="50">
<text aria-hidden="true" fill="none" filter="url(#solid)">solid background</text>
<text fill="blue">solid background</text>
</g>
</svg>
The aria-hidden="true" attribute is there to prevent screen readers from speaking the text twice, if the user uses a screen reader.
You can add style to your text:
style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
text-shadow: rgb(255, 255, 255) -2px -2px 0px, rgb(255, 255, 255) -2px 2px 0px,
rgb(255, 255, 255) 2px -2px 0px, rgb(255, 255, 255) 2px 2px 0px;"
White, in this example.
Does not work in IE :)

clip-path url fails to find id

I have an SVG that I am trying to use to clip a div, but the id I give to the <clipPath> tag does not work.
I have tried changing the ID, and have made sure that the SVG does indeed exist in the same file, and the ID is visible.
The svg is like so:
<svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 149.559">
<defs>
<clipPath id="clipper">
<g id="svgg" stroke="none" fill-rule="evenodd">
<path id="path0" d= .../>
<path id="path1" d= .../>
<path id="path2" d= .../>
<path id="path3" d= .../>
<path id="path4" d= .../>
</g>
</clipPath>
</defs>
</svg>
I added the <defs> and <clipPath> tag so I could use the svg I had as a clipping mask.
The html element being used is:
<div class="logo-bg" style="clipPath: url(#clipper)"></div>
the div does have a width and height.
Within developer tools, the css property of the div I am trying to clip with clip-path: url(#clip-id) shows "could not load the image". Ideally I would be able to clip the div with the SVG.
here's the code I am working with: https://jsfiddle.net/mzLtsqva/6/
I am new to working with SVGs and so would appreciate any help to solve this issue.
Inside the <clipPath> don't wrap the paths in a group element.
In the next example I'm using a clipping path that is not working: #no and one that is working: #yes. In the one that is not working I'm wrapping the elements inside in a <g> element.
svg{border:1px solid;}
<svg width="250" height="250" viewBox="0 0 250 250" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<rect id="rect" x ="0" y ="0" height ="150" width ="70" style ="stroke:#000;" transform="translate(90, 50)"/>
<clipPath id="no">
<g>
<use xlink:href="#rect" fill="none"></use>
<use xlink:href="#rect" fill="none" transform="rotate(60 125 125)"></use>
<use xlink:href="#rect" fill="none" transform="rotate(-60 125 125)"></use>
</g>
</clipPath>
</defs>
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/beagle400.jpg" height="250" width="250" x="-15" y ="50" clip-path="url(#no)"></image>
</svg>
<svg width="250" height="250" viewBox="0 0 250 250" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<clipPath id="yes">
<use xlink:href="#rect" fill="none"></use>
<use xlink:href="#rect" fill="none" transform="rotate(60 125 125)"></use>
<use xlink:href="#rect" fill="none" transform="rotate(-60 125 125)"></use>
</clipPath>
</defs>
<image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/beagle400.jpg" height="250" width="250" x="-15" y ="50" clip-path="url(#yes)"></image>
</svg>

How to hide overflow when sliding an element of an SVG in using keyframe animation?

I need to slide up the entire hand and phone element (hand_phone class) onto it's current position on the red background. The trouble I have is only showing the hand when it's on the red background, not sliding it up on the blue or white. As if the hand and phone is behind everything else and the red box is a window. I've tried clipPath, hidden: overflow, but not having any luck.
https://jsfiddle.net/8moofbef/3/
.hand_phone {
animation: slide 1s;
}
#keyframes slide {
from {transform: translateY(400px)}
to {transform: translateY(0px)}
}
Define the <clipPath> that you want the hand to appear inside of. In this case it's just a <rect> the same size as the red one.
<defs>
<clipPath id="red-rect-clip">
<rect x="167.5" y="981.4" class="st2" width="391.7" height="451"/>
</clipPath>
</defs>
Secondly, you need apply the clip path to the correct elements. It can't be <g class="hand_phone"> because that is being animated with a transform. And any transform you apply to that will also move the clip path that is attached to it.
So what you need to do is surround the "hand_phone" elements with a parent group. Then apply the clip path to that. That way the clip doesn't move, and you get the effect you want.
<g clip-path="url(#red-rect-clip)">
<g class="hand_phone">
<...>
</g>
</g>
.hand_phone {
animation: slide 1s;
}
#keyframes slide {
from {transform: translateY(400px)}
to {transform: translateY(0px)}
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1920 1920" style="enable-background:new 0 0 1920 1920;" xml:space="preserve">
<defs>
<clipPath id="red-rect-clip">
<rect x="167.5" y="981.4" class="st2" width="391.7" height="451"/>
</clipPath>
</defs>
<style type="text/css">
.st0{fill:#057BDB;}
.st1{fill:#FFFFFF;}
.st2{fill:#FF0049;}
.st3{clip-path:url(#SVGID_2_);}
.st4{fill:#01434C;}
.st5{fill:#03828E;}
.st6{fill:#085E67;}
.st7{fill:#6CB5AE;}
.st8{fill:#25A599;}
.st9{fill:#B5B5B5;}
.st10{fill:#DDDDDD;}
.st11{fill:#F27040;}
.st12{fill:#F48359;}
.st13{fill:#F05A2B;}
.st14{fill:#253D46;}
.st15{fill:#969BA4;}
.st16{fill:#465B66;}
.st17{fill:#AFBDC5;}
.st18{fill:#FF8B00;}
.st19{fill:#566D78;}
.st20{fill:#5D6972;}
.st21{fill:#78909B;}
.st22{fill:#F8A773;}
.st23{fill:#FAB685;}
.st24{fill:#F79B6A;}
.st25{fill:#140F2C;}
.st26{fill:#E5E5E5;}
.st27{fill:#F4F4F4;}
.st28{fill:#150E39;}
.st29{fill:#1C1749;}
.st30{fill:#171043;}
.st31{fill:#5E6A7E;}
.st32{fill:#0A038E;}
.st33{fill:#30120B;}
.st34{fill:#CBE6F3;}
.st35{fill:#838E93;}
.st36{fill:#BCB1D7;}
.st37{fill:#9F98C6;}
.st38{fill:#D8CEE6;}
.st39{fill:#FF88A7;}
.st40{fill:#EAEBF0;}
.st41{fill:#F0F1F8;}
.st42{fill:#D5DAEE;}
.st43{fill:#378E43;}
.st44{fill:#BD3A27;}
.st45{fill:#795549;}
.st46{fill:#F69D5E;}
.st47{fill:#221F1F;}
.st48{fill:#403F41;}
.st49{fill:#E6E6E7;}
.st50{fill-rule:evenodd;clip-rule:evenodd;fill:#D0A785;}
.st51{fill-rule:evenodd;clip-rule:evenodd;fill:#FFD6B6;}
.st52{fill-rule:evenodd;clip-rule:evenodd;fill:#AAAAAA;}
.st53{fill-rule:evenodd;clip-rule:evenodd;fill:#CCE9F9;}
.st54{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
.st55{fill-rule:evenodd;clip-rule:evenodd;fill:#CBCBCB;}
.st56{fill-rule:evenodd;clip-rule:evenodd;fill:#989898;}
.st57{fill-rule:evenodd;clip-rule:evenodd;fill:#93C6E2;}
.st58{fill:#CBCBCB;}
.st59{fill:#E6E6E6;}
.st60{fill:#96BEE5;}
.st61{opacity:0.5;fill:#F2F2F2;}
.st62{fill:#F2F2F2;}
.st63{fill:#CBCDCE;}
.st64{fill:#0D3EB7;}
.st65{fill:#4D4D4D;}
.st66{opacity:0.1;}
.st67{fill:#808080;}
.st68{fill:#D7D8D9;}
.st69{fill:#F1F2F2;}
.st70{fill:#FFFEF7;}
.st71{fill:#BDCAE8;}
.st72{opacity:0.5;fill:#FFFFFF;}
.st73{fill:#D7D7D7;}
.st74{fill:#D7869D;}
.st75{opacity:0.2;fill-rule:evenodd;clip-rule:evenodd;fill:#1A1A1A;}
.st76{opacity:0.65;}
.st77{fill:none;stroke:#ED1C24;stroke-width:2;stroke-miterlimit:10;}
.st78{fill:none;stroke:#ED1C24;stroke-width:2;stroke-miterlimit:10;stroke-dasharray:1.9968,1.9968;}
.st79{opacity:0.95;fill:none;stroke:#ED1C24;stroke-width:2;stroke-miterlimit:10;stroke-dasharray:2;}
.st80{fill:#567396;}
.st81{fill:#FFFFFF;stroke:#057BDB;stroke-width:0.75;stroke-miterlimit:10;}
</style>
<g>
<rect y="-1" class="st0" width="1920" height="1920"/>
<g class="white_rectangle">
<rect x="140.6" y="666.1" class="st1" width="849.2" height="793.9"/>
</g>
<g class="red_rectangle">
<rect x="167.5" y="981.4" class="st2" width="391.7" height="451"/>
</g>
<g clip-path="url(#red-rect-clip)">
<g class="hand_phone">
<path class="st46" d="M435.6,1066.8l-16,21c-4.1,5.3-23.5,14.4- 15.2,22.4c10.1,9.7,22.5,10.1,26.5,5.8l28-31
c4.2-5.1,10.1-14.8,1.3-23.2C450.1,1052,439.5,1062.5,435.6,1066.8z"/>
<path class="st46" d="M280.3,1138.7c-0.6,33.7-3.4,107.9-3.4,107.9s-1.2,15.3,4.8,22.2c6.1,6.9,15.1,9.9,15.1,9.9l-15.1,31.5
l80.3,42.6l56.4-87.9l-118.3-110.6C300.2,1154.2,303.5,1137,280.3,1138.7z"/>
<path class="st47" d="M418.3,1044.1H312.8c-7.1,0-12.9,5.8-12.9,12.9v201.7c0,7.2,5.8,12.9,12.9,12.9h105.5
c7.2,0,12.9-5.8,12.9-12.9v-201.7C431.3,1049.9,425.5,1044.1,418.3,1044.1z"/>
<rect x="308" y="1072.8" class="st1" width="115.2" height="167.1"/>
<circle class="st48" cx="365.6" cy="1060" r="4.8"/>
<path class="st48" d="M374,1250.7h-16.8c-2.9,0-5.3,2.4-5.3,5.3c0,2.9,2.4,5.3,5.3,5.3H374c2.9,0,5.3-2.4,5.3-5.3
C379.3,1253,376.9,1250.7,374,1250.7z"/>
<path class="st46" d="M424.7,1208l-5.1,6.5c-4.1,5.3-9,14.4-0.7,22.4c10.1,9.7,20.7-0.8,24.6-5.1l4.4-5.6
c4.2-5.1,10.1-14.8,1.3-23.2C439.2,1193.3,428.6,1203.8,424.7,1208z"/>
<path class="st46" d="M424.7,1166.5l-5.1,6.5c-4.1,5.3-9,14.4-0.7,22.4c10.1,9.7,20.7-0.8,24.6-5.1l4.4-5.6
c4.2-5.1,10.1-14.8,1.3-23.2C439.2,1151.8,428.6,1162.2,424.7,1166.5z"/>
<path class="st46" d="M424.7,1124.6l-5.1,6.4c-4.1,5.3-9,14.4-0.7,22.4c10.1,9.7,20.7-0.8,24.6-5l4.4-5.6
c4.2-5.1,10.1-14.8,1.3-23.2C439.2,1109.9,428.6,1120.4,424.7,1124.6z"/>
<polygon class="st49" points="270.7,1297.2 200,1433.1 335.4,1433.1 376.7,1353.7 "/>
<polygon class="st1" points="270.7,1297.2 200,1433.1 267.7,1433.1 323.7,1325.5 "/>
</g>
</g>
</g>
</svg>
https://jsfiddle.net/8moofbef/4/

SVG circle take all the height preserving aspectratio on any div size

The idea is to draw a circle in SVG that takes the whole height on the parent div whatever the size of the parent div. The width should be somehow ignored.
I've been working a bit with aspect ratio of SVG but this does not really work on all scenarios :
<div style='width:400px;height:100px'>
<svg width="100%" height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin slice">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">Example SVG text 1</text>
</svg>
</div>
With the div width defined with 200px it's working.
Fiddle: https://jsfiddle.net/91sp2j0x/11/
Specifying a value of 50 for the r attribute will allow the nested svg element to maintain a 100% height of its containing (parent) element.
Code Snippet Demonstration:
.resize-demonstration {
resize: auto;
border: 1px solid gray;
box-sizing: border-box;
overflow:hidden;
}
.container-model {
border-right: 1px dashed gray;
}
<p>Resize the element below <u>vertically</u> or <u>horzontally</u> to demonstrate the intended behaviour</p>
<p><em>Note:</em> the <code>svg</code> has been wrapped in an containing element for <em>user-friendly resizing</em> (interaction with the resizing icon in the bottom-right corner), this is <strong>only for the sake of demonstration</strong> and should not be considered required.</p>
<div class="resize-demonstration" style="height: 100px">
<div style='width:50px;height:100%;overflow:hidden;' class="container-model">
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet">
<circle cx="50" cy="50" r="50" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">100px width : Nothing is visible</text>
</svg>
</div>
</div>
Updated JSFiddle
Is this what you would like to happen? I set the height of the div for each of the divs to different heights, and set the divs to display: inline-block so that the width is not 100%, as divs usually are by default, since they are block elements. That way the height of the SVG will be the height of the div.
<div style='height:200px;display:inine-block;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">200px h</text>
</svg>
</div>
<div style='height:500px;display:inine-block;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">500px h</text>
</svg>
</div>
<div style='height:300px;display:inine-block;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="xMinYMin meet" >
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">300px h</text>
</svg>
</div>
https://jsfiddle.net/suefeng/v14e7b81/2/
with preserveAspectRatio="none" and svg{width:100%} will give 100% adjusted height.
svg{
width: 100%;
}
<div style='width:200px;height:100px;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="none">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">200px w: cut text</text>
</svg>
</div>
<div style='width:500px;height:100px;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="none">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">500px w: full visible text</text>
</svg>
</div>
<div style='width:50px;height:100px;overflow:hidden'>
<svg height="100%" viewbox="0 0 200 100" preserveAspectRatio="none" >
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">100px width : Nothing is visible</text>
</svg>
</div>
If i understood right, u want to have the circle with the radius of the height...
I did that successfully like so (if i understood what u asked correctly):
<div style='width:800px;height:120px;'>
<svg viewbox="0 -10 200 140" preserveAspectRatio="xMinYMin slice"> /* just remove the height and the width from the svg, it will take the parameters from the div... */
<circle cx="18%" cy="18%" r="18%" stroke="black" stroke-width="0" fill="red" />
<text x="100" y="50">Example SVG text 1</text>
</svg>
</div>

Background color of text in SVG

I want to color the background of svg text similar to background-color in css
I was only able to find documentation on fill, which colors the text itself
Is it even possible?
You could use a filter to generate the background.
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" result="bg" />
<feMerge>
<feMergeNode in="bg"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">solid background</text>
</svg>
No this is not possible, SVG elements do not have background-... presentation attributes.
To simulate this effect you could draw a rectangle behind the text attribute with fill="green" or something similar (filters). Using JavaScript you could do the following:
var ctx = document.getElementById("the-svg"),
textElm = ctx.getElementById("the-text"),
SVGRect = textElm.getBBox();
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", SVGRect.x);
rect.setAttribute("y", SVGRect.y);
rect.setAttribute("width", SVGRect.width);
rect.setAttribute("height", SVGRect.height);
rect.setAttribute("fill", "yellow");
ctx.insertBefore(rect, textElm);
The solution I have used is:
<svg>
<line x1="100" y1="100" x2="500" y2="100" style="stroke:black; stroke-width: 2"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.6em">Hello World!</text>
<text x="150" y="105" style="fill:black">Hello World!</text>
</svg>
A duplicate text item is being placed, with stroke and stroke-width attributes. The stroke should match the background colour, and the stroke-width should be just big enough to create a "splodge" on which to write the actual text.
A bit of a hack and there are potential issues, but works for me!
Instead of using a <text> tag, the <foreignObject> tag can be used, which allows for XHTML content with CSS.
No, you can not add background color to SVG elements. You can do it programmatically with d3.
var text = d3.select("text");
var bbox = text.node().getBBox();
var padding = 2;
var rect = self.svg.insert("rect", "text")
.attr("x", bbox.x - padding)
.attr("y", bbox.y - padding)
.attr("width", bbox.width + (padding*2))
.attr("height", bbox.height + (padding*2))
.style("fill", "red");
Answer by Robert Longson (#RobertLongson) with modifications:
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow"/>
<feComposite in="SourceGraphic" operator="xor"/>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50"> solid background </text>
<text x="20" y="50" font-size="50">solid background</text>
</svg>
and we have no bluring and no heavy "getBBox" :)
Padding is provided by white spaces in text-element with filter.
It's worked for me
Going further with #dbarton_uk answer, to avoid duplicating text you can use paint-order=stroke style:
<svg>
<line x1="100" y1="100" x2="350" y2="100" style="stroke:grey; stroke-width: 100"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.5em; fill:black; paint-order:stroke; stroke-linejoin:round">Hello World!</text>
</svg>
Note the stroke-linejoin:round which is needed to avoid seeing spikes for the W sharp angle.
You can combine filter with the text.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter x="0" y="0" width="1" height="1" id="bg-text">
<feFlood flood-color="white"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<!-- something has already existed -->
<rect fill="red" x="150" y="20" width="100" height="50" />
<circle cx="50" cy="50" r="50" fill="blue"/>
<!-- Text render here -->
<text filter="url(#bg-text)" fill="black" x="20" y="50" font-size="30">text with color</text>
<text fill="black" x="20" y="50" font-size="30">text with color</text>
</svg>
</body>
</html>
this is my favorite hack (not sure it should work). It refer an element that is not yet displayed, and it works pretty well
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 620 40" preserveAspectRatio="xMidYMid meet">
<defs>
<filter x="-0.02" y="0" width="1.04" height="1.1" id="removebackground">
<feFlood flood-color="#00ffff"/>
</filter>
</defs>
<!--Draw the text-->
<use xlink:href="#mygroup" filter="url(#removebackground)" />
<g id="mygroup">
<text id="text1" x="9" y="20" style="text-anchor:start;font-size:14px;">custom text with background</text>
<line x1="200" y1="18" x2="200" y2="36" stroke="#000" stroke-width="5"/>
<line x1="120" y1="27" x2="203" y2="27" stroke="#000" stroke-width="5"/>
</g>
</svg>
For those wondering how to apply padding to a text element when it has a background like in the Robert's answer, do the following:
<svg>
<defs>
<filter x="-0.1" y="-0.1" width="1.2" height="1.2" id="solid">
<feFlood flood-color="#171717"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">Hello</text>
</svg>
In the example above, filter's x and y positions can be used as transform: translate(-10%, -10%) would, and width and height values can be read as 120% and 120%. So we made background 20% bigger, and offsetted it -10%, so background is now 10% bigger on each side of the text.
The previous answers relied on doubling up text and lacked sufficient whitespace.
By using atop and I was able to get the results I wanted.
This example also includes arrows, a common use case for SVG text labels:
<svg viewBox="-105 -40 210 234">
<title>Size Guide</title>
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="white"></feFlood>
<feComposite in="SourceGraphic" operator="atop"></feComposite>
</filter>
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z"></path>
</marker>
</defs>
<g id="garment">
<path id="right-body" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 0 l30 0 l0 154 l-30 0"></path>
<path id="right-sleeve" d="M30 0 l35 0 l0 120 l-35 0" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></path>
<use id="left-body" href="#right-body" transform="scale(-1,1)"></use>
<use id="left-sleeve" href="#right-sleeve" transform="scale(-1,1)"></use>
<path id="collar-right-top" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 -6.5 l11.75 0 l6.5 6.5"></path>
<use id="collar-left-top" href="#collar-right-top" transform="scale(-1,1)"></use>
<path id="collar-left" fill="white" stroke="black" stroke-width="1" stroke-linejoin="round" d="M-11.75 -6.5 l-6.5 6.5 l30 77 l6.5 -6.5 Z"></path>
<path id="front-right" fill="white" stroke="black" stroke-width="1" d="M18.25 0 L30 0 l0 154 l-41.75 0 l0 -77 Z"></path>
<line x1="0" y1="0" x2="0" y2="154" stroke="black" stroke-width="1" stroke-dasharray="1 3"></line>
<use id="collar-right" href="#collar-left" transform="scale(-1,1)"></use>
</g>
<g id="dimension-labels">
<g id="dimension-sleeve-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="85" y1="0" x2="85" y2="120" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="85" y="60" class="dimension" text-anchor="middle" dominant-baseline="middle"> 120 cm</text>
</g>
<g id="dimension-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-85" y1="0" x2="-85" y2="154" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="-85" y="77" text-anchor="middle" dominant-baseline="middle" class="dimension"> 154 cm</text>
</g>
<g id="dimension-sleeve-to-sleeve">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-65" y1="-20" x2="65" y2="-20" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="-20" text-anchor="middle" dominant-baseline="middle" class="dimension"> 130 cm </text>
</g>
<g title="Back Width" id="dimension-back-width">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-30" y1="174" x2="30" y2="174" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="174" text-anchor="middle" dominant-baseline="middle" class="dimension"> 60 cm </text>
</g>
</g>
</svg>
An obvious workaround to the problem of the blur produced by the filter effect is to render the <text> two times: once for the background (with transparent characters) and once for the characters (without a background filter).
For me, this was the only way to make the text readable in Safari.
<svg width="100%" height="100%">
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" />
</filter>
<g transform="translate(20, 50)" font-size="50">
<text aria-hidden="true" fill="none" filter="url(#solid)">solid background</text>
<text fill="blue">solid background</text>
</g>
</svg>
The aria-hidden="true" attribute is there to prevent screen readers from speaking the text twice, if the user uses a screen reader.
You can add style to your text:
style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
text-shadow: rgb(255, 255, 255) -2px -2px 0px, rgb(255, 255, 255) -2px 2px 0px,
rgb(255, 255, 255) 2px -2px 0px, rgb(255, 255, 255) 2px 2px 0px;"
White, in this example.
Does not work in IE :)

Resources