How to detect real size of svg path in FireFox? - javascript

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!

Related

Changing the length of a circle SVG stroke programmatically?

I created a circle in Inkscape and switched off the fill so that only the stroke is visible and also made the starting point 45 degrees and the ending point 315 degrees.
I then rotated it 90 degrees and this is the end result.
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="300" height="300" version="1.1" viewBox="0 0 79.375 79.375" xmlns="http://www.w3.org/2000/svg">
<path transform="rotate(90)" d="m64.961-15.355a34.984 34.412 0 0 1-49.474 1e-6 34.984 34.412 0 0 1-1e-6 -48.666 34.984 34.412 0 0 1 49.474-2e-6" fill="none" opacity=".55814" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="10.583"/>
</svg>
And it renders like this:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="300" height="300" version="1.1" viewBox="0 0 79.375 79.375" xmlns="http://www.w3.org/2000/svg">
<path transform="rotate(90)" d="m64.961-15.355a34.984 34.412 0 0 1-49.474 1e-6 34.984 34.412 0 0 1-1e-6 -48.666 34.984 34.412 0 0 1 49.474-2e-6" fill="none" opacity=".55814" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="10.583"/>
</svg>
I want to be able to overlay a copy and control the length of the stroke starting at the bottom left corner. So for example display only 22% of the total length of the overlay or display the segment from the 315 degree end point to 255.60 degrees?
How would we go about this (Effectively programmatically doing the inkscape start and end controls)?
Probably easiest way would be to use pathLength attribute like:
pathlength="100" stroke-dasharray="10 100"
Altering the the first value of the stroke-dasharray would express the "percentage" (10 in above example makes it occupy 10% of the total length). Moving that segment along the path is possible thanks negative stroke-dashoffset. Funky perk of this approach is that it can be used for "tracing" any compact path:
<body bgcolor="darkslategray" text="snow">
Length:
<input id="l" type="range" value="10" max="80"
oninput="p.setAttribute('stroke-dasharray', value + ' 100');
o.max = 100 - value; nextElementSibling.value = value;
"> <output>10</output><br>
Offset:
<input id="o" type="range" value="20" max="90"
oninput="p.setAttribute('stroke-dashoffset', - value);
l.max = 100 - value; nextElementSibling.value = value;
"> <output>20</output><br>
<svg width="300" height="150" viewBox="0 0 80 80" fill="none" stroke-linecap="round"
stroke="#000" stroke-width="10">
<path d="M13 66 A 35 35 0 1 1 65 65 A 10 10 1 1 1 40 45 A 7 7 1 1 0 25 35" />
<path d="M13 66 A 35 35 0 1 1 65 65 A 10 10 1 1 1 40 45 A 7 7 1 1 0 25 35"
id="p"
stroke="#F99"
stroke-width="4"
pathlength="100.1"
stroke-dashoffset="-20"
stroke-dasharray="10 1000"
/></svg>
<!--
* 100.1 pathlength value is to keep zero length "dot" at the final 100 offset
* 1000 array "gap" value is here just to be sure; any value greater or equal pathlength should do.
-->
The following snippet takes a percentage as input and then computes the parameters of the elliptical arc curve command A in the <path> element. 100% corresponds to a three-quarter-arc.
var path = document.getElementById("path");
function draw(v) {
var theta = v * Math.PI * 0.015;
var large = theta <= Math.PI ? 0 : 1;
path.setAttribute("d", `M1,0 A1,1 0 ${large} 1 ${Math.cos(theta)},${Math.sin(theta)}`);
}
<svg width="150" height="150" viewBox="-1.2 -1.2 2.4 2.4"
transform="rotate(135)">
<path d="M1,0 A1,1 0 1 1 0,-1"
fill="none" opacity=".55814"
stroke="#000" stroke-linecap="round"
stroke-linejoin="round" stroke-width=".25"/>
<path id="path"
fill="none"
stroke="red" stroke-linecap="round"
stroke-linejoin="round" stroke-width=".1"/>
</svg>
<form onsubmit="draw(this.v.value); return false;">
<input name="v"/>%
<input type="submit" value="Draw"/>
</form>
(After reading myf's answer, it became clear that you want two arcs in total. I have adapted my code accordingly, but the idea remains basically the same.)

Drawing a box with two sides as an Arc in javascript/svg

I am trying to make a shape like this
and i managed to get this sofar
But i cant get the right arc to go outwards instead of inwards.
I cant find any help online how to do this. I tried rotation and playing around with the rx,ry but no luck.
Here is the JSFiddle to it
JsFiddle
<svg height="200" width="300">
<g stroke="none" fill="blue">
<path d="
M 150 0
a 1 1 0 0 0 0 100
l -100 0
a 1 1 0 0 0 0 -100
l 100 0
Z
"/>
</g>
</svg>
On the first arc, you simply need to flip the sweep-flag to "clockwise":
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#elliptical_arc_curve
<svg height="200" width="300">
<g stroke="none" fill="blue">
<path d="
M 150 0
a 1 1 0 0 1 0 100
l -100 0
a 1 1 0 0 0 0 -100
l 100 0
Z
"/>
</g>
</svg>

Dynamically generate SVG with images inside?

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>

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>

Animate SVG element after changing it on the fly

Internet Explorer does not support beginElement for SVG animations, so I am looking for a workaround. My aim is to string together a series of animations, under user control. Below is a demo that shows two animations to simulate stages in blowing up a balloon. If you click on the smaller balloon after its first animation
In Chrome, Firefox and Opera: it jumps to the final stage of the second animation ()
In Safari: it shows the start of the animation but never animates
In Internet Explorer: I don't get any animation at all
Is there a cross-platform technique I can use to create a sequence of animations, where the user must intervene between each animation?
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="500px" height="500px" viewBox="0 0 250 250">
<defs>
<path id="one"
d="M 0 95 l 30 0 A 20 15 30 1 1 30 105 l -30 0 z"
fill="blue">
<animate
attributeName="d"
attributeType="XML"
from="M 0 95 l 30 0 A 20 15 30 1 1 30 105 l -30 0 z"
to="M 0 95 l 30 0 A 25 20 0 1 1 30 105 l -30 0 z"
dur="1s"
fill="freeze" />
</path>
<path id="two"
d="M 0 95 l 30 0 A 25 20 0 1 1 30 105 l -30 0 z"
fill="blue">
<animate
attributeName="d"
attributeType="XML"
from="M 0 95 l 30 0 A 25 20 0 1 1 30 105 l -30 0 z"
to="M 0 95 l 30 0 A 50 40 0 1 1 30 105 l -30 0 z"
dur="1s"
fill="freeze" />
</path>
</defs>
<use id="a" xlink:href="#one"/>
<use xlink:href="#two" transform="rotate(180 125 125)"/>
</svg>
<script>
var xlinkNS = "http://www.w3.org/1999/xlink"
var use = document.querySelector("#a")
use.onmouseup = inflate
function inflate() {
use.setAttributeNS(xlinkNS, "xlink:href", "#two")
}
</script>
jsFiddle
Intenet Explorer doesn't support SMIL animation at all.
The usual recommended workaround for IE is to use the Fakesmile library which adds SMIL support to IE. It supports beginElement().
I would strongly recommend Snap.svg
Edit: SVG is powerful and complex. Snap.svg gives you a ton of control over your svg animations whether they're created dynamically or with a tool like Adobe Illustrator or Sketch. It's a cross-browser solution written by the same person that wrote Raphael (another svg animation library)
It makes things really easy and is quite awesome to use. So instead of fiddling around with what you've got I would strongly recommend getting your hands on Snap.svg. At least follow the link and check it out. Very cool!

Categories