How do I draw this in html5 canvas?
I can only do the rectangle with round corners part.
<canvas id="myCanvas" width="578" height="200"></canvas>
<script type="text/javascript">
function roundRect(x, y, w, h, radius)
{
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var r = x + w;
var b = y + h;
context.beginPath();
context.strokeStyle="green";
context.lineWidth="4";
context.moveTo(x+radius, y);
context.lineTo(r-radius, y);
context.quadraticCurveTo(r, y, r, y+radius);
context.lineTo(r, y+h-radius);
context.quadraticCurveTo(r, b, r-radius, b);
context.lineTo(x+radius, b);
context.quadraticCurveTo(x, b, x, b-radius);
context.lineTo(x, y+radius);
context.quadraticCurveTo(x, y, x+radius, y);
context.stroke();
}
roundRect(10, 10, 200, 100, 20);
</script>
If you prefer rounded corners there is a easier way: you may use the context method arcTo()For example to draw a rectangle with rounded corners you can write:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
function roundRect(x, y, w, h, r){
var x0 = x, y0 = y;
var x1 = x0+w, y1 = y0;
var x2 = x1, y2 = y0+h;
var x3 = x, y3 = y2;
ctx.strokeStyle = "green";
ctx.lineWidth=4;
ctx.beginPath();
ctx.moveTo(x3,y3-r);
ctx.arcTo(x0,y0,x1,y1,r);
ctx.arcTo(x1,y1,x2,y2,r);
ctx.arcTo(x2,y2,x3,y3,r);
ctx.arcTo(x3,y3,x0,y0,r);
ctx.stroke();
}
roundRect(10, 10, 200, 100, 10);
<canvas id="myCanvas" width="578" height="200"></canvas>
However the image of the 2 rectangles may be done using 2 simple canvas rectangles and an SVG filter like this:
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(10, 10, 100, 100)
ctx.fillRect(100, 100, 30, 30)
canvas{filter: url(#goo);}
<svg width="1" height="1">
<defs>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur" />
<feColorMatrix in="blur" mode="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 40 -28" result="goo" />
</filter>
</defs>
</svg>
<canvas id="c" width="578" height="200"></canvas>
If you are not familiar with The Gooey Effect you may read this article: https://css-tricks.com/gooey-effect/
I hope this helps
Related
I'm trying to build a d3js chart that zooms only on the X-axis but allows panning on both axes. The example below has the effect I desire:
https://jsfiddle.net/xpr364uo/
However, I'm having trouble translating this into my own code. For one, I'm rendering to canvas so I don't have the ability to set the "transform" attribute on some element. Also my zooming uses rescaleX/rescaleY on copies of the scales, as is the "new way" to do zooming via d3-zoom, from what I understand:
const zoomBehavior = zoom().on('zoom', () => {
const xDomain = event.transform.rescaleX(x2).domain();
const yDomain = event.transform.rescaleY(y2).domain();
xScale.domain(xDomain);
yScale.domain(yDomain);
render();
});
This works to zoom/pan on both axes. How can I modify it to get the same affect as in the fiddle? What am I supposed to do with deltaPanY (from the fiddle), in my code?
You could keep track of a second zoom transform (I'll call this yTransform) and use this to rescale the y axis. As you want the x to zoom normally, you can still use d3.event.transform.rescaleX() to rescale on the X axis, while the yTransform can be used to rescale on the Y axis.
When panning, the y translate value of yTransform should be updated with the current zoom state. Conversely, when zooming, yTransform should be used to override the change in the zoom state's y translate.
Perhaps something like:
var yTransform = d3.zoomIdentity; // initial state for the y transform
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform; // zoom state
x2 = t.rescaleX(x); // rescale x as normal (t.y is irrelevant)
// for a pan event, update the y translate
if (d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;
// for a scroll, use the current y translate
else t.y = yTransform.y;
y2 = yTransform.rescaleY(y); // rescale y.
render();
})
The k and x values for yTranslate don't matter: the scale is always 1 as we aren't zooming in, and the x translate is irrelevant to rescale on the y axis. The above doesn't account for double click events, but I'll add that below.
var dots = d3.range(100)
.map(function() {
return {x: Math.random(), y: Math.random()}
})
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();
var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");
// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
g.call(d3.axisRight(y2))
render();
var yTransform = d3.zoomIdentity;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;
else t.y = yTransform.y;
y2 = yTransform.rescaleY(y);
render();
})
canvas.call(zoom);
function render() {
context.clearRect(0,0,500,300);
dots.forEach(function(d) {
context.beginPath();
context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
context.stroke();
})
g.call(d3.axisRight(y2));
}
canvas, svg {
position: absolute;
top: 0;
left: 0;
}
svg {
pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>
As I'm modifying properties directly - which is not the most ideal.
Alternatively, we could track a translate offset on the y (the difference between a y translate with dbl clicks/wheel events and without those events). Both y offset and y translate could be used to create an appropriate zoom transform:
var yOffset = 0;
var lastY = 0;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
lastY = t.y - yOffset;
y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
}
else {
yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
}
render();
})
var dots = d3.range(100)
.map(function() {
return {x: Math.random(), y: Math.random()}
})
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();
var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");
// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
g.call(d3.axisRight(y2))
render();
var yOffset = 0;
var lastY = 0;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
lastY = t.y - yOffset;
y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
}
else {
yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
}
render();
})
canvas.call(zoom);
function render() {
context.clearRect(0,0,500,300);
dots.forEach(function(d) {
context.beginPath();
context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
context.stroke();
})
g.call(d3.axisRight(y2));
}
canvas, svg {
position: absolute;
top: 0;
left: 0;
}
svg {
pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>
I am looking for a feasible solution for this easeljs application of mine I've created. I want to fill color gradually(dynamically) in the parts of this shape with the help of easeljs only so that it will look like I'm filling the fuel tank.
function tankInit() {
var x = 100;
var y = 20;
var width = 120;
var height = 210;
var stage = new createjs.Stage("fuelTank");
stage.scaleX = 1.2;
stage.scaleY = 1.2;
var rectangle1 = new createjs.Shape();
rectangle1.graphics.beginLinearGradientFill(["#00abe5","#005f7f"], [0.2, 0.8], x/8, y/2, x, y/2).drawRect(x, 210 , -height, 15);
stage.addChild(rectangle1);
var ellipse1 = new createjs.Shape();
// ellipse1.graphics.beginStroke("#C0C0C0");
ellipse1.graphics.beginLinearGradientFill(["#939393","#d3d3d3"], [0.8, 0.2], 7*x/4, x, x, x).drawEllipse(x, 55, width, 2*height / 24);
// ellipse1.graphics.endStroke();
stage.addChild(ellipse1);
ellipse1.graphics.moveTo(x, y + height / 8);
ellipse1.graphics.lineTo(x, y + 7 * height / 8);
ellipse1.graphics.moveTo(x, y + height / 8);
ellipse1.graphics.lineTo(x, y + 7 * height / 8);
var ellipse2 = new createjs.Shape();
ellipse2.graphics.beginLinearGradientFill(["#0098cc","#005f7f"], [0.2, 0.8], 7*x/5, y, 7*x/10, x).drawEllipse(x, 210, width, 2*height / 11);
stage.addChild(ellipse2);
var rectangle2 = new createjs.Shape();
rectangle2.graphics.beginLinearGradientFill(["#bdbdbd","#939393"], [0.7, 0.3], 2*x, x, y, 2*y).drawRect(x, 66, width, height / 6);
stage.addChild(rectangle2);
var rectangle3 = new createjs.Shape();
rectangle3.graphics.beginLinearGradientFill(["#005f7f","#0098cc"], [0.7, 0.3], 2*x, 2*y, y, 2*y).drawRect(x, 100, width, 130);
stage.addChild(rectangle3);
var rectangle4 = new createjs.Shape();
/*rectangle4.graphics.beginLinearGradientFill(["#0098cc","#00abe5"], [0.7, 0.3], x/2, y/4, x/4, y/6)*/
/*rectangle4.graphics.beginLinearGradientFill(["#FFFFFF", "#0098cc", "#FFFFFF"], [0.2, 0.6, 0.2], [0, 127, 255], 120 , y, x, y).drawRect(x+width, 95, 122, 15);*/
rectangle4.graphics.beginLinearGradientFill(["rgba(255,255,255,0)", "rgba(0,133,178,1)","rgba(255,198,255,0)"], [0.1,0.7, 0.3], 0, 50, 0, 130).drawRect(x+width, 95, 122, 15);
stage.addChild(rectangle4);
stage.update();
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://code.createjs.com/easeljs-0.8.1.min.js"></script>
<title>Fuel Tank HTML5 Canvas</title>
<head>
<body onload="tankInit();">
<canvas id="fuelTank" width="1000" height="1000" ></canvas>
You can do this by simply animating the height of your "fluid" over time. The easiest way would be by using TweenJS to tween the scaleY property. See this demo for an implementation example:
http://jsfiddle.net/sud05dp5/
I been struggling for hours trying figure how to get the correct rotation center point after I do the following:
1) drag the box to somewhere near the center of the screen
2) hit the test button to rotate box 90 degrees
3) drag the box to another location
4) hit the test button again to rotate another 90 degress(180)
5) The resulting rotation and translation is off. It works fine if I rotate it to 0 degrees and drag the box then hit the test button and it rotates correctly.
Please see my code:
enter code herehttps://jsfiddle.net/1ctqckhL/3/
Its easier if you pick the right group element to put the rotate on. Assuming you want it to rotate around the center of the svg element (i.e. around the cross hairs), you can do it like this...
Key points
transform container2 so (0,0) is on the rotation origin
this means translation and rotation are decoupled
only change rotation in move_path
only change translate in zoomed
use d3.transform to manage transform elements
simplify rotation counter using modulo operator (%)
added offset to initial position of rect to generalise
Working code
var container2;
var last_scale = 1;
var last_rotation = [0, 0, 0];
var last_translate = [0, 0];
var zoom;
w = window.innerWidth;
h = window.innerHeight;
var offsetx = w / 4;
var offsety = h / 4;
d3.select("#container3").attr("transform", function() {
var tr = d3.transform(d3.select(this).attr("transform"));
return (tr.translate = [offsetx, offsety], tr.toString())
});
// document.getElementById("mid_x").style.top = h / 2 + 'px';
// document.getElementById("mid_y").style.left = w / 2 + 'px';
d3.select("#test_btn").style({
top: h - 50 + "px",
left: "10%"
});
zoom = d3.behavior.zoom()
.center([w / 2, h / 2])
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select("#svg_main")
.attr("width", w)
.attr("height", h);
svg.append("path").attr("d", "M" + [w / 2, 0] + "L" + [w / 2, h]).style({
stroke: "red"
})
svg.append("path").attr("d", "M" + [0, h / 2] + "L" + [w, h / 2]).style({
stroke: "red"
});
var container2 = d3.select("#container2")
.attr("transform", "translate(" + zoom.center() + ")")
.call(zoom),
//////////////////////////////////////////////////////////////////////////////////////////////////
//include scaling and fix text just for demo...
container3 = container2.select("#container3").attr("transform", function() {
var tr = d3.transform(d3.select(this).attr("transform"));
return (tr.scale = [h / 955, h / 955], tr.toString())
});
container3.selectAll("text").style("font-size", (12 * 955 / h) + "px").attr("dy", "0.35em");
//////////////////////////////////////////////////////////////////////////////////////////////////
var trans2 = d3.transform(container2.attr("transform"));
container2.select("#container3").attr("transform", function() {
var tr = d3.transform(d3.select(this).attr("transform"));
return (tr.translate = [offsetx - trans2.translate[0], offsety - trans2.translate[1]], tr.toString())
})
d3.selectAll("#test_btn").on("click", move_path);
function move_path() {
last_rotation[0] = (last_rotation[0] += 90) % 360;
container2.attr("transform", function() {
var tr = d3.transform(d3.select(this).attr("transform"));
return (tr.rotate = last_rotation, tr.toString())
});
}
function zoomed() {
d3.select(this).selectAll("g").attr("transform", function() {
var trf = d3.transform(d3.select(this).attr("transform")),
trn = d3.event.translate;
return (trf.translate = [offsetx - trans2.translate[0] + trn[0], offsety - trans2.translate[1] + trn[1]], trf.toString())
})
}
svg {
display: block;
overflow: visible;
background: #9F0;
outline: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div style="width:100%;height:100%;border:1px solid blue;position:absolute;left:0px;top:0px;overflow:hidden;">
<div id="map_div" style="position:relative;left:0px;top:0px;border:0px solid red;width:100%;height:100%;overflow:hidden;">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg_main" style="border:1px solid red;">
<g id="container1">
<g id="container2">zoom and rotate
<g id="container3"> offset, drag and global scale
<rect y="0" x="0" fill="#00AEEF" stroke="#000000" width="300" height="300" />
<text transform="matrix(1 0 0 1 90 8.167)" font-family="'MyriadPro-Regular'" font-size="12">top</text>
</g>
</g>
</g>
</svg>
<!--<div id="mid_x" style="height:1px;background:red;width:100%;position:absolute;top:500px;left:0px;"></div>-->
<!--<div id="mid_y" style="height:100%;background:red;width:1px;position:absolute;top:0px;"></div>-->
</div>
</div>
<div id="map_holder" style="display:none;"></div>
<input id="test_btn" type="button" style="position:absolute;top:500px;left:100px;" value="rotate">
I am trying to calculate the angle of rotation of a circle, I am using the following script:
var circle = new Kinetic.Circle({
x: 256,
y: 256,
radius: 140,
stroke: 'black',
strokeWidth: 4 ,
offset: [0, 0],
draggable: true,
dragBoundFunc: function (pos) {
var pos = stage.getMousePosition();
var xd = 140 - pos.x;
var yd = 140 - pos.y;
var theta = Math.atan2(yd, xd);
var degree = (theta / (Math.PI / 180) - 45);
this.setRotationDeg(degree);
return {
x: this.getAbsolutePosition().x,
y: this.getAbsolutePosition().y
};
}
});
I don't think it is accurate, I added a shape inside the circle to see the rotation but could not group them together, I would appreciate your suggestions on how to calculate the degree of rotation and how to group the shape with the circle so the rotate at the same time. The complete project script is at http://jsfiddle.net/user373721/Ja6GB. Thanks in advance.
Here is how you calculate the angle of the mouse position from "12 o'clock"
Pretend your canvas is a clock centered in the canvas.
Here's how to calculate the angle of the current mouse position assuming 12 o'clock is zero degrees.
function degreesFromTwelveOclock(cx,cy,mouseX,mouseY){
// calculate the angle(theta)
var theta=Math.atan2(mouseY-centerY,mouseX-centerX);
// be sure theta is positive
if(theta<0){theta += 2*Math.PI};
// convert to degrees and rotate so 0 degrees = 12 o'clock
var degrees=(theta*180/Math.PI+90)%360;
return(degrees);
}
Here is complete code and a Fiddle: http://jsfiddle.net/m1erickson/HKq77/
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var centerX=canvas.width/2;
var centerY=canvas.height/2;
var radius=10;
// draw a center dot for user's reference
ctx.beginPath();
ctx.arc(centerX,centerY, radius, 0 , 2 * Math.PI, false);
ctx.fill();
function handleMouseMove(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
$("#movelog").html("Mouse: "+ mouseX + " / " + mouseY);
$("#angle").html("Angle: "+parseInt(degreesFromTwelveOclock(centerX,centerY,mouseX,mouseY)));
}
function degreesFromTwelveOclock(cx,cy,mouseX,mouseY){
// calculate the angle(theta)
var theta=Math.atan2(mouseY-centerY,mouseX-centerX);
// be sure theta is positive
if(theta<0){theta += 2*Math.PI};
// convert to degrees and rotate so 0 degrees = 12 o'clock
var degrees=(theta*180/Math.PI+90)%360;
return(degrees);
}
$("#canvas").mousemove(function(e){handleMouseMove(e);});
}); // end $(function(){});
</script>
</head>
<body>
<p id="movelog">Move</p>
<p id="angle">Out</p>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I have a image which i would like to animate on my web page.
Currently i am using a gif.
http://dev.thepenline.com/TEMP/Hol_10_swingingBell.gif
I would like to have more control on its animation, which js lib/framework would be best to help me out?
Should i use canvas or SVG?
Which (small) library is best to use?
I happen to know a little raphael.js
But i am not sure how to get this effect done.
I've done something with SVG that's close to what you're trying to achieve: http://live.echo-flow.com/stackoverflow/clock.svg
In case I ever take the example down, here's the relevant code.
clock.svg:
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 100 100">
<rect x="0" y="0" height="100" width="100" fill="none" stroke="blue" id="border"/>
<script type="text/ecmascript" xlink:href="clock.js"></script>
</svg>
clock.js:
var svgRoot = document.documentElement;
var svgNS = "http://www.w3.org/2000/svg";
var ident = svgRoot.createSVGMatrix();
function Pendulum(thetaZero,period,tempo,fillColor){
//sensible default values
thetaZero = thetaZero || 0;
period = period || 20;
tempo = tempo || 20;
fillColor = fillColor || "red";
if(fillColor == "randomize"){
fillColor = "rgb(" + Math.random() * 254 +
"," + Math.random() * 254 +
"," + Math.random() * 254 + ")";
}
//construct his representation in DOM
var g = document.createElementNS(svgNS,"g");
g.setAttributeNS(null,"transform","translate(50,20) rotate(" + thetaZero + ")");
var rotateTransform = g.transform.baseVal.getItem(1);
var path = document.createElementNS(svgNS,"line");
path.setAttributeNS(null,"x1",0);
path.setAttributeNS(null,"y1",0);
path.setAttributeNS(null,"x2",0);
path.setAttributeNS(null,"y2",40);
path.setAttributeNS(null,"stroke","black");
var c = document.createElementNS(svgNS,"circle");
var styleString = "fill:" +fillColor;
//console.log(styleString);
c.setAttributeNS(null,"cx",0);
c.setAttributeNS(null,"cy",50);
c.setAttributeNS(null,"r",10);
c.setAttributeNS(null,"style",styleString);
c.setAttributeNS(null,"stroke","black");
c.setAttributeNS(null,"opacity",.5);
g.appendChild(path);
g.appendChild(c);
svgRoot.appendChild(g);
this.node=g;
//timeout
var timeStep = 10; //in milliseconds
var timeCount = 0;
var _animateTimeStep = function(){
timeCount++;
var adjustedTimeCount = timeCount/tempo;
//compute theta
//theta(t) = period * sin(t + thetaZero)
var theta = period * Math.sin(adjustedTimeCount + thetaZero);
//set his current rotate transformation to the new theta
rotateTransform.setMatrix(ident.rotate(theta));
}
var timerId = null;
//method definitions
this.go=function(){
timerId = window.setInterval(_animateTimeStep,timeStep);
};
this.stop=function(){
window.clearInterval(timerId);
};
}
//Pendulum(thetaZero,period,tempo,fillColor)
var p1 = new Pendulum(-10,20,40,"red");
p1.go();
var p2 = new Pendulum(20,20,40,"yellow");
p2.go();
var p3 = new Pendulum(-20,20,40,"blue");
p3.go();
var p4 = new Pendulum(10,20,40,"purple");
p4.go();
With Raphael.js, you could just do:
var img = paper.image("image.png", 10, 10, 80, 80);
(function swingRight(){
img.animate({'rotation': 30}, 1000, '<>', function swingLeft(){
img.animate({'rotation': -30}, 1000, '<>', swingRight);
});
}());
Using canvas
window.onload = function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var width = canvas.width = 500;
var height = canvas.height = 300;
var fixedx = width/2;
var fixedy = 20;
var radius = 150;
var dir = 2;
var minangle = 45;
var maxangle = 135;
var color = rgb();
var x = minangle;
(draw = function() {
var posx = fixedx + radius * Math.cos(x * Math.PI / 180);
var posy = fixedy + radius * Math.sin(x * Math.PI / 180);
with(ctx) {
fillStyle = '#000';
fillRect(0, 0, width, height);
fill();
beginPath();
strokeStyle = '#fff';
moveTo(fixedx, fixedy);
lineWidth = 5;
lineCap = 'round';
lineTo(posx, posy);
stroke();
closePath();
beginPath();
fillStyle = color;
arc(posx, posy, 20, 0, Math.PI * 2, 0);
fill();
closePath();
}
x += 1 * dir;
if(x >= maxangle || x <= minangle) {
dir = -dir;
if(Math.abs(dir) == dir) {
color = rgb();
}
}
setTimeout(draw, 1000/30);
})();
function rgb() {
var clr = 'rgb(';
for(var i =0; i< 3; i++) {
clr += Math.floor(Math.random() * 255) + ',';
}
return clr.replace(/\,$/,')');
}
};
Simple Demo
CodePen
HTML:
<svg id="clock" viewBox="0 0 100 100" width="500" height="500">
<circle id="face" cx="50" cy="50" r="45"/>
<g id="pendulum">
<line id="stick" x1="50" y1="50" x2="50" y2="80"/>
<circle id="end" cx="50" cy="85" r="5"/>
</g>
</svg>
JS:
var amplitude=90,startAngle=90,wtime=700,timeout=40
var animation,start,correction,running=false
var pendulum = document.getElementById("pendulum")
pendulum.setAttribute("transform", "rotate(" + startAngle + ",50,50)")
correction=2*Math.asin(startAngle/amplitude)
start=(new Date()).getTime()
movePendulum()
function movePendulum(){
var angle
var pi=Math.PI,sin=Math.sin,asin=Math.asin,abs=Math.abs
var now=(new Date()).getTime()
var time=now-start
time=time/wtime
time=(time-correction)/2
var easeVal=sin(time)
angle=-amplitude*easeVal
var pendulum = document.getElementById("pendulum")
pendulum.setAttribute("transform", "rotate(" + angle + ",50,50)")
animation=setTimeout(movePendulum,timeout)
}