How to pack text inside svg rect - javascript

I wish to fit svg text inside a rect. I could use an approach to compare the widths and add line breaks to the text, but it's not tedious.
Is there a more elegant way than this? Maybe by using CSS or d3?
UPDATE:the following code appends foreignObject using d3 but the div is not displayed. (it is there in the code inspecter)
var group = d3.select("#package");
var fo = group.append("foreignObject").attr("x", 15).attr("y", 15).attr("width", 190).attr("height", 90);
fo.append("div").attr("xmlns", "http://www.w3.org/1999/xhtml").attr("style", "width:190px; height:90px; overflow-y:auto").text("Thiggfis the dgdexsgsggs wish to fit insidegssgsgs");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<p id="p"></p>
<svg width="220" height="120" viewBox="0 0 220 120" id="package">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black"/>
</svg>

A namespace cannot be assigned by attr, it's a side effect of element creation. You need an html div so you need to tell d3 that by calling the element xhtml:div, once you do that, d3 will do the rest.
var group = d3.select("#package");
var fo = group.append("foreignObject").attr("x", 15).attr("y", 15).attr("width", 190).attr("height", 90);
fo.append("xhtml:div").attr("style", "width:190px; height:90px; overflow-y:auto").text("Thiggfis the dgdexsgsggs wish to fit insidegssgsgs");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<p id="p"></p>
<svg width="220" height="120" viewBox="0 0 220 120" id="package">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black"/>
</svg>

Here's a simple example of a foreignObject used to insert HTML markup into an SVG:
<svg width="220" height="120" viewBox="0 0 220 120">
<rect x="10" y="10" width="200" height="100" fill="none" stroke="black" />
<foreignObject x="15" y="15" width="190" height="90">
<div xmlns="http://www.w3.org/1999/xhtml" style="width:190px; height:90px; overflow-y:auto"><b>This</b> is the <i>text</i> I wish to fit inside <code>rect</code></div>
</foreignObject>
</svg>

Related

External SVG file in clippath

I have SVG file that i created it in photoshop. I would like to use it in my html page with clip-path property. I am trying to implement it as using clip-path:url(#mysvg); and paste the svg code to my html page. But i does not work. How can i do that?
My purpose is like this with css:
Here is the .svg file:
https://svgshare.com/i/dfw.svg
Here is the svg code
<!--IMAGE-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 814 506" >
<image id="image" class="image__svg-image" width="100%" height="100%" clip-path="url(#mask)" x="-100px" xlink:href="https://res.cloudinary.com/alvarosaburido/image/upload/v1589435086/blog/The%20Magic%20of%20SVG%20Clip-path/pic_yo5eyq.png" />
</svg>
<!--MY SVG FILE-->
<svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1920" viewBox="0 0 1920 1920">
<metadata><?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39 ">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""/>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?></metadata>
<defs>
<style>
.cls-1 {
fill: #fff;
fill-rule: evenodd;
}
</style>
</defs>
<path id="rect1" class="cls-1" d="M133,333.637L1426.05,171.265a157.557,157.557,0,0,1,175.99,136.647l157.93,1256.5L466.929,1726.79a157.574,157.574,0,0,1-176-136.65Z"/>
</svg>
In this example the viewBox of the <svg> is 100 in width and the image also takes up 100% of the width. So, no matter the actual width of the image it will always fill the entire SVG.
The <clipPath> fits in the size of the viewBox of the <svg> that holds the image. I know the width is 100, so I made the clippath 70 in height and width plus the extra height that the rotation takes up. This matches kind of the height of the images (unknown at this point).
I replaced the content of the <clipPath>. It is more "transparent" what the clip path does and easier to manipulate.
<!--IMAGE-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="300">
<image width="100%" href="https://res.cloudinary.com/alvarosaburido/image/upload/v1589435086/blog/The%20Magic%20of%20SVG%20Clip-path/pic_yo5eyq.png" clip-path="url(#mask)" />
</svg>
<!--MY SVG FILE-->
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<defs>
<clipPath id="mask" transform="translate(15 0) rotate(-10 60 0)">
<rect width="50" height="50" />
<rect x="20" y="20" width="50" height="50" />
<rect x="20" width="50" height="50" rx="10" />
<rect y="20" width="50" height="50" rx="10" />
</clipPath>
</defs>
</svg>
Update
OP asks if the original path can be used as a clip-path. It can, but the viewBox needs to be modified accordingly. So, if the viewbox 0 0 2300 1800 is used the path fits the image.
<!--IMAGE-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2300 1800" width="300">
<image width="100%" href="https://res.cloudinary.com/alvarosaburido/image/upload/v1589435086/blog/The%20Magic%20of%20SVG%20Clip-path/pic_yo5eyq.png" clip-path="url(#mask)" />
</svg>
<!--MY SVG FILE-->
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<defs>
<clipPath id="mask" transform="translate(350 0)">
<path id="rect1" class="cls-1" d="M133,333.637L1426.05,171.265a157.557,157.557,0,0,1,175.99,136.647l157.93,1256.5L466.929,1726.79a157.574,157.574,0,0,1-176-136.65Z"/>
</clipPath>
</defs>
</svg>
Update
To "path" or not to "path", that is the question. This third example is a better solution. The path is simpler and there are not that many child elements in <clipPath>.
<!--IMAGE-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11" width="300">
<image width="100%" href="https://res.cloudinary.com/alvarosaburido/image/upload/v1589435086/blog/The%20Magic%20of%20SVG%20Clip-path/pic_yo5eyq.png" clip-path="url(#mask)" />
</svg>
<!--MY SVG FILE-->
<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
<defs>
<clipPath id="mask" transform="translate(2 0) rotate(-10 7 0)">
<path d="M 0 0 L 6 0 A 1 1 90 0 1 7 1 L 7 7 L 1 7 A 1 1 90 0 1 0 6 Z"/>
</clipPath>
</defs>
</svg>
Update
This fourth example is using the original path, BUT defined in a <clipPath> and used as an external reference in CSS. The external SVG file have the following content:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="mask" transform="translate(350 0)">
<path id="rect1" class="cls-1" d="M133,333.637L1426.05,171.265a157.557,157.557,0,0,1,175.99,136.647l157.93,1256.5L466.929,1726.79a157.574,157.574,0,0,1-176-136.65Z"/>
</clipPath>
</defs>
</svg>
But for this example I replace the URL (like https://svgshare.com/i/dfw.svg#rect) to the SVG file with a data URI.
svg>image {
clip-path: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxkZWZzPgogICAgPGNsaXBQYXRoIGlkPSJtYXNrIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNTAgMCkiPgogICAgICA8cGF0aCBpZD0icmVjdDEiIGNsYXNzPSJjbHMtMSIgZD0iTTEzMywzMzMuNjM3TDE0MjYuMDUsMTcxLjI2NWExNTcuNTU3LDE1Ny41NTcsMCwwLDEsMTc1Ljk5LDEzNi42NDdsMTU3LjkzLDEyNTYuNUw0NjYuOTI5LDE3MjYuNzlhMTU3LjU3NCwxNTcuNTc0LDAsMCwxLTE3Ni0xMzYuNjVaIi8+CiAgICA8L2NsaXBQYXRoPgogIDwvZGVmcz4KPC9zdmc+#mask');
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2300 1800" width="300">
<image width="100%" href="https://res.cloudinary.com/alvarosaburido/image/upload/v1589435086/blog/The%20Magic%20of%20SVG%20Clip-path/pic_yo5eyq.png" />
</svg>

ForeignObject in an SVG doesn't work - React

I'm trying to display a React component inside of an SVG. I used the foreignObject component to display my React object (ToolbarItem) inside of the SVG. However, nothing is displayed. What I did wrong?
Thanks for your help
<svg xmlns="http://www.w3.org/2000/svg" width="222.002" height="119.151" viewBox="0 0 222.002 119.151">
<g id="Margin" transform="translate(-51 -59)">
<path id="Soustraction_10" data-name="Soustraction 10" d="M10914,6398.1h0l-39-38.132v-41.828l39-38.139Z" transform="translate(-10641 -6220.475)" fill="#313c57">
<foreignObject x="40" y="40" width="100" height="100">
<ToolbarItem propKey="marginTop" type='draggableNumber' max={maxTop} />
</foreignObject>
</path>
</g>
</svg>
You can add a body tag
<foreignObject x="40" y="40" width="100" height="100">
<body>
<ToolbarItem propKey="marginTop" type='draggableNumber' max={maxTop} />
</body>
</foreignObject>
EDIT: the above works but generates a warning message.
Do the following instead :
<foreignObject x="40" y="40" width="100" height="100">
<div data-xmlns="http://www.w3.org/1999/xhtml">
<ToolbarItem propKey="marginTop" type='draggableNumber' max={maxTop} />
</div>
</foreignObject>

Get fill color of svg rectangle with plain javascript

I have a pretty simple question, but i'm really stuck on this and can't get it working.
I have an svg that contains a rectangle with an id 'bridge'.
I want to get its fill color in console.
I thought "document.getElementById('bridge').style.fill" should work but it just returns an empty string.
I tried some other ways but they all didn't work.
Please help me to get rectangle's fill color with plain javascript and explain why style.fill return an empty string.
let bridge=document.getElementById('bridge');
bridge.addEventListener('click', function() { console.log(bridge.style.fill); });
<svg id="game" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 300">
<rect id="bridge" x="200" y="100" width="200" height="300" rx="10" fill='#80c41e'/>
</svg>
Try using getAttribute() instead:
let bridge = document.getElementById('bridge');
bridge.addEventListener('click', function() {
console.log(bridge.getAttribute('fill'));
});
<svg id="game" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 300">
<rect id="bridge" x="200" y="100" width="200" height="300" rx="10" fill="#80c41e"/>
</svg>
You want the computed style because you've set a mapped CSS property via an attribute so the style setting is indirect.
let bridge=document.getElementById('bridge');
bridge.addEventListener('click', function() {
console.log(window.getComputedStyle(bridge).getPropertyValue('fill')); });
<svg id="game" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 300">
<rect id="bridge" x="200" y="100" width="200" height="300" rx="10" fill='#80c41e'/>
</svg>
Alternatively you could actually use a style rather than a mapped attribute i.e.
let bridge=document.getElementById('bridge');
bridge.addEventListener('click', function() {
console.log(bridge.style.fill);
bridge.style.fill='red'});
<svg id="game" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 600 300">
<rect id="bridge" x="200" y="100" width="200" height="300" rx="10" style="fill:#80c41e;"/>
</svg>
As that makes it easier to set a new style.

Set X and Y value for g element of SVG

I am relatively new in SVG drawing with HTML5.
What I want to do is to make a group of elements in SVG with g element so that all elements inside of that g element can work like a group and all the element's base x and y value can be received from the upper g element.
So, what I have done is something like this-
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<g x="1000" y="1000">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<text style="text-anchor: middle;" class="small">My Text</text>
</g>
</svg>
What I expected is all the elements inside the g element will get x="1000" and y="1000" so my expected output is like this-
But I am getting this-
Re-
I don't like to set x and y element in text element. I just want to set relative x and y into the text element if needed, but that should be relative to g element.
Can anyone help me what I need to do to achieve my target with a group in SVG?
<g> elements don't support x or y attributes. You can use a transform instead though.
I've decreased the values from 1000 to 100 as otherwise the output is outside the 500 x 300 canvas of the outer <svg> element.
I've provided additional x and y attributes on the text element so it appears positioned as in your example. If wanted you could put the text itself in a <g> element or an <svg> element.
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(100, 100)">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<text x="100" y="30" style="text-anchor: middle;" class="small">My Text</text>
</g>
</svg>
or using an additional <g> element to avoid x and y on the text itself.
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(100, 100)">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<g transform="translate(100, 30)">
<text style="text-anchor: middle;" class="small">My Text</text>
</g>
</g>
</svg>
Alternatively you could use an inner <svg> element instead of a <g> element as that does support x and y attributes
<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
<svg x="100" y="100">
<title>SVG Title Demo example</title>
<rect width="200" height="50"
style="fill:wheat; stroke:blue; stroke-width:1px"/>
<text x="100" y="30" style="text-anchor: middle;" class="small">My Text</text>
</svg>
</svg>

SVG element not visible on screen, can see element added in console

I have the following SVG code. The SVG with id "nestedsvg" is being appended in the HTML, I can view it on the console. But it's not visible on the screen. I tried assigning it a z-index of 99 but still it's invisible. Where am I going wrong?
<svg data="BusinessRoleFigure" x="144" y="95"
width="128" height="66" id="outer" style="position: relative;">
<rect x="0" y="0" width="100%" height="100%"
stroke="rgb(178,178,126)" stroke-width="1" fill="rgb(255,255,181)"
style="position: relative;"></rect>
<svg id="nestedsvg" x="100%" height="100" width="50">
<rect x="-50" rx="5" ry="5" width="20" height="10" stroke="black"
stroke-width="1" fill="black" z-index="99"></rect>
</svg>
<circle cx="118" cy="13" r="5" fill="none"
stroke-linejoin="round" stroke="black"
z-index="1" stroke-width="1"></circle>
</svg>
Jsfiddle: http://jsfiddle.net/MxHPq/145/
This is because the rectangle you are drawing is outside of the nested SVG viewport.
That SVG has a width and height of 100x50, and you are drawing a 20x10 rectangle at (-50,0). Meaning the rectangle covers the area from (-50,0) to (-30,10). So it is not visible. By default, objects outside a nested SVG viewport are not visible.
There are two ways to fix this:
Make objects outside the viewport visible. You can do this by setting overflow="visible" on the nested SVG.
<svg data="BusinessRoleFigure" x="144" y="95" width="128" height="66" id="outer">
<rect x="0" y="0" width="100%" height="100%" stroke="rgb(178,178,126)" stroke-width="1" fill="rgb(255,255,181)"></rect>
<svg id="nestedsvg" x="100%" height="100" width="50" overflow="visible">
<rect x="-50" rx="5" ry="5" width="20" height="10" stroke="black" stroke-width="1" fill="black"></rect>
</svg>
<circle cx="118" cy="13" r="5" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></circle>
</svg>
Move the rectangle inside the SVG viewport and reposition the SVG so that the rectangle ends up in the same place.
I don't know why you wanted the nested SVG to be at x="100%", but you would need to change that if you go with this solution.
<svg data="BusinessRoleFigure" width="128" height="66" id="outer">
<rect x="0" y="0" width="100%" height="100%" stroke="rgb(178,178,126)" stroke-width="1" fill="rgb(255,255,181)"></rect>
<svg id="nestedsvg" x="78" height="100" width="50">
<rect x="0" rx="5" ry="5" width="20" height="10" stroke="black" stroke-width="1" fill="black"></rect>
</svg>
<circle cx="118" cy="13" r="5" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></circle>
</svg>
A few other notes about your original SVG:
x and y coordinates on the root <svg> element have no effect.
z-index currently has no meaning in SVGs. Although this may change for the upcoming SVG2 standard.
position: relative has no meaning in SVGs.
I've removed these things from my modified examples.

Categories

Resources