Scripted SVG animation no-longer working in some browsers - javascript

I am trying to work out why the code below no-longer works in Firefox or Chrome.
The files were last modified over ten years ago.
The static svg dispays, but the script does not run. To my considerable surprise, it does work as it should in Edge, as does a more complicated diagram with interactive elements.
There is probably some obscure setting I need to doctor in Firefox, but I don't know where to look. I don't know when I last tried one of these files, but I would be fairly sure they still worked a couple of years ago.
The code is probably full of daftnesses, as I have done very little javascript, and I probably should now be using requestanimationframe, but that is not the point - it has worked, and still does in Edge.
(Question edited to remove link to irrelevant SMIL version of the animation.)
This is the html file:
<html>
<head>
<title>
SVG slider-crank animated by script
</title>
</head>
<body onload="main()">
<script type="text/javascript">
<!--
var svgdoc = null;
var crank = null;
var crosshead = null;
var conrod = null;
var pi = Math.PI;
function main()
{
var timer = null;
var angle = 0;
var diagram = document.getElementById('svg');
if (diagram && diagram.contentDocument)
{
svgdoc = diagram.contentDocument;
}
else
{
try
{
svgdoc = diagram.getSVGDocument();
}
catch(exception)
{
alert("Unable to get SVG document");
}
}
crank = svgdoc.getElementById('ShowCrank');
crosshead = svgdoc.getElementById('ShowCrosshead');
conrod = svgdoc.getElementById('ShowConRod');
timer = setInterval(function(){(angle = rotation(angle))}, 25);
}
function rotation(angle)
{
var step = 3;
var theta = angle * pi / 180;
var alpha = Math.asin(Math.sin(theta) / 5);
var offset = 100 * (Math.cos(theta) -1) - 500 * (Math.cos(alpha) - 1);
crank.setAttributeNS(null, 'transform', ("rotate(" + angle + ", 800, 300)"));
crosshead.setAttributeNS(null, 'transform', ("translate(" + offset + ", 0)"));
conrod.setAttributeNS(null, 'transform', ("translate(" + offset + ", 0) rotate(" + (alpha * 180 / pi) + ", 400, 300)"));
angle = angle < 360 - step ? angle + step : 0;
return angle;
}
-->
</script>
<object id="svg" type="image/svg+xml" data="Slider_Crank.svg" width="1200" height="800">
<param name="src" value="Slider_Crank.svg">
</object>
</body>
</html>
This is the svg file:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events"
width="1200"
height="800">
<title> Slider-Crank </title>
<defs>
<rect
id="Slidebar"
stroke-width="1"
stroke="black"
fill="silver"
fill-opacity="1"
x="0"
y="-12"
width="300"
height="24"
/>
<g id="Crosshead" stroke-width="1" stroke="black" fill-opacity="1">
<rect
fill="gold"
x="-50"
y="-25"
width="100"
height="50"
/>
<circle cx="0" cy="0" r="15" fill="white"/>
</g>
<g id="Crank" stroke-width="1" stroke="black" fill-opacity="1">
<path fill="silver"
d="M 99.959 40.000
A 40 40 0 0 0 99.959, -40.000
A 450 450 0 0 1 9.950, -49.000
A 50 50 0 1 0 9.950, 49.000
A 450 450 0 0 1 99.959, 40.000
z"/>
<circle cx="100" cy="0" r="25" fill="white"/>
<circle cx="0" cy="0" r="30" fill="lightgrey"/>
</g>
<g id="ConRod" stroke-width="1" stroke="black" fill-opacity="0.7">
<path fill="silver"
d="M 12.387 21.715
A 30 30 0 0 1 27.551 17.776
L 453.475 22.035
A 30 30 0 0 1 473.243 29.733
A 40 40 0 0 1 473.243 -29.733
A 30 30 0 0 1 453.475 -22.035
L 27.551 -17.776
A 30 30 0 0 1 12.387 -21.715
A 25 25 0 0 1 12.387 21.715
z"/>
<circle cx="0" cy="0" r="25" fill="silver"/>
<circle cx="0" cy="0" r="15" fill="white"/>
<circle cx="500" cy="0" r="40" fill="silver"/>
<circle cx="500" cy="0" r="25" fill="white"/>
</g>
</defs>
<use id="ShowTopSlidebar" xlink:href="#Slidebar" x="150" y="263"/>
<use id="ShowBottomSlidebar" xlink:href="#Slidebar" x="150" y="337"/>
<use id="ShowCrosshead" xlink:href="#Crosshead" x="400" y="300"/>
<use id="ShowCrank" xlink:href="#Crank" x="800" y="300"/>
<use id="ShowConRod" xlink:href="#ConRod" x="400" y="300"/>
</svg>

Thanks to Robert Longson: Firefox about:config setting 'security.fileuri.strict_origin_policy'

Related

Make eyes follow cursor in this SVG

I am trying to make the eye pupil of the svg to follow cursor using this tutorial:
https://dev.to/anomaly3108/make-svg-follow-cursor-using-css-and-js-2okp
We have 4 divs:
eyeball_left
eyeball_right
pupil_left
pupil_right
looks like the JS is working, but the angle is not really accurate. the pupils are going too high and they do not stay in the correct position.
let eyeball_left = document.querySelector("#eyeball_left"),
pupil_left = document.querySelector("#pupil_left"),
eyeArea_left = eyeball_left.getBoundingClientRect(),
pupil_leftArea = pupil_left.getBoundingClientRect(),
R_left = eyeArea_left.width / 2,
r_left = pupil_leftArea.width / 2,
centerX_left = eyeArea_left.left + R_left,
centerY_left = eyeArea_left.top + R_left;
console.log(centerX_left)
console.log(centerY_left)
let eyeball_right = document.querySelector("#eyeball_right"),
pupil_right = document.querySelector("#pupil_right"),
eyeArea_right = eyeball_right.getBoundingClientRect(),
pupil_rightArea = pupil_right.getBoundingClientRect(),
R_right = eyeArea_right.width / 2,
r_right = pupil_rightArea.width / 2,
centerX_right = eyeArea_right.left + R_right,
centerY_right = eyeArea_right.top + R_right;
console.log(centerX_right)
console.log(centerY_right)
document.addEventListener("mousemove", (e) => {
let x_left = e.clientX - centerX_left,
y_left = e.clientY - centerY_left,
theta_left = Math.atan2(y_left, x_left),
angle_left = (theta_left * 180) / Math.PI + 360;
let x_right = e.clientX - centerX_right,
y_right = e.clientY - centerY_right,
theta_right = Math.atan2(y_right, x_right),
angle_right = (theta_right * 180) / Math.PI + 360;
pupil_left.style.transform = `translateX(${
R_left - r_left + "px"
}) rotate(${angle_left + "deg"})`;
pupil_left.style.transformOrigin = `${r_left + "px"} center`;
pupil_right.style.transform = `translateX(${
R_right - r_right + "px"
}) rotate(${angle_right + "deg"})`;
pupil_right.style.transformOrigin = `${r_right + "px"} center`;
});
#monster {
height: 100px;
width: 400px;
}
<div id="monster">
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="168.88 0 290.9 400.77">
<g>
<title>Layer 1</title>
<path
id="svg_1"
fill="#6c63ff"
d="m296.30999,388.65991l0,-67.88825l-22,0l0,67.88825a13.98286,13.98286 0 0 0 -7,12.11175l36,0a13.98286,13.98286 0 0 0 -7,-12.11175z"
/>
<path
id="svg_2"
fill="#6c63ff"
d="m355.30999,388.65991l0,-67.88825l-22,0l0,67.88825a13.98286,13.98286 0 0 0 -7,12.11175l36,0a13.98286,13.98286 0 0 0 -7,-12.11175z"
/>
<circle
id="svg_3"
fill="#6c63ff"
r="145.45113"
cy="238.54887"
cx="314.33362"
/>
<ellipse
id="svg_4"
fill="#fff"
ry="19.21053"
rx="57.63158"
cy="311.43609"
cx="314.33362"
/>
<circle
id="svg_5"
fill="#fff"
r="24.69925"
cy="205.61654"
cx="262.19076"
/>
<circle
id="svg_6"
fill="#fff"
r="24.69925"
cy="205.61654"
cx="366.47648"
/>
{/* eyebol */}
<circle
id="eyeball_left"
fill="#3f3d56"
r="19.21053"
cy="205.31579"
cx="262.67948"
/>
<circle
id="eyeball_right"
fill="#3f3d56"
r="19.21053"
cy="205.31579"
cx="366.73212"
/>
{/* eyebol */}
<ellipse
id="svg_9"
fill="#3f3d56"
ry="74.09774"
rx="96.05263"
cy="87.09774"
cx="314.33362"
/>
<ellipse
id="svg_10"
fill="#3f3d56"
ry="18"
rx="38"
cy="18"
cx="314.33362"
/>
<path
id="svg_11"
fill="#3f3d56"
d="m315.39428,259.75517c6.323,-6.40629 16.04713,-6.53419 24.2561,-4.42458c9.786,2.51489 18.116,8.57423 27.17791,12.79851a49.55555,49.55555 0 0 0 14.58024,4.54776a38.27945,38.27945 0 0 0 36.63871,-17.0858a38.7584,38.7584 0 0 0 4.54212,-30.91717a1.50128,1.50128 0 0 0 -2.89283,0.79752a35.70693,35.70693 0 0 1 -3.34417,27.11259a35.29669,35.29669 0 0 1 -35.30417,17.03843a49.62651,49.62651 0 0 1 -14.22886,-4.81212c-8.76148,-4.28973 -16.98465,-10.00419 -26.54935,-12.41745c-9.21411,-2.32481 -19.9481,-1.90083 -26.997,5.241c-1.35753,1.37543 0.76245,3.4981 2.12132,2.12132l-0.00002,-0.00001z"
/>
<path
id="svg_12"
fill="#3f3d56"
d="m315.39428,257.63384c-6.22928,-6.31139 -15.3898,-7.36984 -23.77027,-5.92682c-9.6154,1.65567 -17.88675,6.88869 -26.379,11.36988c-8.6772,4.57879 -17.92825,8.08187 -27.8912,6.48578a35.20905,35.20905 0 0 1 -23.1751,-14.039a35.77653,35.77653 0 0 1 -5.208,-30.05228a1.50128,1.50128 0 0 0 -2.89283,-0.79752a38.80889,38.80889 0 0 0 2.82291,27.89016a37.47231,37.47231 0 0 0 20.97865,18.1838c9.41409,3.348 19.35061,2.63 28.52089,-1.11613c9.42621,-3.85066 17.77515,-10.13661 27.45644,-13.36827c8.93708,-2.98324 20.2603,-3.75844 27.41619,3.49176c1.3583,1.37619 3.47944,-0.7453 2.12132,-2.12132l0,-0.00004z"
/>
<circle
id="svg_13"
fill="#3f3d56"
r="11"
cy="258.5"
cx="314.36371"
/>
{/* PUPIL */}
<circle
id="pupil_left"
fill="#fff"
r="4"
cy="198.77165"
cx="254.31"
/>
<circle
id="pupil_right"
fill="#fff"
r="4"
cy="198.77165"
cx="376.31"
/>
{/* PUPIL */}
</g>
</svg>
The basic idea here, is that I use a line element to decide the rotation/direction of the eye. A line can have a marker in both ends and in the middle. In this example the eye ball is a marker and then I update the end of the line based on the position of the mouse.
First a simple example with outlines and then the full example:
let l1 = document.querySelector("#l1");
let l2 = document.querySelector("#l2");
let svg1 = document.querySelector("#svg1");
const toSVGPoint = (svg, x, y) => {
let p = new DOMPoint(x, y);
return p.matrixTransform(svg.getScreenCTM().inverse());
};
document.addEventListener('mousemove', e => {
let p = toSVGPoint(svg1, e.clientX, e.clientY);
l1.setAttribute('x2', p.x);
l1.setAttribute('y2', p.y);
l2.setAttribute('x2', p.x);
l2.setAttribute('y2', p.y);
});
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 50">
<circle cx="75" cy="25" r="20" fill="none" stroke="blue" />
<circle cx="125" cy="25" r="20" fill="none" stroke="blue" />
<line marker-start="url(#pupil)" id="l1" x1="75" y1="25" stroke="red" />
<line marker-start="url(#pupil)" id="l2" x1="125" y1="25" stroke="red" />
<defs>
<marker id="pupil" viewBox="0 0 10 10" refX="16" refY="5" markerWidth="10"
markerHeight="10" orient="auto-start-reverse">
<rect width="10" height="10" fill="none" stroke="red"/>
<circle fill="none" stroke="green" r="4" cy="5" cx="5" />
</marker>
</defs>
</svg>
let l1 = document.querySelector("#l1");
let l2 = document.querySelector("#l2");
let svg1 = document.querySelector("#svg1");
const toSVGPoint = (svg, x, y) => {
let p = new DOMPoint(x, y);
return p.matrixTransform(svg.getScreenCTM().inverse());
};
document.addEventListener('mousemove', e => {
let p = toSVGPoint(svg1, e.clientX, e.clientY);
l1.setAttribute('x2', p.x);
l1.setAttribute('y2', p.y);
l2.setAttribute('x2', p.x);
l2.setAttribute('y2', p.y);
});
#monster {
height: 100px;
width: 400px;
}
<div id="monster">
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" viewBox="168.88 0 290.9 400.77">
<g>
<title>Layer 1</title>
<path
id="svg_1"
fill="#6c63ff"
d="m296.30999,388.65991l0,-67.88825l-22,0l0,67.88825a13.98286,13.98286 0 0 0 -7,12.11175l36,0a13.98286,13.98286 0 0 0 -7,-12.11175z"
/>
<path
id="svg_2"
fill="#6c63ff"
d="m355.30999,388.65991l0,-67.88825l-22,0l0,67.88825a13.98286,13.98286 0 0 0 -7,12.11175l36,0a13.98286,13.98286 0 0 0 -7,-12.11175z"
/>
<circle
id="svg_3"
fill="#6c63ff"
r="145.45113"
cy="238.54887"
cx="314.33362"
/>
<ellipse
id="svg_4"
fill="#fff"
ry="19.21053"
rx="57.63158"
cy="311.43609"
cx="314.33362"
/>
<circle
id="svg_5"
fill="#fff"
r="24.69925"
cy="205.61654"
cx="262.19076"
/>
<circle
id="svg_6"
fill="#fff"
r="24.69925"
cy="205.61654"
cx="366.47648"
/>
{/* eyebol */}
<circle
id="eyeball_left"
fill="#3f3d56"
r="19.21053"
cy="205.31579"
cx="262.67948"
/>
<circle
id="eyeball_right"
fill="#3f3d56"
r="19.21053"
cy="205.31579"
cx="366.73212"
/>
{/* eyebol */}
<ellipse
id="svg_9"
fill="#3f3d56"
ry="74.09774"
rx="96.05263"
cy="87.09774"
cx="314.33362"
/>
<ellipse
id="svg_10"
fill="#3f3d56"
ry="18"
rx="38"
cy="18"
cx="314.33362"
/>
<path
id="svg_11"
fill="#3f3d56"
d="m315.39428,259.75517c6.323,-6.40629 16.04713,-6.53419 24.2561,-4.42458c9.786,2.51489 18.116,8.57423 27.17791,12.79851a49.55555,49.55555 0 0 0 14.58024,4.54776a38.27945,38.27945 0 0 0 36.63871,-17.0858a38.7584,38.7584 0 0 0 4.54212,-30.91717a1.50128,1.50128 0 0 0 -2.89283,0.79752a35.70693,35.70693 0 0 1 -3.34417,27.11259a35.29669,35.29669 0 0 1 -35.30417,17.03843a49.62651,49.62651 0 0 1 -14.22886,-4.81212c-8.76148,-4.28973 -16.98465,-10.00419 -26.54935,-12.41745c-9.21411,-2.32481 -19.9481,-1.90083 -26.997,5.241c-1.35753,1.37543 0.76245,3.4981 2.12132,2.12132l-0.00002,-0.00001z"
/>
<path
id="svg_12"
fill="#3f3d56"
d="m315.39428,257.63384c-6.22928,-6.31139 -15.3898,-7.36984 -23.77027,-5.92682c-9.6154,1.65567 -17.88675,6.88869 -26.379,11.36988c-8.6772,4.57879 -17.92825,8.08187 -27.8912,6.48578a35.20905,35.20905 0 0 1 -23.1751,-14.039a35.77653,35.77653 0 0 1 -5.208,-30.05228a1.50128,1.50128 0 0 0 -2.89283,-0.79752a38.80889,38.80889 0 0 0 2.82291,27.89016a37.47231,37.47231 0 0 0 20.97865,18.1838c9.41409,3.348 19.35061,2.63 28.52089,-1.11613c9.42621,-3.85066 17.77515,-10.13661 27.45644,-13.36827c8.93708,-2.98324 20.2603,-3.75844 27.41619,3.49176c1.3583,1.37619 3.47944,-0.7453 2.12132,-2.12132l0,-0.00004z"
/>
<circle
id="svg_13"
fill="#3f3d56"
r="11"
cy="258.5"
cx="314.36371"
/>
</g>
{/* PUPIL */}
<line marker-start="url(#pupil)" id="l1" x1="262.67948" y1="205.31579" stroke="none" />
<line marker-start="url(#pupil)" id="l2" x1="366.73212" y1="205.31579" stroke="none" />
{/* PUPIL */}
<defs>
<marker id="pupil" viewBox="0 0 10 10" refX="16" refY="5" markerWidth="10"
markerHeight="10" orient="auto-start-reverse">
<circle fill="#fff" r="4" cy="5" cx="5" />
</marker>
</defs>
</svg>
</div>
Alternative: update <circle> cx and cy attributes
This approach requires to calculate
the angle between cursor coordinates and the eyeball's center
the new point position on the circle (based on angle, radius
Demo example
const svg = document.getElementById('svg')
document.addEventListener("mousemove", (e) => {
movePupils(e);
});
function movePupils(e) {
let eyes = svg.querySelectorAll('.eye');
eyes.forEach(eye=>{
let eyeball = eye.querySelector('.eyeball');
let pupil = eye.querySelector('.pupil');
// get center cx/cy and radius
let pCenter = {x: +eyeball.getAttribute('cx'), y:+eyeball.getAttribute('cy') };
let rEyeball = +eyeball.getAttribute('r');
let rPupil = +pupil.getAttribute('r');
// translate cursor HTML DOM coordinates to SVG DOM units
let pCursor = new DOMPoint(e.clientX, e.clientY);
pCursor = pCursor.matrixTransform(svg.getScreenCTM().inverse());
// get angle between cursor and eyeball center;
let angle = (Math.atan2(pCursor.y - pCenter.y, pCursor.x - pCenter.x) * 180) / Math.PI;
//get distance between cursor and eyeball center
let a = pCursor.x - pCenter.x;
let b = pCursor.y - pCenter.y;
let distance = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
// adjust pupil movement inside eyeball boundaries
let offset = distance<rEyeball ? 1/rEyeball*distance : 1;
let radiusOuter = (rEyeball-rPupil)*offset;
let pMoved = {
x: pCenter.x + Math.cos((angle * Math.PI) / 180) * radiusOuter,
y: pCenter.y + Math.sin((angle * Math.PI) / 180) * radiusOuter
}
// update attributes
pupil.setAttribute('cx', pMoved.x)
pupil.setAttribute('cy', pMoved.y)
})
}
body{
margin:5em;
}
svg{
width:20em;
overflow:visible;
border:1px solid #ccc;
}
<svg id="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 100">
<g class="eye">
<circle class="eyeball" cx="30" cy="50" r="25" fill="none" stroke="#000" />
<circle class="pupil" cx="30" cy="50" r="5" fill="#000" />
</g>
<g class="eye">
<circle class="eyeball" cx="75" cy="25" r="20" fill="none" stroke="#000" />
<circle class="pupil" cx="75" cy="25" r="5" fill="#000" />
</g>
<g class="eye">
<circle class="eyeball" cx="120" cy="50" r="25" fill="none" stroke="#000" />
<circle class="pupil" cx="120" cy="50" r="5" fill="#000" />
</g>
<g class="eye">
<circle class="eyeball" cx="75" cy="75" r="20" fill="none" stroke="#000" />
<circle class="pupil" cx="75" cy="75" r="5" fill="#000" />
</g>
</svg>
The above script can be applied by wrapping all eyeballs and pupils in a group with a class "eye" like so :
<g class="eye">
<circle class="eyeball" cx="120" cy="50" r="25" />
<circle class="pupil" cx="120" cy="50" r="5" />
</g>
Like in #chrwahl's example we need to convert HTML DOM coordinates to SVG user units.
let pCursor = new DOMPoint(e.clientX, e.clientY);
pCursor = pCursor.matrixTransform(svg.getScreenCTM().inverse());
Calculate angles
let pCenter = {x: +eyeball.getAttribute('cx'), y:+eyeball.getAttribute('cy') };
let angle = (Math.atan2(pCursor.y - pCenter.y, pCursor.x - pCenter.x) * 180) / Math.PI;
Fine tune pupil positioning within eyeball area
Calculating the distance between cursor and eyeball center, allows us to further adjust the pupil movement: If the cursor is within the eyeball, the pupil will be centered around the current mouse coordinates.
let a = pCursor.x - pCenter.x;
let b = pCursor.y - pCenter.y;
let distance = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
let offset = distance<rEyeball ? 1/rEyeball*distance : 1;
let radiusOuter = (rEyeball-rPupil)*offset;
Point on circle
let pOnCircle = {
x: pCenter.x + Math.cos((angle * Math.PI) / 180) * radiusOuter,
y: pCenter.y + Math.sin((angle * Math.PI) / 180) * radiusOuter
}
Sinc we want the circle to be placed within the eyball's boundaries we need to use a decreased radius for this calculation (according to the pupil's radius).

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.

Moving image on scroll through svg path

I want to move object through svg path on scroll=) I was trying to add parts of path on scroll into path, but it still doesn't work. Help!!!=)
https://jsfiddle.net/YuriiBielozertsev/Ltx9ed0L/
<?xml version="1.0"?>
<svg viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Draw the outline of the motion path in grey, along with 2 small circles at key points -->
<path d="M10,110 A120,120 -45 0,1 110 10 A120,120 -45 0,1 10,110" stroke="green" stroke-width="2" fill="none" id="theMotionPath"/>
<circle cx="10" cy="110" r="3" fill="#000"/>
<circle cx="110" cy="10" r="3" fill="#000"/>
<!-- Red circle which will be moved along the motion path. -->
<circle cx="0" cy="" r="5" fill="red">
<!-- Define the motion path animation -->
<animateMotion dur="6s" repeatCount="indefinite">
<mpath xlink:href="#theMotionPath"/>
</animateMotion>
</circle>
</svg>
Something like this?
How this works:
When we get a scroll event we:
Calculate how far down the page we are
Convert this percentage to a position on the path using the <path> element functions getTotalLength() and getPointAtLength().
Reposition the dot so that it appears at this point.
function positionTheDot() {
// What percentage down the page are we?
var scrollPercentage = (document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
// Get path length
var path = document.getElementById("theMotionPath");
var pathLen = path.getTotalLength();
// Get the position of a point at <scrollPercentage> along the path.
var pt = path.getPointAtLength(scrollPercentage * pathLen);
// Position the red dot at this point
var dot = document.getElementById("dot");
dot.setAttribute("transform", "translate("+ pt.x + "," + pt.y + ")");
};
// Update dot position when we get a scroll event.
window.addEventListener("scroll", positionTheDot);
// Set the initial position of the dot.
positionTheDot();
.verylong {
height: 2000px;
}
svg {
position: fixed;
width: 200px;
height: 200px;
}
<svg viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Draw the outline of the motion path in grey, along with 2 small circles at key points -->
<path d="M10,110 A120,120 -45 0,1 110 10 A120,120 -45 0,1 10,110" stroke="green" stroke-width="2" fill="none" id="theMotionPath"/>
<circle cx="10" cy="110" r="3" fill="#000"/>
<circle cx="110" cy="10" r="3" fill="#000"/>
<!-- Red circle which will be moved along the motion path. -->
<circle cx="0" cy="0" r="5" fill="red" id="dot"/>
</svg>
<div class="verylong">
</div>

Scaling after rotation changes the position of rect element

Original rect, with a rotation:
<rect id="location_1" x="40" y="40" height="100" width="100" fill="red" stroke="2" stroke-width="2" transform="rotate(45, 90, 90)"></rect>
After scaling, the rotation center is changed based on the new bbox:
<rect id="location_1" x="40" y="40" height="200" width="200" fill="red" stroke="2" stroke-width="2" transform="rotate(45, 140, 140)"></rect>
After scaling of the rect, if we update the rotation center then the postion of the rect is changed. But if we do not update the center then it works as expected, till the other transformations are applied.
Scale as in updating width and height, not the scale transform
My question is how to scale keeping the position of the element same.?
JSFiddle with the situation
If clicked on Scale - No Update the position is not changed but when clicked on Scale - Update the position changes.
function scaleAndUpdate(id, update) {
var
elem = document.getElementById(id),
bbox, cx, cy;
elem.setAttribute('width', 200);
elem.setAttribute('height', 200);
if (update) {
bbox = elem.getBBox();
cx = bbox.x + (bbox.width / 2);
cy = bbox.y + (bbox.height / 2);
elem.setAttribute('transform', 'rotate(45, ' + cx + ', ' + cy + ')');
}
}
function reset(id) {
var
elem = document.getElementById(id);
elem.setAttribute('x', 40);
elem.setAttribute('y', 40);
elem.setAttribute('width', 100);
elem.setAttribute('height', 100);
elem.setAttribute('transform', 'rotate(45, 90, 90)');
}
<div>
<svg x="0" y="0" width="300" height="300" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="smallGrid" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 10 0 L 0 0 0 10" fill="none" stroke="gray" stroke-width="0.5" />
</pattern>
<pattern id="grid" width="100" height="100" patternUnits="userSpaceOnUse">
<rect width="100" height="100" fill="url(#smallGrid)" />
<path d="M 100 0 L 0 0 0 100" fill="none" stroke="gray" stroke-width="1" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" style="pointer-events: none;"></rect>
<g id="locationGroup">
<rect id="location_1" x="40" y="40" height="100" width="100" fill="red" stroke="2" stroke-width="2" transform="rotate(45, 90, 90)"></rect>
<circle cx="90" cy="20" r="5" fill="black"></circle>
</g>
</svg>
<button onclick="scaleAndUpdate('location_1')">Scale - No Update</button>
<button onclick="scaleAndUpdate('location_1', true)">Scale - Update</button>
<button onclick="reset('location_1')">Reset</button>
</div>

jQuery-added svg elements do not show up

Sorry if this has already been answered, I am new to SO.
I am trying to create svg elements using jquery, and I have this code as part of an HTML page:
<svg viewBox="0 0 1000 500">
<defs>
<clipPath id="clip">
<ellipse cx="100" cy="250" rx="200" ry="50" />
</clipPath>
</defs>
<g>
<path d="M 0,0 L 1000,0 1000,500 0,500"
fill="#9ADEFF" />
<path id="boat" stroke="none" fill="red"
d="M 100,175 L 300,175 300,325 100,325"
clip-path="url(#clip)" />
</g>
<g id="0002" width="100" height="100%"
transform="translate(1000)">
<line x1="50" y1="0" x2="50" y2="300"
stroke="green" stroke-width="100" />
</g>
</svg>
and this Javascript (with jQuery 1.9):
var id = 10000,
coinArray = []
function generateNextLine(type) {
$('svg').append($(type()))
return $('svg')[0]
}
function idNo() {
id++
return ((id-1)+"").substr(-4)
}
function random(x,y) {
if (!y) {
y=x
x=0
}
x=parseInt(x)
y=parseInt(y)
return (Math.floor(Math.random()*(y-x+1))+x)
}
function coins() {
coinArray[id%10000]=[]
var gID = idNo(), x,
g=$(document.createElement('g')).attr({
id: gID,
width: "100",
height: "100%"
})
while (3<=random(10)) {
var randomPos=random(50,450)
coinArray[(id-1)%10000][x] = randomPos
$(g).append(
$(document.createElement('circle'))
.attr({
cx: "50",
cy: randomPos,
r: "50",
fill: "yellow"
})
)
x++
}
return $(g)[0]
}
When I run generateNextLine(coins);, the svg adds this element:
<g id="0000" width="100" height="100%">
<circle cx="50" cy="90" r="50" fill="yellow"></circle>
</g>
However, the actual display of the svg doesn't change. If I add this code directly to the svg, it renders as I would expect, but running my javascript function does not seem to do anything to the display. I am using Chrome 28, on OS X Lion.
You must create SVG elements in the SVG namespace which means you can't do
document.createElement('g')
but in instead you must write
document.createElementNS('http://www.w3.org/2000/svg', 'g')
same for circle etc.

Categories

Resources