in svg: translate vs position x and y - javascript

When I want to position simple objects such as rect or line should I use transform attribute or x and y attributes?
// this
d3.selectAll('rect')
.attr('x', d => d)
.attr('y', 0)
// or this?
d3.selectAll('rect')
.attr("transform", d => `translate(${d}, 0)`);
What is the performance difference?

In SVG transform is not hardware accelerated. They have around the same performance for single elements (in my experience). However, I use transform more to move thing around because in SVG not all elements have a x or y attributes, consider...
<line x1="0" y1="0" x2="100" y2="100" />
<circle cx="100" cy="100" r="100" />
<path d="M 0 0 L 100 100" />
<rect x="0" y="0" width="100" height="100" />
You must write a different implementation for each of these elements if you are not using transform. One area where transform is indeed faster is moving a large number of elements, if you have...
<g transform="translate(100, 100)">
<line x1="0" y1="0" x2="100" y2="100" />
<circle cx="100" cy="100" r="100" />
<path d="M 0 0 L 100 100" />
<rect x="0" y="0" width="100" height="100" />
</g>
It will be less processing intensive than moving each element individually

Related

SVG connect two points with a line, and automatically update the line if a point is moved

I'd like to connect two points (circles) with a line:
window.onclick = () => {
document.getElementById('c2').setAttribute("cx", 150);
}
<svg>
<circle cx="10" cy="10" r="2" id="c1" />
<circle cx="90" cy="50" r="2" id="c2" />
<line x1="10" y1="10" x2="90" y2="50" stroke="black" />
</svg><br>
Click here to move a circle.
such that if I modify the center of any <circle> with setAttribute("cx", 150) then the line automatically follows the new circle position.
Is there a way to do this with <use>? Something like (pseudo-code):
<svg>
<circle cx="10" cy="10" r="2" id="c1" />
<circle cx="90" cy="50" r="2" id="c2" />
<use x1=xlink:c1:cx y1=xlink:c1:cy x2=xlink:c2:cx y2=xlink:c2:cy stroke="black" type="line" />
</svg>
Goal: I don't want to have to set the coordinates two times, in both the circle and line. Instead I would like to set the coordinates once, and that the line uses a reference to the circle elements.
Note: I have read SVG connect two points with a line but it did not help.
You can use a <marker> that can be placed on start, middle and end of an element.
window.onclick = () => {
document.getElementById('l1').setAttribute("x2", 150);
}
<svg viewBox="0 0 200 100" width="200">
<defs>
<marker id="circle" viewBox="0 0 4 4" refX="2"
refY="2" markerWidth="4" markerHeight="4">
<circle cx="2" cy="2" r="2" />
</marker>
</defs>
<line id="l1" x1="10" y1="10" x2="90" y2="50" stroke="black"
marker-start="url(#circle)" marker-end="url(#circle)"/>
</svg><br>
Click here to move a circle.

Fill Percentage area in a custom Progress Bar SVG

I am new to SVG and stuck with a small task.
I would like to fill a custom SVG to a specific percentage.
Here is my initial SVG
<svg width="233" height="9" viewBox="0 0 233 9" fill="none" xmlns="http://www.w3.org/2000/svg"><path id="base1" d="M0.0104229 4.53685C0.585564 0.0541843 77.5774 0.498692 134.721 1.59593C171.989 2.31113 232.913 -0.235688 232.75 4.22739C232.525 10.3451 134.045 7.87626 89.0013 7.23356C39.1891 6.52242 -0.727053 10.2816 0.0104229 4.53685Z" fill="#DADBDD"></path></svg>
Here is my final SVG
<svg width="233" height="9" viewBox="0 0 233 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.0104229 4.53685C0.585564 0.0541843 77.5774 0.498692 134.721 1.59593C171.989 2.31113 232.913 -0.235688 232.75 4.22739C232.525 10.3451 134.045 7.87626 89.0013 7.23356C39.1891 6.52242 -0.727053 10.2816 0.0104229 4.53685Z" fill="#DADBDD" />
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="233" height="9">
<path d="M0.0104229 4.53685C0.585564 0.0541843 77.5774 0.498692 134.721 1.59593C171.989 2.31113 232.913 -0.235688 232.75 4.22739C232.525 10.3451 134.045 7.87626 89.0013 7.23356C39.1891 6.52242 -0.727053 10.2816 0.0104229 4.53685Z" fill="#DADBDD" />
</mask>
<g mask="url(#mask0)">
<ellipse rx="49.9644" ry="25.4799" transform="matrix(0.943377 0.331722 -0.657906 0.7531 34.4845 13.7852)" fill="#ED718F" />
</g>
</svg>
I want to fill this with the percentage area as entered by the user.
Any help will be highly appreciated
As I've commented: if you want to use a maska mask you can use a thick line (stroke-width="9" - for example) instead of the ellipse, and you can mask this line. Then you can change the stroke-dasharray according to the percentage you have.
//the total length of the line which in this case is as long as the shape
let tl = theLine.getTotalLength();
// the percentage for the progress
let xperc = itr.value;
onInput();
itr.addEventListener("input", onInput);
// a function that is setting the value for the stroke-dasharray of the line according with the progress
function onInput() {
xperc = itr.value;
theLine.setAttribute("stroke-dasharray", `${tl * xperc} ${tl - tl * xperc}`);
}
<input id="itr" type="range" min="0" max="1" value="0.35" step=".001" /><br>
<svg width="233" height="9" viewBox="0 0 233 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="theShape" d="M0.0104229 4.53685C0.585564 0.0541843 77.5774 0.498692 134.721 1.59593C171.989 2.31113 232.913 -0.235688 232.75 4.22739C232.525 10.3451 134.045 7.87626 89.0013 7.23356C39.1891 6.52242 -0.727053 10.2816 0.0104229 4.53685Z" />
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="233" height="9">
<use xlink:href="#theShape" fill="#fff" />
</mask>
</defs>
<use xlink:href="#theShape" fill="#DADBDD" />
<path id="theLine" d="M0 4.5L233 4.5" stroke="#ED718F" stroke-width="9" mask="url(#mask0)" />
</svg>

How do I rotate and skew an SVG rect "in-place"?

I am playing with SVG and I am stumped by something.
I am trying to make the pink square into a diamond by using skew and rotate.
However I am getting strange behaviour that I cannot figure out how to overcome.
Adding the skew, gets me the diamond effect I want, but then I need to rotate and reposition it so it lines up with the circles.
<rect x="126" y="0" width="40" height="40"fill="pink" transform="skewY(10)" />
However, when I apply rotation transform="rotate(45)" to the rect, it doesn't rotate "in-place", but rotates [I think] relative from the corner of the page.
<rect x="126" y="0" width="40" height="40"fill="pink" transform="skewY(10)" />
Does anyone know how I can freely rotate and skew this rectangle (in SVG and not CSS or anything) without it moving around so wildly and awkwardly ?
<h1>Shapes</h1>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" r="20" fill="blue" stroke="red" ></circle>
<circle cx="62" cy="20" r="20" fill="yellow" stroke="red" ></circle>
<circle cx="104" cy="20" r="20" fill="blue" stroke="red" ></circle>
<rect x="126" y="0" width="40" height="40"fill="pink"/>
<circle cx="188" cy="20" r="20" fill="green" stroke="red" ></circle>
</svg>
Simplest is to use transform-origin and transform-box
rect {
transform-origin: center;
transform-box: fill-box;
}
<h1>Shapes</h1>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" r="20" fill="blue" stroke="red" ></circle>
<circle cx="62" cy="20" r="20" fill="yellow" stroke="red" ></circle>
<circle cx="104" cy="20" r="20" fill="blue" stroke="red" ></circle>
<rect transform="rotate(45)" x="126" y="0" width="40" height="40"fill="pink"/>
<circle cx="188" cy="20" r="20" fill="green" stroke="red" ></circle>
</svg>
<h1>Shapes</h1>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="21" cy="21" r="20" fill="blue" stroke="red"></circle>
<circle cx="63" cy="21" r="20" fill="yellow" stroke="red"></circle>
<circle cx="105" cy="21" r="20" fill="blue" stroke="red"></circle>
<rect x="154" y="0" width="40" height="40" fill="pink" transform="rotate(45, 154, 0)"/>
<circle cx="203" cy="21" r="20" fill="green" stroke="red"></circle>
</svg>
This can be done using rotate. For rotate
First argument is the angle of rotation i.e. 45 degrees.
Second argument is the x offset about which the rect is to be rotated and is calculated as follows:
x = (border_width_of_circle_1 * 2 + radius_of_circle_1 * 2) + (border_width_of_circle_2 * 2 + radius_of_circle_2 * 2) + (border_width_of_circle_3 * 2 + radius_of_circle_3 * 2) + 1/2 * diagonal_of_square
= (2 + 40) + (2 + 40) + (2 + 40) + (1/2 * sqrt(40^2 + 40^2))
= 42 + 42 + 42 + (1/2 * sqrt(3200))
= (42 * 3) + (1/2 * 56)
= 126 + 28 = 154
Third argument is the y offset about which the rect is to be rotated, which in our case will be 0.

CSS JS animate image along path and make dashes afterwards

I would like to animate an image on a path and show the route of the object (e.g a plane flying over a map). It should look like on this image:
But the dashes should apply after the object has reached the position, so the dashes were shown after the object.
I have tried multiple times, but I can do only once. Dash animation or plane on path . Does someone knows a solution.
animate a mask over the dashed path
move the plane along the same path
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1" height="200" width="400">
<defs>
<path id="basePath" d="M 50,150 A 280 500 0 0 1 350,150" />
<mask id="mask">
<use xlink:href="#basePath" stroke-width="3" stroke="white"
stroke-dasharray="1000,0" fill="none">
<animate attributeName="stroke-dasharray" from="0,348.5" to="348.5,0"
begin="0s" dur="5s" fill="freeze" />
</use>
</mask>
</defs>
<circle r="4" cx="50" cy="150" fill="grey" />
<circle r="4" cx="350" cy="150" fill="grey" />
<use xlink:href="#basePath" stroke-width="2" stroke-dasharray="10"
stroke="grey" fill="none" mask="url(#mask)"/>
<path d="M 27,3 H 21 L 13,15 H 9 L 12,3 H 5 L 3,7 H -1 L 1,0 -1,-7 H 3 L 5,-3 H 12 L 9,-15 H 13 L 21,-3 H 27 C 33,-3 33,3 27,3 Z"
fill="white" stroke="black" stroke-width="1.5">
<animateMotion rotate="auto" begin="0s" dur="5s" fill="freeze">
<mpath xlink:href="#basePath"/>
</animateMotion>
</path>
</svg>
The main thing to do is to calculate the length of the path, so you can set the stroke-dasharray values for the mask animation such that they keep pace with the animated plane. You can get that length in Javascript with
document.querySelector('#basePath').getTotalLength()

Change an inline SVG's x and y with ecmascript

I am using an inline SVG in an SVG and have set some default x and y values. When I change them, the inline SVG moves accordingly. I am trying to change it with
var inlineSVG = document.getElementById("inlineSVG");
inlineSVG.style.x = "90";
and that adds style="x:90px;" but that doesn't actually affect the element.
It's weird (in my head) because this works with a rect but not with an svg.
Here is my actual code:
<?xml version='1.0' encoding='UTF-8'?>
<svg width='1000' height='360'
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink="http://www.w3.org/1999/xlink"
onload='init(evt)'
>
<script type='text/ecmascript'>
function init(event){
var wing1 = document.getElementById("wing1");
wing1.style.x = "90";
}
</script>
<circle cx="200" cy="140" r="5" fill="red" />
<circle cx="220" cy="170" r="5" fill="red" />
<circle cx="180" cy="170" r="5" fill="red" />
<circle cx="220" cy="220" r="5" fill="red" />
<circle cx="180" cy="220" r="5" fill="red" />
<svg id="wing1" x="280" y="100" viewBox="0 0 350 300">
<g>
<g>
<g>
<ellipse fill="#E6E7E8" cx="229.505" cy="117.813" rx="5.862" ry="4.547"/>
</g>
<g>
<ellipse fill="#E6E7E8" cx="265.931" cy="117.819" rx="5.862" ry="4.547"/>
</g>
</g>
<g>
<g>
<ellipse fill="#E6E7E8" cx="229.191" cy="125.538" rx="5.862" ry="4.547"/>
</g>
<g>
<ellipse fill="#E6E7E8" cx="265.617" cy="125.531" rx="5.861" ry="4.547"/>
</g>
</g>
</g>
<ellipse fill="#E6E7E8" cx="247.244" cy="121.796" rx="20.635" ry="38.017"/>
</svg>
<rect id="square" x="0" y="470" width="50" height="50" fill="#BADA55" style="fill-opacity : 0.5" />
<line x1="0" y1="0" x2="1000" y2="360" style="stroke: yellowgreen;
stroke-width: 1;
stroke-dasharray: 10 1;"></line>
<line x1="0" y1="360" x2="1000" y2="0" style="stroke: yellowgreen;
stroke-width: 1;
stroke-dasharray: 10 1;"></line>
I tried adding !important to the value but it didn't work ( because I guess it doesn't count it as a valid number? ).
The solution is to directly change the x attribute like so:
selector.setAttribute("attr",val);

Categories

Resources