<html>
<head>
<meta name="description" content="http://i.imgur.com/4ibsGlI.gif" />
<meta charset=utf-8 />
<title>JS Bin</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<canvas id="canvas" width="450px" height="450px"></canvas>
<script type="text/javascript" src="rad.js"></script>
</body>
</html>
/*
solution for:
http://i.imgur.com/4ibsGlI.gif
by @msvaljek
*/
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
cwidth = canvas.width,
cheight = canvas.height;
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ) {
window.setTimeout(callback, 1000 / 60);
};
})();
var PiRad = function () {
this.state = 'begin';
this.circleColor = 'rgb(31, 31, 255)';
this.crossColor = 'rgb(235, 235, 235)';
this.markerColor = 'rgb(255, 0, 0)';
this.textColor = 'rgb(255, 208, 22)';
this.innerCircleFill = 'rgb(255, 242, 204)';
this.innerCircleStroke = 'rgb(255, 203, 47)';
};
PiRad.prototype.changeState = function (newState, timeout) {
var that = this;
(function() {
if (newState !== this.nextState) {
setTimeout(function() {that.state = newState;}, timeout);
this.nextState = newState;
}
})();
};
PiRad.prototype.draw = function () {
var that = this;
ctx.lineWidth = 1;
ctx.strokeStyle = this.crossColor;
ctx.beginPath();
ctx.moveTo(25, cheight / 2);
ctx.lineTo(cwidth - 25, cheight / 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(cwidth / 2, 25);
ctx.lineTo(cwidth / 2, cheight - 25);
ctx.stroke();
this.drawSmallCircle();
if (this.state === 'begin') {
this.nextState = '';
this.smallCircleColor = 'red';
this.movingDot = {
x: cwidth / 2,
y: cheight / 2,
dx: 5,
a: 0,
da: 0.2
};
this.movingCenter = {
x: cwidth / 2,
y: cheight / 2,
a: Math.PI,
da: 0.1
};
this.r = 150;
this.innerR = 40;
this.textOut = {
x: 200,
y: 270
};
this.marker = {
a: -1,
b: 0,
s: 0.05
};
this.lines = [];
this.circleMarks = [];
that.state = 'idle';
this.changeState('moveRedDotOut', 1000);
} else if (this.state === 'moveRedDotOut') {
this.drawRedDotWithConnectionToCenter();
if ((this.movingDot.x - cwidth / 2) >= this.r) {
this.movingDot.x = cwidth / 2 + this.r;
this.changeState('moveRedDotAround', 2000);
} else {
this.movingDot.x += this.movingDot.dx;
}
} else if (this.state === 'moveRedDotAround') {
if (this.movingDot.a - this.movingDot.da > Math.PI * -2) {
this.movingDot.a -= this.movingDot.da;
} else {
this.movingDot.a = -2 * Math.PI;
this.changeState('drawRAndStartWait', 1000);
}
this.movingDot.x = cwidth / 2 + Math.cos(this.movingDot.a) * this.r;
this.movingDot.y = cheight / 2 + Math.sin(this.movingDot.a) * this.r;
this.drawCircleToAngle(this.movingDot.a);
this.drawRedDotWithConnectionToCenter();
} else if (this.state === 'drawRAndStartWait') {
this.drawR();
this.changeState('hideR', 3000);
} else if (this.state === 'hideR') {
this.drawCircleToAngle(this.movingDot.a);
this.drawRedDotWithConnectionToCenter();
this.changeState('moveRedLineUp', 2000);
} else if (this.state === 'moveRedLineUp') {
this.smallCircleColor = 'black';
if (this.movingCenter.a + this.movingCenter.da < 3 * Math.PI / 2) {
this.movingCenter.a += this.movingCenter.da;
} else {
this.movingCenter.a = 3 * Math.PI / 2;
this.changeState('stickRedLineToCircle', 3000);
}
this.movingCenter.x = this.movingDot.x + Math.cos(this.movingCenter.a) * this.r;
this.movingCenter.y = this.movingDot.y + Math.sin(this.movingCenter.a) * this.r;
this.drawCircleToAngle(this.movingDot.a);
this.drawRedDotWithMovingCenter();
} else if (this.state === 'stickRedLineToCircle') {
var endx = cwidth / 2 + Math.cos(-1) * this.r;
var endy = cheight / 2 + Math.sin(-1) * this.r;
if (endx < this.movingCenter.x) {
this.movingCenter.x -= 2;
} else {
this.movingCenter.x = endx;
}
if (endy > this.movingCenter.y) {
this.movingCenter.y += 0.7;
} else {
this.movingCenter.y = endy;
}
if (this.movingCenter.x == endx && this.movingCenter.y == endy) {
this.changeState('markFirstSegment', 2000);
}
this.drawCircleToAngle(this.movingDot.a);
this.drawRedDotWithMovingCenterCurved();
} else if (this.state === 'markFirstSegment') {
this.drawMarkedSegments();
this.changeState('outputOneRad', 2000);
} else if (this.state === 'outputOneRad') {
this.drawSomeText('1 rad');
this.drawMarkedSegments();
this.changeState('startMarking', 2000);
} else if (this.state === 'startMarking') {
this.circleMarks[0] = 0;
this.lines[0] = -1;
if (this.marker.a > -2) {
this.marker.a -= this.marker.s;
this.marker.b = this.marker.a + 1;
} else {
this.marker.a = -2;
this.marker.b = -1;
this.state = 'mark2Rad';
}
this.drawSomeText('1 rad');
this.drawMarkedSegments();
} else if (this.state === 'mark2Rad') {
this.drawSomeText('2 rad');
this.drawMarkedSegments();
this.changeState('startMarking3', 2000);
} else if (this.state === 'startMarking3') {
this.circleMarks[1] = -1;
this.lines[1] = -2;
if (this.marker.a > -3) {
this.marker.a -= this.marker.s;
this.marker.b = this.marker.a + 1;
} else {
this.marker.a = -3;
this.marker.b = -2;
this.state = 'mark3Rad';
}
this.drawSomeText('2 rad');
this.drawMarkedSegments();
} else if (this.state === 'mark3Rad') {
this.drawSomeText('3 rad');
this.drawMarkedSegments();
this.changeState('startMarkingPi', 2000);
} else if (this.state === 'startMarkingPi') {
this.circleMarks[2] = -2;
this.lines[2] = -3;
if (this.marker.a > -Math.PI) {
this.marker.a -= this.marker.s / 8;
} else {
this.marker.a = -Math.PI;
}
if (this.marker.b > -3) {
this.marker.b -= this.marker.s / 2;
} else {
this.marker.b = -3;
}
if (this.marker.a === -Math.PI && this.marker.b === -3) {
this.state = 'markPi';
} else {
this.drawSomeText('3 rad');
}
this.drawMarkedSegments();
} else if (this.state === 'markPi') {
this.drawSomeText('π rad');
this.drawMarkedSegments();
this.changeState('removePiMarkings', 2000);
} else if (this.state === 'removePiMarkings') {
this.circleMarks.length = 0;
this.circleMarks[0] = 0;
this.circleMarks[1] = Math.PI;
this.lines.length = 0;
this.marker.b = this.marker.a;
this.drawSomeText('π rad');
this.drawMarkedSegments({hideRedMarkers: true});
this.changeState('movePiTextUp', 2000);
} else if (this.state === 'movePiTextUp') {
if (this.textOut.y > 170) {
this.textOut.y -= 5;
}
this.drawMarkedSegments({hideRedMarkers: true});
this.drawSomeText('π rad');
this.changeState('moveToTwoPi', 2000);
} else if (this.state === 'moveToTwoPi') {
if (this.marker.a > -2 * Math.PI) {
this.marker.a -= this.marker.s;
} else {
this.marker.a = -2 * Math.PI;
this.textOut.x -= 25;
this.state = 'mark2Pi';
}
this.circleMarks[2] = this.marker.a;
this.marker.b = this.marker.a;
this.drawMarkedSegments({hideRedMarkers: true});
this.drawSomeText('π rad');
} else if (this.state === 'mark2Pi') {
this.circleMarks.length = 0;
this.circleMarks[0] = 0;
this.drawSomeText('2π rad');
this.drawMarkedSegments({hideRedMarkers: true});
this.changeState('displayFinal', 2000);
} else if (this.state === 'displayFinal') {
this.circleMarks.length = 0;
this.drawSomeText('2π rad');
this.drawMarkedSegments({hideRedMarkers: true, hideInnerCircle: true});
this.changeState('begin', 4000);
}
};
PiRad.prototype.drawRedDotWithConnectionToCenter = function () {
ctx.beginPath();
ctx.fillStyle = this.smallCircleColor;
ctx.arc(this.movingDot.x, this.movingDot.y, 4, 0, 2 * Math.PI);
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = this.markerColor;
ctx.beginPath();
ctx.moveTo(cwidth / 2, cheight / 2);
ctx.lineTo(this.movingDot.x, this.movingDot.y);
ctx.stroke();
};
PiRad.prototype.drawRedDotWithMovingCenter = function () {
ctx.strokeStyle = ctx.fillStyle = this.markerColor;
ctx.beginPath();
ctx.arc(this.movingDot.x, this.movingDot.y, 4, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(this.movingCenter.x, this.movingCenter.y, 4, 0, 2 * Math.PI);
ctx.fill();
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(this.movingCenter.x, this.movingCenter.y);
ctx.lineTo(this.movingDot.x, this.movingDot.y);
ctx.stroke();
};
PiRad.prototype.drawRedDotWithMovingCenterCurved = function () {
ctx.strokeStyle = ctx.fillStyle = this.markerColor;
ctx.beginPath();
ctx.arc(this.movingDot.x, this.movingDot.y, 4, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(this.movingCenter.x, this.movingCenter.y, 4, 0, 2 * Math.PI);
ctx.fill();
var endx = ~~(cwidth / 2 + Math.cos(-1) * this.r);
var endy = ~~(cheight / 2 + Math.sin(-1) * this.r);
var distance = ~~(Math.sqrt(Math.pow(endx - this.movingCenter.x, 2) + Math.pow(endy - this.movingCenter.y, 2)));
var factor = 1.12 + distance / 400;
var x = ~~(cwidth / 2 + Math.cos(-0.5) * this.r * factor);
var y = ~~(cheight / 2 + Math.sin(-0.5) * this.r * factor);
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(this.movingDot.x, this.movingDot.y);
ctx.quadraticCurveTo(x, y, this.movingCenter.x, this.movingCenter.y);
ctx.stroke();
};
PiRad.prototype.drawR = function () {
this.drawCircleToAngle(this.movingDot.a);
this.drawRedDotWithConnectionToCenter();
ctx.fillStyle = this.markerColor;
ctx.font = '60px arial italic';
ctx.fillText('r', 290, 210);
};
PiRad.prototype.drawSomeText = function (text) {
ctx.fillStyle = this.textColor;
ctx.font = '40px arial italic bold';
ctx.fillText(text, this.textOut.x, this.textOut.y);
};
PiRad.prototype.drawCircleToAngle = function (angle) {
ctx.beginPath();
ctx.strokeStyle = this.circleColor;
ctx.lineWidth = 1;
ctx.arc(cwidth / 2, cheight / 2, this.r, 0, angle, true);
ctx.stroke();
};
PiRad.prototype.drawInnerCircleToAngle = function (angle) {
ctx.strokeStyle = this.innerCircleStroke;
ctx.fillStyle = this.innerCircleFill;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(cwidth / 2, cheight / 2);
ctx.arc(cwidth / 2, cheight / 2, this.innerR, 0, angle, true);
ctx.closePath();
ctx.fill();
ctx.stroke();
};
PiRad.prototype.drawSmallCircle = function () {
ctx.beginPath();
ctx.fillStyle = this.smallCircleColor;
ctx.arc(cwidth / 2, cheight / 2, 4, 0, 2 * Math.PI);
ctx.fill();
};
PiRad.prototype.drawMarkedSegments = function (config) {
if (!config || !config.hideInnerCircle) {
this.drawCircleToAngle(2 * Math.PI);
this.drawInnerCircleToAngle(this.marker.a);
}
ctx.strokeStyle = this.innerCircleStroke;
for (var i = 0; i < this.lines.length; i++) {
ctx.lineWidth = 0.5;
ctx.beginPath();
ctx.moveTo(cwidth / 2, cheight / 2);
ctx.lineTo(cwidth / 2 + Math.cos(this.lines[i]) * this.r, cheight / 2 + Math.sin(this.lines[i]) * this.r);
ctx.stroke();
}
for (var j = 0; j < this.circleMarks.length; j++) {
ctx.fillStyle = this.innerCircleStroke;
ctx.beginPath();
ctx.arc(cwidth / 2 + Math.cos(this.circleMarks[j]) * this.r, cheight / 2 + Math.sin(this.circleMarks[j]) * this.r, 4, 0, 2 * Math.PI);
ctx.fill();
}
var bx = cwidth / 2 + Math.cos(this.marker.b) * this.r,
by = cheight / 2 + Math.sin(this.marker.b) * this.r,
ax = cwidth / 2 + Math.cos(this.marker.a) * this.r,
ay = cheight / 2 + Math.sin(this.marker.a) * this.r;
if (!config || !config.hideInnerCircle) {
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(cwidth / 2, cheight / 2);
ctx.lineTo(cwidth / 2 + this.r, cheight / 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(cwidth / 2, cheight / 2);
ctx.lineTo(ax, ay);
ctx.stroke();
this.drawSmallCircle();
} else {
ctx.lineWidth = 2;
}
ctx.strokeStyle = this.innerCircleStroke;
ctx.beginPath();
ctx.arc(cwidth / 2, cheight / 2, this.r, 0, this.marker.b, true);
ctx.stroke();
if (!config || !config.hideRedMarkers) {
ctx.lineWidth = 2;
ctx.strokeStyle = ctx.fillStyle = this.markerColor;
ctx.beginPath();
ctx.arc(ax, ay, 4, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(bx, by, 4, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(cwidth / 2, cheight / 2, this.r, this.marker.a, this.marker.b, false);
ctx.stroke();
}
};
var piRad = new PiRad();
(function animloop() {
requestAnimFrame(animloop);
ctx.clearRect(0, 0, cwidth, cheight);
piRad.draw();
})();
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |