How to make my canvas animation to not loop - html

<script>
function hum() {
var width = 280,
height = 210,
frames = 3,
currentFrame = 0,
hump = document.getElementById("Canvas2").getContext('2d');
humpImage = new Image()
humpImage.src = 'images/hum.png';
var draw = function () {
hump.clearRect(0, 0, width, height);
hump.drawImage(humpImage, 0, height * currentFrame, width, height, 0, 0, width, height);
if (currentFrame == frames) {
currentFrame = 0;
} else {
currentFrame++;
}
}
setInterval(draw, 150);
}
</script>
The above is a canvas animation javascript code . Here it loops automatically.. I want to make to cancel the loop and to play the canvas only on click how to do it.
Please spot light on this

Saravan, theres a number of different ways you could go about this. Best practise, I believe is to use the RequestAnimationFrame function, though I don't seem to find any old source code here in which I've used it.
I notice in your code, each time around the draw-loop, you call setInterval. Unless I'm mistaken, the correct function to use there is actually setTimeOut. As you can see from my code, (and js help) the function setInterval only needs to be called once.
When you call it, it gives you a unique identifier which can be used later to remove the interval handler. That's the method I've used. When the button is clicked, the interval is set and it's handle saved. Then, when the canvas is clicked, I simply remove the interval handler.
It's late and I'm tired. This should give you plenty to go on.
EDIT: new code.
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
window.addEventListener('load', mInit, false);
var drawIntervalHandle;
var hump, humpImage, isRunning = false;
var width = 44,height = 128,frames = 3,currentFrame = 0;
var myImageSource = 'thermometer.png';
function mInit()
{
byId('Canvas2').addEventListener('click', onCanvasClick, false);
initAndStartAnim();
}
function onCanvasClick()
{
if (isRunning)
{
clearInterval(drawIntervalHandle);
isRunning = false;
byId('status').innerHTML = "Stopped, frame: " + currentFrame;
}
else
{
drawIntervalHandle = setInterval(draw, 150);
isRunning = true;
}
}
function draw()
{
hump.clearRect(0, 0, width, height);
hump.drawImage(humpImage, 0, height * currentFrame, width, height, 0, 0, width, height);
if (currentFrame == frames)
currentFrame = 0;
else
currentFrame++;
byId('status').innerHTML = "Running, frame: " + currentFrame;
}
function initAndStartAnim()
{
var can = document.getElementById("Canvas2");
can.width = width;
can.height = height;
hump = can.getContext('2d');
humpImage = new Image()
humpImage.src = myImageSource;
drawIntervalHandle = setInterval(draw, 150);
isRunning = true;
}
</script>
<style>
</style>
</head>
<body>
<canvas id='Canvas2'>Canvas not supported if you see this. :(</canvas>
<br>
<div id='status'></div>
</body>
</html>

Related

Canvas animation using png file

I am trying to make a racing game using canvas, and a png file with transparent background for the car.
I have a problem when I hold down on one of the arrows. In the opposite way of the movement it appears like a white shadow the old position of the car.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var canvas;
var ctx;
var car = new Image();
var x = 0;
var y = 0;
function startUp(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
loadImages();
startGameLoop();
}
function startGameLoop() {
setInterval(function() {
drawScreen();
drawCar();
}, 16);
window.addEventListener('keydown', whatKey, true);
}
function drawScreen(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#aaaaaa';
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fill();
}
function drawCar(){
ctx.drawImage(car, x ,y, 200, 103);
}
function whatKey(evt) {
switch (evt.keyCode) {
// Left arrow.
case 37: {
x -= 15;
}
break;
// Right arrow.
case 39:{
x += 15;
}
break;
// Down arrow
case 40:{
y += 15;
}
break;
// Up arrow
case 38: {
y -= 15;
}
break;
}
}
function loadImages() {
car.src = 'http://sammywasem.com/images/f3-top.png';
}
</script>
</head>
<body onload="startUp()">
<canvas id="myCanvas" width="1050" height="620">
</canvas>
</body>
</html>
The car move is in fact the right way.
Say you press the right key : the event is sent to the window, and trapped by your code : the car goes right. But the event is also handled by the whole window, where it is understood as : move the window to the right.
So what you see in the end, since the window move more than the car, is a move to the left of the car.
What you need to do is to prevent the key event from bubbling further once you handled it : it is done with
event.preventDefault();
and
event.stopPropagation();
look mdn to see the specifications of preventDefault or stopPropagation .
i've done a fiddle out of your code, and updated it :
http://jsbin.com/joboxuvu/1/edit?js,output

clearRect not working?

I am a very beginner web designer and I have two questions about this code.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#canvas1{border: #666 3px solid;}
</style>
<script type="application/javascript" language="javascript">
function draw (x,y){
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext('2d');
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillstyle = "rgb (0,200, 0)";
ctx.fillRect(x, 20, 50, 50);
ctx.restore();
x += 5;
var loop = setTimeout('draw('+x+', '+y+')', 100);
}
</script>
</head>
<body>
<button onclick="draw(0,0)">Start</button>
<canvas id="canvas1" width="400" height="400"</canvas>
</body>
</html>
Why does the block always turn out black? and Why if I try to press start again, the clearRect function doesn’t work?
When you click start you are starting a new loop that runs parallel to the one already started and depending on which one executes first your canvas would be cleared and filled twice (or as many times you started the loop - the more the more pronounced the flicker will be).
You need a mechanism to prevent the loop from starting several times. One way is to use a flag. I would also suggest you refactor the code a bit separating the loop and the drawing:
Live demo here
/// put these outside, no need to re-allocate them each time
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext('2d');
var w = canvas.width;
var h = canvas.height;
var x = 0;
var isRunning = false;
/// all loop-related here:
function loop() {
/// call draw from inside the loop instead
draw(x, 0); /// you are never using y so I just set 0 here...
x += 5;
if (isRunning && x <= w) {
requestAnimationFrame(loop); /// this is a better alternative
//setTimeout(loop, 100); /// optionally, but not recommended
} else {
isRunning = false; /// box is outside visible area so we'll stop..
}
}
/// keep draw() "clean", no loop code in here:
function draw(x, y) {
/// no need for save/restore here...
ctx.clearRect(0, 0, w, h);
ctx.fillStyle = "rgb(0, 200, 0)";
ctx.fillRect(x, 20, 50, 50);
}
Your fillStyle was typed wrong, your rgb(... must be without space (as mentioned in the other answer - but it would only result in the fill style being black in this case), in addition you're missing a closing bracket for your canvas tag in the html.
To check for button clicks this is a more recommended way instead of inlining the JS in the html:
/// check for button clicks (start):
document.getElementById('start').addEventListener('click', function() {
if (!isRunning) { /// are we running? if not start loop
isRunning = true; /// set flag so we can prevent multiple clicks
x = 0; /// reset x
loop(); /// now we start the *loop*
}
}, false);
And in your html:
<button id="start">Start</button>
Now you can easily make a pause button:
<button id="stop">Stop</button>
and add this to the script:
document.getElementById('stop').addEventListener('click', function() {
isRunning = false;
}, false);
Because you need to use:
ctx.fillStyle = "rgb(0,200,0)";
with a capital 'S' and no space between 'rgb' and the opening bracket: http://jsfiddle.net/dtHjf/
Then, in terms of why pressing 'Start' multiple times causes the square to flicker, that's because each time you press it you're starting another animation loop, but without cancelling the old one, so that you've got multiple loops fighting with each other. If you make sure to cancel any pre-existing loop before you start a new one, you should be fine:
http://jsfiddle.net/dtHjf/2/
HTML:
<button id="startAnim">Start</button><br />
<canvas id="canvas1" width="400" height="400"></canvas>
JS:
var canvas = document.getElementById('canvas1'),
ctx = canvas.getContext('2d'),
loop;
function draw(x, y) {
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgb(0,200,0)";
ctx.fillRect(x, 20, 50, 50);
ctx.restore();
x += 5;
loop = setTimeout(function(){draw(x,y)}, 100);
}
document.getElementById('startAnim').onclick = function(){
clearTimeout(loop);
draw(0,0);
};
Also, this is unrelated to your problem, but you might want to take a look at requestAnimationFrame:
https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/

why is my canvas zoom/pan slowing down.

I'm a new programmer just trying to learn how to make webpages. I found a code to zoom and pan canvas elements but when I implemented it into an extJS window It started becoming sluggish. It doesn't become sluggish if the image I render is just a shape, only if It's from a file image. I thought at first I was creating instances of objects over and over but I tried deleting the objects after use and it didn't change anything. Why is my zooming slowing down?
Ext.onReady(function(){
Ext.define("w",{
width: 1000,
height: 750,
extend: "Ext.Window",
html: '<canvas id="myCanvas" width="1000" height="750">'
+ 'alternate content'
+ '</canvas>'
,afterRender: function() {
this.callParent(arguments);
var canvas= document.getElementById("myCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var stage = new createjs.Stage("myCanvas");
/*function addCircle(r,x,y){
var g=new createjs.Graphics().beginFill("#ff0000").drawCircle(0,0,r);
var s=new createjs.Shape(g)
s.x=x;
s.y=y;
stage.addChild(s);
stage.update();
}*///// If I use this function instead of loading an img there's no slowdown.
function setBG(){
var myImage = new Image();
myImage.src = "dbz.jpg";
myImage.onload = setBG;
var bgrd = new createjs.Bitmap(myImage);
stage.addChild(bgrd);
stage.update();
delete myImage;
delete bgrd;
};
setBG();
//addCircle(40,200,100);
//addCircle(50,400,400);
canvas.addEventListener("mousewheel", MouseWheelHandler, false);
canvas.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
var zoom;
function MouseWheelHandler(e) {
if(Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)))>0)
zoom=1.1;
else
zoom=1/1.1;
stage.regX=stage.mouseX;
stage.regY=stage.mouseY;
stage.x=stage.mouseX;
stage.y=stage.mouseY;
stage.scaleX=stage.scaleY*=zoom;
stage.update();
delete zoom;
}
stage.addEventListener("stagemousedown", function(e) {
var offset={x:stage.x-e.stageX,y:stage.y-e.stageY};
stage.addEventListener("stagemousemove",function(ev) {
stage.x = ev.stageX+offset.x;
stage.y = ev.stageY+offset.y;
stage.update();
delete offset;
});
stage.addEventListener("stagemouseup", function(){
stage.removeAllEventListeners("stagemousemove");
});
});
} //end aferrender
}); //end define
Ext.create("w", {
autoShow: true });
}); //end onready
It looks like you are infinitely re-loading the BG image. After your BG image finishes loading, your onload function callback just makes it call getBG again which will just repeat the same process forever.
function setBG() {
...
myImage.onload = setBG;
...
}
I'm not sure exactly what you expect by doing this.
You really shouldn't need to delete the image. Off the top of my head, this is how I would generally load an image for use in canvas, (based on how your train of thought is working).
function setBG(){
var myImage = new Image();
myImage.src = "dbz.jpg";
myImage.onload = function(){
var bgrd = new createjs.Bitmap(this);
stage.addChild(bgrd);
stage.update();
}
};
setBG();

Swapping between layers in kineticJS

I'm trying to treat layers as pages -- i.e. I draw on one page, then turn the page and draw on another, each time storing the previous page in case the user goes back to it.
In my mind this translates as:
Create current_layer global pointer.
Each time newPage() is called, store the old layer in an array, and overwrite the pointer
layer_array.push(current_layer); //store old layer
current_layer = new Kinetic.Layer(); //overwrite with a new
New objects are then added to the current_layer which binds them to the layer, whether they are drawn or not. (e.g. current_layer.add(myCircle) )
Retrieving a page is simply updating the pointer to the requesting layer in the array, and redrawing the page. All the child nodes attached to the layer will also be drawn too
current_layer = layer_array[num-1]; //num is Page 2 e.g
current_layer.draw()
However nothing is happening! I can create new pages, and store them appropriately - but I cannot retrieve them again...
Here's my full code (my browser is having problems using jsfiddle):
<html>
<head>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.0.min.js"></script>
<script>
//Global
var stage; //canvas
var layer_array = [];
var current_page; //pointer to current layer
window.onload = function() {
stage = new Kinetic.Stage({
container: 'container',
width: 400,
height: 400
});
//Add initial page to stage to draw on
newPage()
};
//--- Functions ----//
function newPage(){
if(!current_page){
console.log("current page undefined");
} else {
layer_array.push(current_page);
// stage.remove(current_page);
//Nope, not working.
stage.removeChildren();
//Works, but I think it unbinds all objects
// from their specific layers...
// stage.draw()
console.log("Stored layer and removed it from stage");
}
current_page = new Kinetic.Layer();
console.log("Currently on page:"+(layer_array.length+1));
stage.add(current_page);
stage.draw();
}
function gotoPage(num){
stage.removeChildren()
stage.draw()
num = num-1;
if(num >= 0) {
current_page = layer_array[num];
console.log("Now on page"+(num+1));
stage.add(current_page);
stage.draw();
}
}
function addCircletoCurrentPage()
{
var rand = Math.floor(3+(Math.random()*10));
var obj = new Kinetic.Circle({
x: rand*16, y: rand*16,
radius: rand,
fill: 'red'
})
var imagelayer = current_page;
imagelayer.add(obj);
imagelayer.draw();
}
</script>
</head>
<body>
<div id="container"></div>
<button onclick="addCircletoCurrentPage()" >click</button>
<button onclick="newPage()" >new</button>
<button onclick="gotoPage(1)" >page1</button>
<button onclick="gotoPage(2)" >page2</button>
<button onclick="gotoPage(3)" >page3</button>
</body>
</html>
This was a fun problem. I think this fixes your troubles: http://jsfiddle.net/LRNHk/3/
Basically, you shouldn't remove() or removeChildren() as you risk de-referencing them.
Instead you should use:
layer.hide(); and layer.show();
this way, you keep all things equal and you get speedy draw performance.
So your go to page function should be like this:
function gotoPage(num){
for(var i=0; i<layer_array.length; i++) {
layer_array[i].hide();
}
layer_array[num].show();
console.log("Currently on page:"+(num));
console.log("Current layer: " + layer_array[num].getName());
stage.draw();
}
I also modified your other functions, which you can see in the jsfiddle.
Okay I changed my approach and instead of swapping layers (100x easier and makes more sense), I instead opted for serializing the entire stage and loading it back.
It works, but it really shouldn't have to be like this dammit
//Global
var stage; //canvas
var layer_array = [];
var current_page; //pointer to current layer
var page_num = 0;
window.onload = function() {
stage = new Kinetic.Stage({
container: 'container',
width: 400,
height: 400
});
//Add initial page to stage to draw on
newPage()
};
//--- Functions ----//
function newPage(){
if(!current_page){
console.log("current page undefined");
} else {
savePage(page_num)
stage.removeChildren()
console.log("Stored layer and removed it from stage");
}
current_page = new Kinetic.Layer();
console.log("Currently on page:"+(layer_array.length+1));
stage.add(current_page);
stage.draw();
page_num ++;
}
function savePage(num){
if( (num-1) >=0){
var store = stage.toJSON();
layer_array[num-1] = store;
console.log("Stored page:"+num)
}
}
function gotoPage(num){
savePage(page_num);
stage.removeChildren()
if(num-1 >= 0) {
var load = layer_array[num-1];
document.getElementById('container').innerHTML = ""; //blank
stage = Kinetic.Node.create(load, 'container');
var images = stage.get(".image");
for(i=0;i<images.length;i++)
{
//function to induce scope
(function() {
var image = images[i];
var imageObj = new Image();
imageObj.onload = function() {
image.setImage(imageObj);
current_page.draw();
};
imageObj.src = image.attrs.src;
})();
}
stage.draw();
page_num =num //update page
}
}
function addCircletoCurrentPage()
{
var rand = Math.floor(3+(Math.random()*10));
var obj = new Kinetic.Circle({
x: rand*16, y: rand*16, name: "image",
radius: rand,
fill: 'red'
})
var imagelayer = current_page;
imagelayer.add(obj);
imagelayer.draw();
}

How do i clear the canvas for another animation?

Was wondering if any of you could help me
I've got to create an advert and want to have text flying in from the right.
So far I've managed to do it, but cannot clear the canvas for reanimation.
Here's my code so far:
<canvas id="canvas" width="800" height="200">Your browser doesn't support the canvas. <br/> I appologise for any inconvenience.
</canvas>
<script type = "text/javascript">
//Set up the canvas
var the_canvas_element = document.getElementById("canvas");
var the_canvas = the_canvas_element.getContext("2d");
//Start Variables
var x = 800;
//Start animation timing
var int = setInterval(draw,10);
var ljmulogo = new Image ();
{
ljmulogo.src = 'C:\Users\Chad\Documents\My Web Sites\CWK 2\Part A\Images\ljmu_logo.png'
setInterval (draw,100)
}
function draw()
{
//Clear canvas
the_canvas.clearRect(0,0,800,200);
//Draw text
the_canvas.fillStyle = "#000000";
the_canvas.font = "24pt arial";
the_canvas.fillText("Welcome to:",x,30);
the_canvas.fillStyle = "#000000";
the_canvas.font = "bold 24pt arial";
the_canvas.fillText("Liverpool John Moores University",x,70);
the_canvas.fillStyle = "#000000";
the_canvas.font = "32pt arial";
the_canvas.fillText("Computer Forensics",x,150);
//Check if at the end
if (x<=20)
{
//End animation
int = clearInterval(int);
}
else
{
//bring text in further
x = x -2;
}
}
</script>
Thanks a lot for any help, much appreciated :)
I'm not sure what's going on here, it doesn't look valid:
var ljmulogo = new Image ();
{
ljmulogo.src = 'C:\Users\Chad\Documents\My Web Sites\CWK 2\Part A\Images\ljmu_logo.png'
setInterval (draw,100)
}
To create a new image, try this:
//This code outside the draw loop
var ljmulogo = new Image();
ljmulogo.src = 'ljmu_logo.png';
//This code inside the draw loop
ctx.drawImage(ljmulogo, 0, 0);
(and remove the setInterval (draw,100) as you already have a setInterval + that 100 value is pretty high).