Captain America with svg - javascript

How I can do this star right with svg? I must use svg and I try with points but not work. I mustn't use element path. Thanks.
<!DOCTYPE html>
<html>
<body>
<svg width="100" height="100">
<circle cx="50" cy="50" r="45" fill="white" stroke="red" stroke-width="10" />
<circle cx="50" cy="50" r="30" stroke="red" stroke-width="10" fill="blue" />
<polygon points="50,25 30,80 75,40 25,40 70,70" style="fill:white;"/>
</svg>
</body>
</html>

If you want to get the most accurate points for the pentastar, you can get them easily from the underlying pentagon.
A simple js function to obtain these points is something like this (REPL, which by the way you can use for polygons with any n edges):
var n = 5;
var points = [];
for (var i=0; i < n; i++) {
var x = 50;
var y = -50;
var r = 25;
points.push([x + r * Math.sin(2 * Math.PI * i / n),
y + r * Math.cos(2 * Math.PI * i / n)]);
}
Result is the pentagon points clock-wise, starting at the top (use all values as positive ones):
[ [ 50, -25 ],
[ 73.77641290737884, -42.27457514062631 ],
[ 64.69463130731182, -70.22542485937369 ],
[ 35.30536869268818, -70.22542485937369 ],
[ 26.22358709262116, -42.27457514062632 ] ]
Your order would be points[x], where x = 0, 3, 1, 4, 2.
And using them for your example rounded to the nearest pixel:
<!DOCTYPE html>
<html>
<body>
<svg width="100" height="100">
<circle cx="50" cy="50" r="45" fill="white" stroke="red" stroke-width="10" />
<circle cx="50" cy="50" r="30" stroke="red" stroke-width="10" fill="blue" />
<polygon points="50,25 35,70 73,42 26,42 65,70" style="fill:white;"/>
</svg>
</body>
</html>

This seems a bit closer.
<!DOCTYPE html>
<html>
<body>
<svg width="100" height="100">
<circle cx="50" cy="50" r="45" fill="white" stroke="red" stroke-width="10" />
<circle cx="50" cy="50" r="30" stroke="red" stroke-width="10" fill="blue" />
<polygon points="50,25 35,70 73,42 26,42 65,70" style="fill:white;"/>
</svg>
</body>
</html>
updated to use #frhd's number, make community wiki,
please see his answer for calculations

Here with path
<svg width="100" height="100">
<circle cx="50" cy="50" r="45" fill="white" stroke="red" stroke-width="10" />
<circle cx="50" cy="50" r="30" stroke="red" stroke-width="10" fill="blue" />
<!-- <polygon points="50,25 30,80 75,40 25,40 70,70" style="fill:white;"/> -->
<path fill="#fff" d="m50,25 5,17h18l-14,11 5,17-15-10-15,10 5-17-14-11h18z" />
</svg>

Related

Using SVG with IDs for subcomponents

I am trying (in javascript) to access parts of a list of SVG components in a little test page, but I am not sure I can achieve what I want this way. The main question is:
Can I have sub-components having the same id in the two SVG top components?
In the code hereafter I want to change the color inside the first disk and the first rectangle. Here is what I tried, but it is not working.
Any tip would be appreciated.
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name=viewport content='width=device-width, initial-scale=1'>
<title>SVG-ID-Test</title>
</head>
<body>
<svg id="theSVGOne" width="200" height="300" fill="#d55">
<circle id="theCircle" cx="100" cy="75" r="50"
stroke="firebrick" fill="#ddd" stroke-width="3" />
<rect id="theRectangle" x="30" y="140" width="110" height="30"
stroke="black" fill="#ddd" stroke-width="3" />
</svg>
<svg id="theSVGTwo" width="200" height="200">
<circle id="theCircle" cx="100" cy="75" r="50"
stroke="firebrick" fill="#ddd" stroke-width="3" />
<rect id="theRectangle" x="30" y="140" width="110" height="30"
stroke="black" fill="#ddd" stroke-width="3" />
</svg>
<div id="status"></div>
<script type='text/javascript'>
let svgOne = document.getElementById('theSVGOne')
let svgTwo = document.getElementById('theSVGTwo')
let statusPane = document.getElementById('status')
statusPane.innerHTML = 'svgOne => '+svgOne.childElementCount.toString()
let circOne = svgOne.firstChild
let rctOne = svgOne.lastChild
circOne.setAttribute(('fill', '#ec3'))
rctOne.setAttribute(('fill', '#e3c'))
</script>
</body>
You can use a class instead of id on the child elements (you can have as many duplicate class attributes as you want in the document) and then use querySelector on each svg to target specific children.
let svgOne = document.getElementById('theSVGOne')
let svgTwo = document.getElementById('theSVGTwo')
let statusPane = document.getElementById('status')
statusPane.innerHTML = 'svgOne => '+svgOne.childElementCount.toString()
let circOne = svgOne.querySelector(".theCircle")
let rctOne = svgOne.querySelector(".theRectangle")
circOne.setAttribute('fill', '#ec3')
rctOne.setAttribute('fill', '#e3c')
let circTwo = svgTwo.querySelector(".theCircle")
let rctTwo = svgTwo.querySelector(".theRectangle")
circTwo.setAttribute('fill', 'blue')
rctTwo.setAttribute('fill', 'green')
<svg id="theSVGOne" width="200" height="300" fill="#d55">
<circle class="theCircle" cx="100" cy="75" r="50" stroke="firebrick" fill="#ddd" stroke-width="3" />
<rect class="theRectangle" x="30" y="140" width="110" height="30" stroke="black" fill="#ddd" stroke-width="3" />
</svg>
<svg id="theSVGTwo" width="200" height="200">
<circle class="theCircle" cx="100" cy="75" r="50" stroke="firebrick" fill="#ddd" stroke-width="3" />
<rect class="theRectangle" x="30" y="140" width="110" height="30" stroke="black" fill="#ddd" stroke-width="3" />
</svg>
<div id="status"></div>
Don't use IDs
While technically you can have duplicate ids in the DOM (and make it work), this is very bad practise. Id stands for identifier and should be unique to be able to "tell them apart". You can't do that with identical ids (which theCircle are we talking about?).
Problem is, html won't complain about duplicate ids (no errors are thrown).
To actually get feedback on right or wrong markup you can go to w3's validator and check your html there.
Eg. Error: Duplicate ID theCircle.
Working (but wrong) example using id:
let svgOne = document.getElementById('theSVGOne')
let svgTwo = document.getElementById('theSVGTwo')
let statusPane = document.getElementById('status')
statusPane.innerHTML = 'svgOne => '+svgOne.childElementCount.toString()
let circOne = svgOne.getElementById("theCircle")
let rctOne = svgOne.getElementById("theRectangle")
circOne.setAttribute('fill', '#ec3')
rctOne.setAttribute('fill', '#e3c')
let circTwo = svgTwo.getElementById("theCircle")
let rctTwo = svgTwo.getElementById("theRectangle")
circTwo.setAttribute('fill', 'pink')
rctTwo.setAttribute('fill', 'teal')
<svg id="theSVGOne" width="200" height="300" fill="#d55">
<circle id="theCircle" cx="100" cy="75" r="50" stroke="firebrick" fill="#ddd" stroke-width="3" />
<rect id="theRectangle" x="30" y="140" width="110" height="30" stroke="black" fill="#ddd" stroke-width="3" />
</svg>
<svg id="theSVGTwo" width="200" height="200">
<circle id="theCircle" cx="100" cy="75" r="50" stroke="firebrick" fill="#ddd" stroke-width="3" />
<rect id="theRectangle" x="30" y="140" width="110" height="30" stroke="black" fill="#ddd" stroke-width="3" />
</svg>
<div id="status"></div>

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.

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.

Create a polygon in SVG from a group of elements in JS

I have an SVG with groups of elements (which are all a uniform shape but the group shape can vary. Obviously putting an outline on the group will give me a rectangle shape. What I am trying to achieve is a polygon that outlines the group giving a rough trace of the items:
http://codepen.io/wroughtec/pen/OXvRrq
(below is an example with circles although have also included a path example as that is what I am actually being given)
<h2>No Outline</h2>
<svg viewbox="0 0 1000 200">
<g>
<circle cx="20" cy="20" r="10" fill="grey" />
<circle cx="50" cy="20" r="10" fill="grey" />
<circle cx="50" cy="50" r="10" fill="grey" />
<circle cx="80" cy="50" r="10" fill="grey" />
<circle cx="110" cy="50" r="10" fill="grey" />
<circle cx="140" cy="50" r="10" fill="grey" />
<circle cx="50" cy="80" r="10" fill="grey" />
<circle cx="80" cy="80" r="10" fill="grey" />
<circle cx="110" cy="80" r="10" fill="grey" />
<circle cx="140" cy="80" r="10" fill="grey" />
<circle cx="170" cy="80" r="10" fill="grey" />
<circle cx="200" cy="80" r="10" fill="grey" />
<circle cx="170" cy="110" r="10" fill="grey" />
<circle cx="200" cy="110" r="10" fill="grey" />
</g>
</svg>
I need to be able to do this automatically in JS but getting the x and y of the circles gives me the center point so my manual one I have had to cheat to expand to the outer shape of the circles and remove unnecessary points (i.e. points on the same line or in the middle).
It sounds like what you're looking for is the convex hull of the points.
D3.js implements this, and a good example can be found here. (The code is rather lengthy, so I won't reproduce it here; the main function of interest is d3.geom.hull)
Note that D3.js v4 was just recently released, and almost all code online (including what I linked) is written for v3. There are significant changes between the two, but v4 still has a convex hull function.

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