I'm trying to make an animation using html canvas, but I don't know if I have the best approach to it.
What I'm trying is to make a <canvas id="canvas"></canvas> and draw a rectangle on it using the fillRect().
Then I'm executing a function onload, that has a timeout of 500 miliseconds.
The function basically draws the rectangle 1px to where I want, by changing its x or y, and then, with clearRect(), I'm crealing the rectangle some time after starting on the starting point and following the other one.
Am I doing this right? or is there a better way to approach it?
You can use this structure
const canvas = document.getElementById('can');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.backgroundColor = 'white';
var someconstructorName = function(paramA,paramB,...){
"Initialisation of variables and other things"
this.draw = function(){
"your logic"
}
this.update = function(){
"your update logic"
this.draw();
}
}
function animate(){
requestAnimationFrame(animate)
}
animate();
Check out this Pen here it will give you a good idea:
https://codepen.io/khanChacha/pen/YgpBxM
If you have to use this style multiple times, I recommend diving into AnimeJS, a JS library which makes animating a lot simpler. ^^
AnimeJS
It supports delays and timelines too, which seems to be what you are using right now ^^
Related
So I have a problem with clearRect in js-canvas-animation. This problem happens only on Android API 16, and only in the moment when animation is restart.
I'm using setInterval() for my animation (here is simplify code)
function start() {
clearInterval(animationInterval);
x = 0;
canvas = document.getElementById("animationCanvas");
ctx = canvas.getContext("2d");
animationInterval = setInterval(func, 30);
}
function func() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(halfWidth - x, 0, 250, 150);
x += extensionStep;
}
It is look like a shore. But every time, when i restart animation with help start function, under new animation i see last frame previous animation. Already i was trying beginPath(), save, stroke. I check all question on SO and nothing.
How i can clear the background under animation?
So after several hours, i found one of the solves. It is strange bug for old android, because new android (i checked on api 24) it fixed. For hard cleaning you can detach canvas from DOM and reattach again:
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.style.display = 'none';// Detach from DOM
canvas.offsetHeight; // Force the detach
canvas.style.display = 'inherit'; // Reattach to DOM
It is simple operation and is not resource intensive.
I am using requestAnimationFrame to animate video streams and I know that request animation does not work in backgrounds so is there any way to make it run on background.
I know that we can animate using setInterval or setTimeout too but when I use these, animations were not work properly and images on video stream were blinking.
Here is my code:
const drawFrame = function drawFrame() {
if (!ctx) {
ctx = canvas.getContext('2d');
}
if (!tmpCanvas) {
tmpCanvas = document.createElement('canvas');
tmpCtx = tmpCanvas.getContext('2d');
tmpCanvas.width = canvas.width;
tmpCanvas.height = canvas.height;
}
tmpCtx.drawImage(videoElement, 0, 0, tmpCanvas.width, tmpCanvas.height);
const imgData = tmpCtx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height);
const data = selectedFilter(imgData);
ctx.putImageData(data, 0, 0);
if (!stopped) {
requestAnimationFrame(drawFrame);
} else {
tmpCanvas = null;
tmpCtx = null;
ctx = null;
}
};
requestAnimationFrame(drawFrame);
I guess you are out of luck. requestAnimationFrame intentionally pauses in the background because animations have no reason to be running in the background, and as you mention setInterval and setTimeout are not designed for animation and should not be used as such.
You mention that you need to animate a video stream. Do you mean that you are applying an animation on top of a video tag, or changing the appearance of the video element via a canvas element? In both cases, you shouldn't continue to do so in the background anyway, but you may give computeFrame (Firefox only) a go, which will fire once every frame of the video. Or is it that you are actually faking a video stream by animating a set of static images in succession? In that case, why are you not using the video element?
I have a project using HTML5 Canvas (createjs) and I've had issues with spikes on text strokes, meaning I have to set the miterlimit of the 2d context. However, the parent (which I have no control over) scales the canvas when the window is resized, which obviously resets the canvas context.
Now, I want to avoid putting an onresize event inside the client - my first thought is just to use the createjs Ticker thus:
createjs.Ticker.addEventListener("tick", handleTick);
function handleTick(event) {
var ctx = canvas.getContext('2d');
ctx.miterLimit = 2;
}
Though this seems a little wasteful, is there a more efficient way of doing this, without using DOM events?
Your approach might work, but its definitely a hack, since you can't expect that context properties will be maintained, or that they won't be applied in the wrong place.
If you do want to patch the display list to update the context, you can use the "drawstart" event, which fires before the display list is drawn:
stage.on("drawstart", function(e) {
var ctx = stage.canvas.getContext("2d");
ctx.miterLimit = 2;
});
However if you want a better approach that is instance-specific, you can extend the Text class to append any context properties you want. Here is a quick example where miterLimit is stored and applied any time the text is drawn. In this example, you can create multiple instances and set different miter limits on them. Note that you might want to also support other properties such as lineJoin.
http://jsfiddle.net/cr4hmgqp/2/
(function() {
"use strict"
function MiterText(text, font, color, miterLimit) {
this.Text_constructor(text,font,color);
this.miterLimit = miterLimit;
};
var p = createjs.extend(MiterText, createjs.Text);
p.draw = function(ctx, ignoreCache) {
ctx.miterLimit = this.miterLimit;
if (this.Text_draw(ctx, ignoreCache)) { return true; }
return true;
};
p.clone = function() {
return this._cloneProps(new MiterText(this.text, this.font, this.color, this.miterLimit));
};
createjs.MiterText = createjs.promote(MiterText, "Text");
}());
Note that this issue should hopefully be fixed in the next version of EaselJS. Here is the tracked issue: https://github.com/CreateJS/EaselJS/issues/781
Cheers.
I am creating a image in a canvas and saving the image. I found a very nice plugin here.
Code for the image saving:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.src = imageURI;
imageObj.onload = function() {
contentW = $("#content").width();
canvas.width = 400;
canvas.height = 600;
context.drawImage(imageObj, 0, 0,canvas.width,canvas.height);
//the plugin
setTimeout(function(){
window.savephotoplugin(canvas,"image/png",device.version,function(val){
//returns you the saved path in val
alert("Photo Saved: " + val);
});
},3000)
}
The plugin works very nice only problem is that it is done before the canvas is even drawn. So I put a setTimeout to avoid it, however is there a way to detect when canvas is done and call the function after it. Tried jquery .change() didn't work.
If anyone finds this code useful feel free to use and the plugin is very nice :)
As you can see drawImage doesn't accept any callbacks. Also canvas doesn't define any events about its drawing process. So you choice of timeout is correct. Only one thing you could possibly improve. Use setTimeout(..., 0) instead of setTimeout(..., 3000). More details about this trick here
I've been playind with canvas lately and started today to work on using setInterval to refresh / animate it regularly.
I was surprised to see how this is heavy for the cpu and slows down eveyrthing. Looking at example online I m sure there is something wrong with my way of doing. I then simplified what I wanted to do at the maximum (not playing with image but rectangles, not using too many objects, etc) but still got the same problem.
I was trying to get a white flash (at 12fps) on top of two rectangles...So nothing complicate at all...
Here is my code.
function Canvas(name){
this.ctx=document.getElementById(name).getContext('2d');
this.canvas=this.ctx.canvas;
this.width=this.canvas.width;
this.height=this.canvas.height;
this.layers=new Array();
this.draw = function() {
this.canvas.width = this.canvas.width;
this.ctx.beginPath();
this.ctx.rect(0,0,this.width,this.height);
this.ctx.closePath();
this.ctx.fillStyle="#eaeaea";
this.ctx.fill();
this.ctx.beginPath();
this.ctx.rect(250,50,300,250);
this.ctx.closePath();
this.ctx.fillStyle="#ff0000";
this.ctx.fill();
intensity=Math.random();
this.flash(intensity);
};
this.flash = function(intensity) {
this.ctx.globalAlpha = intensity;
this.ctx.beginPath();
this.ctx.rect(0,0,this.width,this.height);
this.ctx.closePath();
this.ctx.fillStyle="#fff";
this.ctx.fill();
this.ctx.globalAlpha = 1;
setInterval(this.draw.bind(this),1000);
};
function initCanvas(){
mycanvas=new Canvas('myCanvas');
mycanvas.draw();
}
$(document).ready(function() {
initCanvas();
});
Solution found:
Use setTimeout instead of setInterval.
Close all the paths, which you open:
this.draw = function() {
this.canvas.width = this.canvas.width;
this.ctx.beginPath();
this.ctx.rect(0,0,this.width,this.height);
this.ctx.closePath(); //Closing
this.ctx.fillStyle="#eaeaea";
this.ctx.fill();
this.ctx.beginPath();
this.ctx.rect(250,50,300,250);
this.ctx.closePath(); //Closing
this.ctx.fillStyle="#ff0000";
this.ctx.fill();
this.flash(40);
};
this.flash = function(intensity) {
this.ctx.globalAlpha = intensity;
this.ctx.beginPath();
this.ctx.rect(0,0,this.width,this.height);
this.ctx.closePath(); //Closing
this.ctx.fillStyle="#fff";
this.ctx.fill();
this.ctx.globalAlpha = 1;
setInterval(this.draw.bind(this),1000);
};
You've got a massive memory leak because you keep using setInterval in the flash function. Let's look at the sequence of events
mycanvas object created
draw()
draw calls flash
flash sets an interval to call draw every second
draw calls flash and sets another interval
Process repeats till you've got a lot of intervals calling draw
To solve it, use setTimeout in flash. So it calls draw after a second, which calls flash and then calls draw again in a second. Also, 1000ms won't give you 12fps. 1000/12 will.
Also, use ctx.closePath(); to close the paths you opened with beginPath()
You also never closed the Canvas function with a }.
Here's a demo
I don't know if this is relevant anymore, but I found myself in a similar situation and wanted to give an even better answer.
Use requestAnimationFrame(yourLoop), especially for games since it is faster and has better performance.
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/