Dynamically generate SVG with images inside? - javascript

AngularJS app.
Basically I've to make an horizontal arrow-like drawing with images that will be repeating inside depending on the array of items i sent to the html(ng-repeat).
I got told the best would be svg so here I am, but my knowledge of svg is pretty low. So I managed to make a little arrow with fixed witdh and height. But now I need it a lot bigger.
So I read about viewBox online. It scales the items I have in the SVG, but I have two problems so far.
Images don't work.
All items are stacked up and don't follow the space in between them that I had before.
This code is the working code I have right now, so you get an idea of the element I'm trying to draw. I use the element so I can ng-repeat it with tooltips and text inside, this code is simplified.
.container {
display: flex;
}
.justaclass {
fill: #9b3d9c;
background-color: #9b3d9c;
}
.justaclass-group {
display: inline-block;
position: relative;
}
.justaclass-image {
padding: 2px;
}
<div class="container">
<svg width="8" height="34">
<path class="justaclass"
d="M 0 0 L 7 17 L 0 34 L 17 34 L 17 0 z">
</path>
</svg>
<svg width="33" height="34" class="justaclass justaclass-image">
<image x="0" y="3" width="33" height="25" href="https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"></image>
</svg>
<div class="justaclass-group">
<svg width="50" height="34" class="justaclass image">
<image width="45" height="30"
x="0" y="0"
href="https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg">
</image>
</svg>
<svg width="10" height="34">
<path class="justaclass"
d="M 0 0 L 0 34 L 10 17 z">
</path>
</svg>
</div>
The code isn't perfect pixel-wise here, but that's not my main issue.
Now that I try to make it bigger with viewBox, I can draw the 4 elements but they are misplaced. So If I add display:flex or display:table I quite cannot get the elements aligned horizontally, the elements just disappear.
Anyone got any idea of how to dinamically resize all this elements plus making them all in the same line just like display flex would do?
My first try at doing the above image with viewBox
But If I add display:flex into the container, the svg's are gone and I only see images.
.container {}
.justaclass {
fill: #9b3d9c;
background-color: #9b3d9c;
}
<div class="container">
<svg viewBox="0 0 150 50">
<path class="justaclass"
d="M 0 0 L 7 17 L 0 34 L 17 34 L 17 0 z">
</path>
</svg>
<svg viewBox="0 0 150 50">
<image width="150" height="50" href="https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"></image>
</svg>
<svg viewBox="0 0 150 50">
<image width="150" height="50" href="https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"></image>
</svg>
<svg viewBox="0 0 150 50">
<path class="justaclass"
d="M 0 0 L 0 34 L 10 17 z">
</path>
</svg>
</div>

There are a lot of things that need to change here. First lets talk about viewBox. This attribute describes how the numbers inside the <svg> tag relate to those outside. It gives a rectangle into which the inside content is rendered. Then, this rectangle is scaled such that it fits into the size given by width and height. That means, giving a viewBox without also defining width and height makes not much sense, as the size the content should be scaled to is missing.
What happens if the aspect ratio of the viewBox and that of the width and height do not match? As a default, the content is set into the middle and scaled such that it just fits. Other values can be given with the preserveAspectRatio attribute. For example, to get the left "arrow" part to the right of its rectangle (so there is no gap), set preserveAspectRatio="xMaxYMid meet".
width and height can be given as attributes for SVG content or as CSS properties. It makes sense to define them as styles if they are to be changeable.
Your linked images do not need to have a concrete size. Giving them width="100%" height="100%" scales them to the containing SVG element, without distorting them (unless you define so with preserveAspectRatio="none".
.container {
display: flex;
}
.container svg {
width: 50px;
height: 50px;
}
.container .justaclass {
fill: #9b3d9c;
background-color: #9b3d9c;
width: 150px;
}
<div class="container">
<svg width="50" height="50" viewBox="0 0 8 34" preserveAspectRatio="xMaxYMid meet">
<path
d="M 0 0 L 7 17 L 0 34 L 17 34 L 17 0 z">
</path>
</svg>
<svg class="justaclass">
<image width="100%" height="100%" href="https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"></image>
</svg>
<svg class="justaclass">
<image width="100%" height="100%" href="https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"></image>
</svg>
<svg viewBox="0 0 10 34" preserveAspectRatio="xMinYMid meet">
<path
d="M 0 0 L 0 34 L 10 17 z">
</path>
</svg>
</div>

viewBox defines the size of the contents of the svg before being scaled. You need to set them to the original size of your image and then scale the elements up either with the width and height tags you used before or with css. Here's a fiddle of everything scaled up to double size: https://jsfiddle.net/ohnqbufa/
You can see here that the viewport of your first element is set to the original size and the width and height are set to double.
<svg width="16" height="68" viewBox="0 0 8 34">
<path class="justaclass"
d="M 0 0 L 7 17 L 0 34 L 17 34 L 17 0 z">
</path>
</svg>

Related

SVG make a clip-path to scale and relocate as its container scales

I have this svg:
<svg viewBox="0 0 280 280" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<clipPath id="clipPath">
<path d="M 10 10 q 49 41 100 0 c -14 46 -14 40 50 50 c -24 -1 -50 -6 -40 40 c -14 -44 -53 -73 -99 -51 Z"/>
</clipPath>
</defs>
<rect x="5" y="5" width="190" height="90" fill="#770000" clip-path="url(#clipPath)"/>
</svg>
I can't use CSS styles here, just SVG. And I need to scale and relocate the clipPath as the rect scales or moves.
I tried adding this to the clipPath definition <clipPath id="clipPath" width="190" height="90" x="5" y="5"> with no result. Tried adding these parameters to the path too. No result both times. Also tried reducing the dimensions of the rect the clipPath will truncate instead of readjusting, same thing if I change the X,Y position. Any hint on how to solve this?

How to make svg fit container in React?

See the image above. That is the svg container I have highlighted but I only want the svg to take up it's actual size (i.e. just the inner white triangle)
I've tried changing parent width and height but nothing seems to work. how do I fix this?
this is the markup:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
the second path seems to be the exact width and height of what I need
second edit:
export const Icon = ({ name }) => {
let Icon: any = icons[name]
return (
<div
className={css`
display: flex;
align-items: center;
`}
>
<Icon height={24} fill="red" />
</div>
)
}
and icons is
import { ReactComponent as MyIcon } from "./my-icon.svg"
const icons = {
"my-icon": MyIcon
}
M12 16l-6-6h12z represents your triangle and means
move to the point with coordinates 12 16
then draw the line to -6 -6 (relatively to current point)
then draw the horizontal line with the length equals to 12 then
to go the first point
And you get your icon as the result.
There are several ways you can fit the path, but you should understand how a path works. You can read about it here.
And see examples below
First way to fit:
svg {
border: 1px solid red;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#red">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M 12 24 l -12 -24 h24 z"></path>
</svg>
Second:
svg {
border: 1px solid red;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#red">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M 12 12 l -12 -12 h24 z"></path>
</svg>
You can see from the SVG that the width and height are currently set to 24.
A good start would be to set the width and height to 1em to better match the font size. See first example in the demo below.
If you feel the triangle is too small now, you can make it appear bigger by adjusting the viewBox. At the moment it has quite a bit of padding around it. To shring that padding, increase the viewBox x and y (first two numbers) and reduce the width and height (second two numbers. In the second example below, I have shrunk the padding by 3 units all around the icon by increasing the x and y by 3, and reducing the width and height, by 2 * 3 correspondingly.
The icon is now bigger compared to the text, But it can appear a bit high now.
There are a few ways to adjust that. Including the following:
Use position: relative and top: 2px to push the <svg> down a bit.
Reduce the icon size to something like 0.7em, so that the icon matches better the X height of the font, rather than the full Em height.
Use a different vertical-align setting to adjust the position of the icon relative to the rest of the line. In example 3 below, I have set the icon to vertical-align: text-top.
Push the icon lower in the viewBox by increasing the padding at the top of the viewBox. In example 4 below I've reduced the viewBox y (second number) to increase the top padding.
body {
background-color: dodgerblue;
}
div {
font: 16px sans-serif;
color: white;
}
svg {
width: 1em;
height: 1em;
}
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3 3 18 18" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3 3 18 18" width="24" height="24" fill="#fff" style="vertical-align: text-top">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3 1 18 18" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>

change the filled color of an SVG image in react

I'm trying the control the filled level of an oval SVG.
<?xml version="1.0" encoding="iso-8859-1"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" fill="grey">
<g>
<g>
<path d="M256,0C114.837,0,0,114.837,0,256s114.837,256,256,256s256-114.837,256-256S397.163,0,256,0z M256,490.667
c-129.387,0-234.667-105.28-234.667-234.667S126.613,21.333,256,21.333S490.667,126.613,490.667,256S385.387,490.667,256,490.667z
"/>
</g>
i started to just trying to fill the oval to 100% and it'snot working.
<Card sx={{ maxWidth: 345 }}>
<CardHeader
}
...
<div>
<img src={OvalTank} style={Style} alt="Oval Tank" className=" ovalFilled" />
</div>
Style being set this way
const Style = {
height: 200,
margin: 'auto',
display: 'flex',
justifyContent: 'center',
fill: blue,
}
I try removing the fill attribute from the svg file(which is controlling the border color only).
can someone help to find out how to control filled color?
thanks
With some adjustments in your svg markup, you could place your svg assets by a <use> element.
Your svg element will need a an id for referencing.
I recommend wrapping your graphic in a <symbol> element like so:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<symbol id="path">
<path d="M256,0C114.837,0,0,114.837,0,256s114.837,256,256,256s256-114.837,256-256S397.163,0,256,0z M256,490.667
c-129.387,0-234.667-105.28-234.667-234.667S126.613,21.333,256,21.333S490.667,126.613,490.667,256S385.387,490.667,256,490.667z" />
</symbol>
</svg>
Now you're able to place an instance of your image by a reference:
<svg viewBox="0 0 512 512" style="fill:red">
<use href="ovalTank.svg#path" />
</svg>
Since we've stripped all fill attributes, we can style/override colors for every symbol instance.
The main benefit of this symbol/use approach is the idea of storing multiple graphic assets in one single svg file.
However, loading symbols from a external file will need this file to be on same domain.
Here is an inlined svg example:
svg {
display: inline-block;
width: 5em;
}
<!-- svg file content -->
<svg style="display:none;" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512">
<symbol id="path">
<path d="M256,0C114.837,0,0,114.837,0,256s114.837,256,256,256s256-114.837,256-256S397.163,0,256,0z M256,490.667
c-129.387,0-234.667-105.28-234.667-234.667S126.613,21.333,256,21.333S490.667,126.613,490.667,256S385.387,490.667,256,490.667z" />
</symbol>
<symbol id="icon-calendar" viewBox="0 0 448 512">
<path d="M152 64H296V24C296 10.75 306.7 0 320 0C333.3 0 344 10.75 344 24V64H384C419.3 64 448 92.65 448 128V448C448 483.3 419.3 512 384 512H64C28.65 512 0 483.3 0 448V128C0 92.65 28.65 64 64 64H104V24C104 10.75 114.7 0 128 0C141.3 0 152 10.75 152 24V64zM48 448C48 456.8 55.16 464 64 464H384C392.8 464 400 456.8 400 448V192H48V448z" />
</symbol>
</svg>
<!-- usage -->
<svg viewBox="0 0 512 512" style="fill:red">
<use href="#path" />
</svg>
<svg viewBox="0 0 448 512" style="fill:green">
<use href="#icon-calendar" />
</svg>

Vertically/Horizontally Center Text inside a SVG Path

I would like to vertically/horizontally center a text inside a rectangle made with a SVG Path element.
By center I don't mean having my first letter at the center of the rectangle but having the center of my text at the center of the path.
Here is my code structure:
<svg id="shape">
<path id = "a" d="M 0 0 L 100 0 L 100 100 L 0 100 Z"></path>
<text>
<textPath xlink:href="#a">My Text</textPath>
</text>
</svg>
I've managed to achieve that by doing something like this:
<svg id="shape">
<path id = "a" d="M 0 0 L 100 0 L 100 100 L 0 100 Z"></path>
<text x="50" y="50" text-anchor="middle" alignement-baseline="middle">My Text</text>
</svg>

How to detect real size of svg path in FireFox?

I'm trying to get real "path" size, using getBoundingClientRect function for that, but in FireFox I get different results
In Google Chrome all is well:
1. 100px x 100px
2. 100px x 100px
But in FireFox:
1. 104px x 104px
2. 100px x 100px
Why stroke-width="1" added 4px? How to get real size without empty spaces in FF?
<div>With stroke-width="1"</div>
<svg width="110" height="110">
<path stroke-width="1" d="M0 0 L 100 0 L100 100 L 0 100 Z" fill="black" stroke="black"></path>
</svg>
<br>
<br>
<div>With stroke-width="0"</div>
<svg width="110" height="110">
<path stroke-width="0" d="M0 0 L 100 0 L100 100 L 0 100 Z" fill="black" stroke="black"></path>
</svg>
P.S.
I'm triyng to add "line with text" to this path. I get path position+size with getBoundingClientRect function, create new div in those coordinates.
Result:
I made solution for FF. Simple but it works.
last_current.attr('stroke-width',0); // FireFox fix!
let c_pos = last_current[0].getBoundingClientRect();
last_current.attr('stroke-width', 1); // FireFox fix!

Categories