For a personal project, I'm tyring to make a timer application (for controlling Poker blind schedules). I know there are several solutions already on the market, but for reasons which are too lengthy to go into here, we need a bespoke system. Although the output template of the system will ultimately be definable by the end user, we would like to include a widget which shows a circle that gets animated as the time counts down. Here's an illustration of a working example, showing the yellow circle and what we'd like to achieve (or something similar, anyway):
My question is, how would one code the animation as shown below using either jQuery or raw HTML5 / CSS3 animations? This is for a web application using jQuery, so there are no other library options I can use.
Advanced thanks!
If you can use HTML5, canvas is probably your best bet. Mozilla has some decent tutorials on drawing arcs. This should be enough to get you started.
var canvas = document.getElementById('canvasid'),
width = canvas.width,
height = canvas.height,
ctx = canvas.getContext('2d');
function drawTimer(deg) {
var x = width / 2, // center x
y = height / 2, // center y
radius = 100,
startAngle = 0,
endAngle = deg * (Math.PI/180),
counterClockwise = true;
ctx.clearRect(0, 0, height, width);
ctx.save();
ctx.fillStyle = '#fe6';
// Set circle orientation
ctx.translate(x, y);
ctx.rotate(-90 * Math.PI/180);
ctx.beginPath();
ctx.arc(0, 0, radius, startAngle, endAngle, counterClockwise);
ctx.lineTo(0, 0);
ctx.fill();
}
setInterval(function() {
// Determine the amount of time elapsed; converted to degrees
var deg = (elapsedTime / totalTime) * 360;
drawTimer(deg);
}, 1000);
You can achieve this with CSS3 and jQuery.
Check out my example here http://blakek.us/css3-pie-graph-timer-with-jquery/ which with slight modification you could make the timer look how you want it.
In HTML5 you can draw in a canvas.
For example:
// Create the yellow face
context.strokeStyle = "#000000";
context.fillStyle = "#FFFF00";
context.beginPath();
context.arc(100,100,50,0,Math.PI*2,true);
context.closePath();
context.stroke();
context.fill();
Link
You should really check out Processing!
Especially Processing.js. There have been some interesting things made with it for iPhone
First solution i can think of is html5 canvas implementation. From the example above it becomes clear we're talking mobile, so what support does canvas get around mobile browsers i can't tell. developer.mozilla.org provides sufficient documentation. Using canvas to achieve such count down should be pretty simple task. Good luck.
HTML5 Canvas arc command (MDN) is probably what you're looking for. Just decrease the end angle over time to have your timer decrease.
Related
I'm coding a game in the style of agar.io where I want to move the player to the direction relative to the mouse position.
This is how I check the mouse position, returning a Vector object;
let mouse_vector = new Vector();
canvas.onmousemove = function() {
mouse_vector = mouse(event);
}
function mouse(evt) {
mouse_vector = new Vector(evt.clientX - canvas.width/2, evt.clientY - canvas.height/2);
return mouse_vector;
}
The player is an object with an x and y coordinate to which I add the vector pointing towards the mouse. I have 1 canvas that represents the world and is hidden. The other canvas is my viewport, on which I draw the cropped world relative to the player position.
I'm using requestAnimationFrame here, but I tried using Interval as well.
function draw() {
player.x += mouse_vector.x * 0.005;
player.y += mouse_vector.y * 0.005;
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
canvasCtx.drawImage(game, player.x-canvas.width/2, player.y-canvas.height/2, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
canvasCtx.arc(canvas.width/2, canvas.height/2, player.r, 0, 2*Math.PI);
canvasCtx.fill();
requestAnimationFrame(draw);
}
My issue is that the game starts stuttering over time. I would like it to be as smooth as at the start. I'm suspecting the issue is related to some kind of caching. I've noticed the game uses a lot of CPU power. Any ideas?
The issue I was having was that I was not using the beginPath() method when filling arcs. Seems that not reseting the previous draw builds up and causes performance loss over time.
canvasCtx.beginPath(); // addition
canvasCtx.arc(canvas.width/2, canvas.height/2, player.r, 0, 2*Math.PI);
canvasCtx.fill();
So at the end of the day, you'll be re-drawing the whole map every 16ms to get 60fps which is the target of requestAnimationFrame - so you'll not be escaping the CPU hogging problem, unless you do some optimisation about the drawing.
About the logic you are doing: it doesn't seem from this piece of code that there can be anything that could be building up or leaking memory, so I suggest pasting your code to a code-sharing site like codepen.io and sharing it with the community, we'd have more chance of debugging it.
This is my pseudocode:
when move button is pressed:
currentCirclePos = circleX;
moveTimer = setInterval(move, 10);
My move function looks like this:
var move = function() {
circleX += move a bit;
};
In some cases, the element does not move at all. For those situations, I have the following code :
while(Math.abs(circleX - currentCirclePos) < some distance away){
drawCircle(circleX, circleY);
circleX += gradual shift;
}
This does not change the position of my circle gradually but draws it abruptly some distance away. I don't understand why is that happening. Could anyone help me debug the issue? Let me know if I need to post more code.
This is my requestAnimationFrame code:
requestAnimationFrame(animate);
function animate(){
ctx.clearRect(0,0,cw,ch);
ctx.rect(0, 0, cw, ch);
ctx.fillStyle = 'white';
ctx.fill();
drawCircle(circleX, circleY);
requestAnimationFrame(animate);
}
UPDATE : The while loop was not inside the animation function but now it is. However, it did not make any difference at all.
The problem is that You're using setInterval instead of requestAnimationFrame - it's more reliable way to do this kind of operations on canvas.
Please check this tutorial - author solves this problem pretty straightforward on example.
I'm trying to simulate brushes with the HTML canvas element. To get brush hardness, I'm using a radial gradient, but I'm not entirely sure whether it's faster to create a new radial gradient for every point or saving the radial gradient as an image and use drawImage() instead.
Current code:
var gradient = context.createRadialGradient(x, y, hardness * brushSize, x, y, brushSize);
gradient.addColorStop(0, color);
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
context.fillStyle = gradient;
context.fillRect(x - halfBrushSize, y - halfBrushSize, brushSize, brushSize);
drawImage (apart from creating the image):
context.drawImage(img, x, y);
Gradients are expensive to generate contrary to images which are basically copies. They both need to go through transformation matrix and anti-aliasing process though, but there is no calculation involved with images besides from that.
UPDATE From the comments below people seem to get extremely variable test results depending on browser and hardware. The embedded test is not very accurate and was meant as a pointer, so for this reason I created a more accurate test here. Feel free to post results below in comments.
-- update end --
The following is not the world's most accurate test, but the difference is so large that you get a pretty good pointer in any case to which is faster:
window.performance = window.performance || Date;
setTimeout(go, 250);
function go() {
var ctx = c.getContext("2d");
// create radial gradient
var gr = ctx.createRadialGradient(300, 300, 300, 300, 300, 0);
gr.addColorStop(0, "#000");
gr.addColorStop(1, "#f00");
ctx.fillStyle = gr;
// test gradient fill style
var time1 = performance.now();
for (var i = 1000; i--;) ctx.fillRect(0, 0, c.width, c.height);
var time2 = performance.now() - time1;
o.innerHTML = "Gradient: " + time2.toFixed(4) + "<br>";
// test cached gradient (canvas = image source)
ctx = c2.getContext("2d");
time1 = performance.now();
for (i = 1000; i--;) ctx.drawImage(c, 0, 0);
time2 = performance.now() - time1;
o.innerHTML += "drawImage: " + time2.toFixed(4);
}
<output id=o>Running... please wait</output><br>
<canvas id=c width=600 height=600></canvas><br>
<canvas id=c2 width=600 height=600></canvas>
When it comes to render a radial gradient, you can build the gradient on-the-fly, or use a png as you quoted, yet there's a third possibility : you can use a normalised gradient, that you build once, then use at will at any place/size by using the context transforms.
The code used to create the normalized gradient for a given hardness looks like :
var mySingleGradient = ctx.createRadialGradient(0.5, 0.5, 0.5*hardness, 0.5, 0.5, 0.5);
mySingleGradient.addColorStop(0, color);
mySingleGradient.addColorStop(1, '#000');
Just like when you are using png, you'll run into the issue of caching the gradients for any base color + hardness. But you won't have any png resolution issue, and most probably the size of the gradients will be way smaller than the png's.
You use such a normalised gradient with :
function drawThatGradientHere(ctx, x, y, gradient, brushSize) {
ctx.save();
ctx.translate(x,y);
ctx.scale(brushSize,brushSize);
ctx.fillStyle = gradient;
ctx.fillRect(0,0,1,1);
ctx.restore();
}
I won't go into benchmarking, since there are too many chances to compare apples and oranges without knowing more about the use. Because for instance, the drawImage might very well perform very differently if you are using its scaled version. Mind also that by using an image, you might run into resolution issues (too high : perf, too low : aliasing), that you won't have if you are using a gradient. So even if the gradient was proved slower, you might prefer it because of the way it consistently looks.
A few questions : do you change your hardness often ? do you change the brush size often ? do you change the start/end color of your gradient ?
It's only by answering those question and having a random set of rect/hardness that has the same average distribution of your real use case that you'll be able to benchmark/compare anything.
Last word : If it's becoming hard to say which solution is faster, its time to pick the solution relying on... some other good reason... :-)
Why is it need to use context.save and context.restore() in any Javascript that is drawing lines to a canvas?
this.context = this.canvas.getContext("2d");
I am certain that it has to do with this line of code this.canvas.getContext("2d"); and canvas was defined above.
this.canvas = document.getElementById(config.canvasId);
Code:
BarChart.prototype.drawGridlines = function(){
var context = this.context;
context.save();
context.strokeStyle = this.gridColor;
context.lineWidth = 2;
// draw y axis grid lines
for (var n = 0; n < this.numGridLines; n++) {
var y = (n * this.height / this.numGridLines) + this.y;
context.beginPath();
context.moveTo(this.x, y);
context.lineTo(this.x + this.width, y);
context.stroke();
}
context.restore();
};
context.save() saves the current state of the context (strokeStyle, lineWidth, etc...) then your code will change those values. At the end of your code you call context.restore() which will restore the previous values that your code has changed.
This way you don't need to manually restore everything you changed and your code will not affect other code on the page that has previously modified these values.
You don't. context.save and context.restore are often used for efficient texture translation and rotation, hence show up in many examples.
Save stores your position, angle and various other canvas states. Then you can move to the position/angle you want to draw your image. Restore can then snap your co-ord grid back to the save point. Much easier than rotating points in space mathematically, and the only way to rotate images i know of with canvas 2d context mode.
If your doing simple drawing of boxes, lines or unrotated images then don't bother.
Are there any open source libraries (JaveScript) that implement even-odd fill rule on canvas.
If I try to implement it myself then how complex would it be (considering general case which has complex curves also) and would it hit on the performance (due to overhead of doing it for each pixel in JaveScript).
What are the methods for converting even-odd fill to non-zero winding (considering a generic solution that will work for every case). Once method I found was to divide the shape into all non-intersecting polygons and fill them separately.
One option is to use SVG and draw the SVG on canvas, but I also found that native SVG rendering is a bit slow on iPad, but is SVG slow even when I draw it on HTML canvas (on iPad)?
Thanks in advance
I came across this question as I was looking for the same some time ago. That is, use a "evenodd" fill-rule inside a HTML canvas.
After a little research, and going through mailing lists and patches, I noticed that, as it turns out, recent versions of both Firefox and Chrome have support for this, but each in a different, browser-specific, way.
In Firefox it is a matter of using the mozFillRule attribute. Example:
//
// Firefox Example
//
// canv has the HTML canvas element
var ctx = canv.getContext("2d");
ctx.fillStyle = "#ff0000";
ctx.mozFillRule = 'evenodd';
ctx.beginPath();
ctx.moveTo(100, 10);
ctx.lineTo(40, 180);
ctx.lineTo(190, 60);
ctx.lineTo(10,60);
ctx.lineTo(160,180);
ctx.closePath();
ctx.fill();
In Chrome, you do it by passing the string evenodd as a parameter to the fill method. Example:
//
// Chrome Example
//
// canv has the HTML canvas element
var ctx = canv.getContext("2d");
ctx.fillStyle = "#ff0000";
ctx.beginPath();
ctx.moveTo(100, 10);
ctx.lineTo(40, 180);
ctx.lineTo(190, 60);
ctx.lineTo(10,60);
ctx.lineTo(160,180);
ctx.closePath();
ctx.fill('evenodd');
These are the two browsers I researched for, so I don't know about the state of this in other browers. Hopefully in the not-so-distant future we will be able to use this feature via the fillRule attibute that is now part of the HTML standard.
See fill() method API:
void ctx.fill();
void ctx.fill(fillRule);
void ctx.fill(path, fillRule);
fillRule can be "nonzero" or "evenodd"
--- "nonzero": The non-zero winding rule, which is the default rule.
--- "evenodd": The even-odd winding rule.
Browser compatibility:
--- IE 11+, Firefox 31+, Chrome are OK.
--- I didn't test on IE 9/10.
--- Use ctx.mozFillRule = 'evenodd'; with old Firefox 30/30-.