Animated circle using canvas with numbers counting up - animation

The following jsFiddle http://jsfiddle.net/QsMVn/6/ has animated circles and the percentage showing how much of the circle has been filled. My aim is to have the percentages animated as well so that they move along with the line right next to the end of it. I can't figure out how to do that.
Code of jsFiddle:
// requestAnimationFrame Shim
(function() {
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var endPercent = 85;
var curPerc = 0;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 10;
context.strokeStyle = '#ad2323';
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 10;
context.shadowColor = '#656565';
function animate(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
animate(curPerc / 100);
});
}
}
animate();

You just use the angle you have (in radians) and calculate a distance based on that.
Prerequisites: Change a couple of lines above so you can reuse the radians:
var radians = (degrees - 90) * Math.PI / 180; // subtract 90 here
...
ctx.arc(W / 2, H / 2, W / 3, 0 - 90 * Math.PI / 180, radians, false);
Then use textAlign and textBaseline to center the text:
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
Calculate a position, demo shows text on the inside - for outside (or in the middle of arc) just adjust the dist value:
var dist = W / 3 - 40;
var tx = W * 0.5 + dist * Math.cos(radians);
var ty = H * 0.5 + dist * Math.sin(radians);
ctx.fillText(text, tx, ty);
Modified fiddle here
Hope this helps!

Related

img object width and height ignored when drawing on canvas

I'm changing width and height of an image object before drawing on canvas:
earth.onload = function () {
this.width = 50;
this.height = 50;
}
earth.src = 'images/earth-transparent.png';
// and later on
function drawPlanet(xCenter, yCenter, radius, speed, img) {
var ms = time.getSeconds() * 1000 + time.getMilliseconds();
var angle = ((2 * Math.PI) / (speed * 1000) * ms);
var xDelta = radius * Math.sin(angle);
var yDelta = radius * Math.cos(angle);
var x = xCenter + xDelta;
var y = yCenter + yDelta;
ctx.drawImage(img, x - img.width / 2, y - img.height / 2, img.width, img.height);
}
var time = new Date();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'rgba(0,153,255,0.4)';
ctx.save();
drawPlanet(300, 300, 200, 6, earth);
(drawPlanet is called in the callback of a setInterval, thus the load event of the image has fired) Unfortunately the size of the image being drawn is the original one. When I output width and height in the debugger their value is 50. Why is this?

Change duration of canvas animation with Request Animation Frame

I'm trying to animate a circle that will draw itself similar to a progress bar. I'm intending to use it on a carousel to track when the next slide is coming up. The problem I'm having is I don't know how to change the duration of the animation. I tried adjusting the framerate, and it works but the animation gets really choppy. setInterval kind of works but it displays the entire circle rather than just a portion of it like I'm intending, so I can't time things properly. I need to be able to control the speed of the animation, slowing it down without it being stuttery. The code I'm working on is below.
<script>
(function() {
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 90;
var endPercent = 85;
var curPerc = 0;
var circ = -Math.PI;
var quart = -(Math.PI * 2) + 1;
function animate(current) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(centerX, centerY, radius, -(quart), ((circ) * current) - quart, true);
context.lineWidth = 3;
context.strokeStyle = '#000';
context.stroke();
curPerc++;
if (curPerc < endPercent) {
requestAnimationFrame(function () {
animate(curPerc / 100)
});
}
}
animate();
</script>
requestAnimationFrame does pass an high resolution timestamp in the callback argument. So you could use it to determine where you are in your current animation, and use this delta time to set your positions variables instead of curPerc++.
Here is a naive implementation.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 90;
var endPercent = 85;
var quart = -(Math.PI * 2) + 1;
var startTime = null;
var duration = null;
function animate(time) {
if (!startTime) {
startTime = time;
}
var delta = Math.min(1, (time - startTime) / duration);
var curPerc = ((-2 * Math.PI) / 100) * (endPercent * delta);
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(centerX, centerY, radius, -quart, curPerc - quart, true);
context.stroke();
if (delta < 1) {
requestAnimationFrame(animate);
} else {
startTime = null;
slider.disabled = false;
}
}
var startAnim = function() {
context.lineWidth = 3;
context.strokeStyle = '#000';
slider.disabled = true;
duration = +slider.value;
l.textContent = duration + 'ms';
requestAnimationFrame(animate);
};
slider.onchange = startAnim;
startAnim();
<p>use the slider to update the animation's duration</p>
<input type="range" min="250" max="9000" value="2000"id="slider" />
<label id="l"></label><br>
<canvas id="myCanvas" height="300"></canvas>

Three.js camera dome rotation

I want to recreate this rotation effect on mouse move in Three.js. What should I use to accomplish this? How to recreate this effect maximally similar?
So, instead of rotating the camera I figured, that I can get the needed effect if i just rotate all my scene objects. So I did the following:
canvas.on("mousemove", function(event) {
var canvasWidth = canvas.outerWidth();
var canvasHeight = canvas.outerHeight();
var halfWidth = canvasWidth / 2;
var halfHeight = canvasHeight / 2;
var offsetX = canvas.offset().left;
var offsetY = canvas.offset().top;
// Main vars
var mouseX = event.clientX - offsetX;
var mouseY = event.clientY - offsetY;
var maxDegree = 20 * Math.PI / 180;
var rotationZ = 0;
if (mouseX < halfWidth) {
rotationZ = -1* (halfWidth - mouseX) * (maxDegree / halfWidth);
} else {
rotationZ = (mouseX - halfWidth) * (maxDegree / halfWidth);
}
var rotationX = 0;
if (mouseY < halfHeight) {
rotationX = -1* (halfHeight - mouseY) * (maxDegree / halfHeight);
} else {
rotationX = (mouseY - halfHeight) * (maxDegree / halfHeight);
}
console.log(rotationZ, rotationX);
mshFloor.rotation.set(rotationX, 0, rotationZ);
mshBox.rotation.set(rotationX, 0, rotationZ);
render();
});

How do i bind onclick event to piechart segment?

How do i bind onclick event to piechart segment?
https://github.com/sauminkirve/HTML5/blob/master/PieChart/piechart.html
A pie chart segment is really a wedge. You have several ways to hit-test a wedge.
One way is the math way:
Test if the mouse is within the radius of a circle created by the wedges.
If the radius test is true, then calculate the angle of the mouse versus the circle's centerpoint.
Compare that angle to each wedge. If the angle is between the starting and ending angle of a specific wedge's arc, then the mouse is inside that wedge.
Another way is to use canvas's built in path hit-testing method: isPointInPath
Redefine one wedge. There's no need to actually stroke or fill that wedge. Just do the commands from beginPath to closePath.
Use context.isPointInPath(mouseX,mouseY) to hit-test if the mouse is inside that wedge.
If isPointInPath returns true, you've discovered the wedge under the mouse. If not, then redefine & hit-test each of the other wedges.
Here's something I coded a while back that hit-tests the wedges of a pie chart when hovering and moves the wedge out of the pie when a wedge is clicked.
It uses the isPointInPath method to do the hit-testing:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineJoin = "round";
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
function Wedge(cx, cy, radius, startAngleDeg, endAngleDeg, fill, stroke, linewidth) {
this.cx = cx;
this.cy = cy;
this.radius = radius;
this.startAngle = startAngleDeg * Math.PI / 180;
this.endAngle = endAngleDeg * Math.PI / 180;
this.fill = fill;
this.stroke = stroke;
this.lineWidth = linewidth;
this.offsetX = 0;
this.offsetY = 0;
this.rr = radius * radius;
this.centerX = cx;
this.centerY = cy;
this.midAngle = this.startAngle + (this.endAngle - this.startAngle) / 2;
this.offsetDistance = 15;
this.explodeX = this.offsetDistance * Math.cos(this.midAngle);
this.explodeY = this.offsetDistance * Math.sin(this.midAngle);
this.isExploded = false;
};
Wedge.prototype.draw = function(fill, stroke) {
this.define();
this.fillStroke(fill, stroke);
ctx.beginPath();
ctx.arc(this.cx, this.cy, this.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.lineWidth = 0.50;
ctx.stroke();
}
Wedge.prototype.fillStroke = function(fill, stroke) {
ctx.fillStyle = fill || this.fill;
ctx.fill();
ctx.strokeStyle = stroke, this.stroke;
ctx.lineWidth = this.lineWidth;
ctx.stroke();
}
Wedge.prototype.define = function() {
var x = this.cx + this.offsetX;
var y = this.cy + this.offsetY;
ctx.beginPath();
ctx.arc(x, y, this.radius, this.startAngle, this.endAngle);
ctx.lineTo(x, y);
ctx.closePath();
}
Wedge.prototype.ptAtAngle = function(radianAngle) {
var xx = (this.cx + this.offsetX) + this.radius * Math.cos(radianAngle);
var yy = (this.cy + this.offsetY) + this.radius * Math.sin(radianAngle);
return ({
x: x,
y: y
});
}
Wedge.prototype.explode = function(isExploded) {
this.isExploded = isExploded;
this.offsetX = isExploded ? this.explodeX : 0;
this.offsetY = isExploded ? this.explodeY : 0;
this.draw();
}
Wedge.prototype.isPointInside = function(x, y) {
var dx = x - (this.cx + this.offsetX);
var dy = y - (this.cy + this.offsetY);
if (dx * dx + dy * dy > this.rr) {
return (false);
}
var angle = (Math.atan2(dy, dx) + Math.PI * 2) % (Math.PI * 2);
return (angle >= this.startAngle && angle <= this.endAngle);
}
Wedge.prototype.marker = function(pos) {
ctx.beginPath();
ctx.arc(pos.x, pos.y, 3, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = "red";
ctx.fill();
}
function handleMouseDown(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
clear();
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i].wedge;
if (wedge.isPointInside(mouseX, mouseY)) {
wedge.explode(!wedge.isExploded);
}
wedge.draw();
}
}
function handleMouseUp(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mouseup stuff here
isDown = false;
}
function handleMouseOut(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mouseOut stuff here
isDown = false;
}
function handleMouseMove(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i].wedge;
if (wedge.isPointInside(mouseX, mouseY)) {
wedge.draw("black");
} else {
wedge.draw();
}
}
}
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function(e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function(e) {
handleMouseOut(e);
});
function clear() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
var PI2 = Math.PI * 2;
var cx = 150;
var cy = 150;
var r = 100;
var line = 2;
var stroke = "black";
var wedges = [];
wedges.push({
percent: 18,
fill: "red"
});
wedges.push({
percent: 30,
fill: "blue"
});
wedges.push({
percent: 25,
fill: "green"
});
wedges.push({
percent: 13,
fill: "purple"
});
wedges.push({
percent: 14,
fill: "gold"
});
var rAngle = 0;
for (var i = 0; i < wedges.length; i++) {
var wedge = wedges[i];
var angle = 360 * wedge.percent / 100;
wedge.wedge = new Wedge(cx, cy, r, rAngle, rAngle + angle, wedge.fill, "black", 1);
wedge.wedge.draw();
rAngle += angle;
}
window.onscroll = function(e) {
var BB = canvas.getBoundingClientRect();
offsetX = BB.left;
offsetY = BB.top;
}
body {
background-color: ivory;
}
#canvas {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Hover wedge to highlight it<br>Click wedge to explode that wedge</h4>
<canvas id="canvas" width=300 height=300></canvas>

html5 canvas: overlapping paths not filling correctly

strange issue with HTML5 canvas: I am trying to draw one shape inside another. The outer shape is blue and the inner one red, but the end result is that both shapes end up red. If I step through the code, I can see the blue shape rendered correctly, but then the red shape renders over the blue one, even though it's smaller. Probably a problem with BeginPath/EndPath stuff, but I've seemingly tried every combination with no luck. I have lots more shapes to draw after this one, so I need to figure out how to correctly begin/end a shape before I resume work. Any help is appreciated.
<script type="text/javascript">
window.onload = function () {
var drawingCanvas = document.getElementById('canvas1');
// Is element in the DOM and does browser support canvas
if (drawingCanvas && drawingCanvas.getContext) {
// Init drawing context
var InfieldColor = "#BDB76B";
var OutfieldColor = "#F5F5F5";
var iGrassLen = Math.min(drawingCanvas.width, drawingCanvas.height) * 0.7;
var iRad = iGrassLen * 1.475;
var iAng = -60 * Math.PI / 180;
var iptInfBez0x = iRad * Math.cos(iAng);
var iptInfBez0y = -(iRad * Math.sin(iAng));
iAng = -30 * Math.PI / 180;
var iptInfBez1x = iRad * Math.cos(iAng);
var iptInfBez1y = -(iRad * Math.sin(iAng));
var iInfieldLen = (iGrassLen * (88 / 124));
var iBaseLen = iInfieldLen / 12;
//this is the relative offset between Dixon infield and outfield
var iOutfieldLen = iGrassLen * (282 / 124)
//bezier control points for outfield circle
iRad = iOutfieldLen * 1.31;
iAng = -60 * Math.PI / 180;
var iptOutBez0x = iRad * Math.cos(iAng);
var iptOutBez0y = -(iRad * Math.sin(iAng));
iAng = -30 * Math.PI / 180;
var iptOutBez1x = iRad * Math.cos(iAng);
var iptOutBez1y = -(iRad * Math.sin(iAng));
var iHRLen0 = (340 * iInfieldLen / 90) * 1.025; //iInfieldLen = 90 feet. (plus a fudge factor)
var iHRLen1 = (370 * iInfieldLen / 90) * 1.025;
var iHRLen2 = (400 * iInfieldLen / 90) * 1.025;
var iMoundWid = iInfieldLen / 10;
var context = drawingCanvas.getContext('2d');
context.fillStyle = "#FFFF00";
context.fillRect(0, 0, drawingCanvas.width, drawingCanvas.height);
context.beginPath;
context.moveTo(0, 0);
context.lineTo(iGrassLen, 0);
context.bezierCurveTo(iptInfBez1x, iptInfBez1y, iptInfBez0x, iptInfBez0y, 0, iGrassLen); // bezier curve
context.lineTo(0, 0);
context.closePath();
context.fillStyle = "blue";
context.fill();
context.lineWidth = 1;
context.strokeStyle = "black";
context.stroke();
//infield rectangle
context.beginPath;
context.rect(0, 0, iInfieldLen - (iBaseLen / 4), iInfieldLen - (iBaseLen / 4));
context.closePath;
context.fillStyle = "red";
context.fill();
context.lineWidth = 1;
context.strokeStyle = "black";
context.stroke();
}
}
</script>
context.beginPath;
...
context.closePath;
You forgot (). Without that, these are just discarded references to a function, not calls to it.

Resources