How do you force a canvas refresh in Javascript - javascript

In the code that follows the function board repaints the canvas. However the canvas is not repainted until control reverts to the user. What code sequence is required to force a refresh whenever the function is invoked?
function doRound (ctx, game, loc, move){ // move is selected human move
let go = move;
let translate = {0:-1,1:-8,2:1,3:8}
for (var i = 0; i < 2; i++) {
loc = loc+translate[go]; // calculate move
if (illegal(loc)){
let text = (i === 0) ? "You lose!" : "You win!";
document.getElementById("result").innerHTML = text;
break; // game over
}
game[loc] = true; // place move on board
board(ctx); // refresh canvas
if (i === 0){go = compMove()}; // calc computer move
}
}

You would need to wipe off your canvas before redrawing all its previous content.
you can do so by using clearRect() and pass it the size of the canvas :
ctx.clearRect(0, 0, canvas.width, canvas.height);
here is a basic example on how this can be useful
//note in javascript if an html element has an id a variable with the same name exists you can ommit to get it with document.getElementById("mycan");
let ctx=mycan.getContext("2d");
//initial rect position in x
let move = 20;
//does some heavy processing for no reason
const heavyProcessing = (arr)=>{
for(let i = 0;i<1000000;i++)
{arr.push(Math.floor(Math.random()*100)+1)}
};
//adding an eventListener who listens to user input
window.addEventListener("keydown",(e)=>{
//if(e.key=="ArrowRight){move++} increments x position of red square if the user presses the right arrow on his keyboard
move+=(e.key=="ArrowRight");
});
//call to heavyProcessing before animation begins
let ar=[];
heavyProcessing(ar);
//simple animation timer
setInterval(()=>{
//wipe off canvas before each redraw
ctx.clearRect(0,0,mycan.width,mycan.height);
//choose next rectangle color before drawing
ctx.fillStyle = "red";
//drawing a rectangle
ctx.fillRect(move,20,50,50);
//recalls the code above every frame at a rate of 60 per second
},1000/60)
<canvas id = "mycan" width=300 height=200>
</canvas>

Related

How can I update variables for many different images in a Javascript canvas?

I am new to Javascript in a canvas, and javascript in general.
Basically, what I am trying to do is:
have many different randomly spawning fire balls (images), that all start at a fixed y value and a random x value.
They should then fall at a speed of a variable.
I got the random x position, and the fixed y value, but I don't know how I can have a separate falling variable for each new image that tracks it's speed, such as applying this to each individual fire ball:
fireBallSpeed = 10;
fireBallTop = 0;
if (randomSpawn == 1){
fireBallTop += fireBallSpeed;
fireBall.style.top = fireBallTop + 'px';
ctx.drawImage(fireBall, randomX, fireBallTop, 50, 50);
}
You should save each fireball into an array and loop over the array updating each one's position.
let fireballs = []
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
function gameLoop() {
// Clear the canvas
ctx.clearRect(0, 0, 800, 300)
// Loop over each fireball and update their positon
fireballs.forEach(fireball => {
ctx.fillRect(fireball.x, fireball.y, 10, 10)
// Set the new position for the next frame using the speed
fireball.y += fireball.speed
})
// Request another frame
requestAnimationFrame(gameLoop)
}
function gameInit() {
// Create 5 fireballs
for (let i = 0; i < 5; i++) {
fireballs.push({ x: Math.random() * 280, y: 5, speed: Math.random() * 2 })
}
}
// Init the game
gameInit()
// Start the game loop
// This function should only be used for the drawing portion
// and setInterval should be used for the logic
// For simplicity I am going to use this for both
requestAnimationFrame(gameLoop)
<canvas width="800" height="300"></canvas>

Creating a scratch card in EaselJS

I've been trying to develop a scratch card in EaselJS.
So far, I've managed to get a Shape instance above a Bitmap one and enabled erasing it with click and drag events, so the image below becomes visible.
I've used the updateCache() with the compositeOperation approach and it was easy enough, but here is my issue:
How can I find out how much the user has already erased from the Shape instance, so I can setup a callback function when, say, 90% of the image below is visible?
Here is a functioning example of what I'm pursuing: http://codecanyon.net/item/html5-scratch-card/full_screen_preview/8721110?ref=jqueryrain&ref=jqueryrain&clickthrough_id=471288428&redirect_back=true
This is my code so far:
function Lottery(stageId) {
this.Stage_constructor(stageId);
var self = this;
var isDrawing = false;
var x, y;
this.autoClear = true;
this.enableMouseOver();
self.on("stagemousedown", startDrawing);
self.on("stagemouseup", stopDrawing);
self.on("stagemousemove", draw);
var rectWidth = self.canvas.width;
var rectHeight = self.canvas.height;
// Image
var background = new createjs.Bitmap("http://www.taxjusticeblog.org/lottery.jpg");
self.addChild(background);
// Layer above image
var overlay = new createjs.Shape();
overlay.graphics
.f("#55BB55")
.r(0, 0, rectWidth, rectHeight);
self.addChild(overlay);
overlay.cache(0, 0, self.canvas.width, self.canvas.height);
// Cursor
self.brush = new createjs.Shape();
self.brush.graphics
.f("#DD1111")
.dc(0, 0, 5);
self.brush.cache(-10, -10, 25, 25);
self.cursor = "none";
self.addChild(self.brush);
function startDrawing(evt) {
x = evt.stageX-0.001;
y = evt.stageY-0.001;
isDrawing = true;
draw(evt);
};
function stopDrawing() {
isDrawing = false;
};
function draw(evt) {
self.brush.x = self.mouseX;
self.brush.y = self.mouseY;
if (!isDrawing) {
self.update();
return;
}
overlay.graphics.clear();
// Eraser line
overlay.graphics
.ss(15, 1)
.s("rgba(30,30,30,1)")
.mt(x, y)
.lt(evt.stageX, evt.stageY);
overlay.updateCache("destination-out");
x = evt.stageX;
y = evt.stageY;
self.update();
$rootScope.$broadcast("LotteryChangeEvent");
};
}
Any ideas?
That's a tricky one, regardless of the language. The naive solution would simply be to track the length of the paths the user "draws" within the active area, and then reveal when they scratch long enough. That's obviously not very accurate, but is fairly simple and might be good enough.
The more accurate approach would be to get the pixel data of the cacheCanvas, then check the alpha value of each pixel to get an idea of how many pixels are transparent (have low alpha). You could optimize this significantly by only checking every N pixel (ex. every 5th pixel in every 5th row would run 25X faster).

Given an input to draw a rectangle canvas with selected background

I want to make real time rendered rectange which is filled by texture of material you choose. I want to use HTML5 <canvas> and jQuery. In this fiddle http://jsfiddle.net/1fhhtev7/ is how it should looks like. when you add "a" and "b" side it will generate proportional rectangle and when you click on material it will fill the canvas by texture of material.
Here is how the preview should looks like in final:
Can you help me with this?
You should capture the values of the input elements and then set width and height properties accordingly.
$('input[name=sideA],input[name=sideB]').on('input',yourFunction())
Then you should make a function with a loop where you draw the image.
var imgPosX = 0;
var imgPosY = 0;
var img = document.getElementById(YOURIMAGEID);
while(imgPosX < canvas.width){
ctx.drawImage(img,imgPosX,imgPosY);
if(imgPosX === canvas.width){
imgPosY += 50;
imgPosX += 0;
}
if(imgPosY === canvas.height){
break;
}
imgPosX +=50;
}
you call this function in your eventlistener.
Try this,
function draw(){
$('img').click(function(){
var $img = $(this).outerHTML;
ctx.clearRect(0,0,canvas.width,canvas.height);
var pat = ctx.createPattern(this, 'repeat');
ctx.rect(40,40,width,height);
ctx.fillStyle = pat;
ctx.fill();
});
}
Working Demo
Canvas draw only to change number not keyup function

JavaScript iteration

I want to use JavaScript to draw a series of images onto an HTML5 canvas. I have the following while loop, which I had hoped would draw all of the images to the canvas, however, it is currently only drawing the first one:
function drawLevelOneElements(){
/*First, clear the canvas */
context.clearRect(0, 0, myGameCanvas.width, myGameCanvas.height);
/*This line clears all of the elements that were previously drawn on the canvas. */
/*Then redraw the game elements */
drawGameElements();
/*Draw the elements needed for level 1 (26/04/2012) */
var fileName = 1;
var imagePositionX = 20;
var imagePositionY = 30;
while(fileName < 11){
/*Create an array of images here, move to next element of array on each iteration */
var numbers = new Array();
numbers[0] = "1.png"
numbers[1] = "2.png"
numbers[3] = "3.png"
numbers[4] = "4.png"
numbers[5] = "5.png"
image.src = fileName+".png";
image.src = numbers[0];
image.onload = function(){
context.drawImage(image, imagePositionX, imagePositionY, 50, 50);
}
fileName = fileName+1;
imageY = imageY+20;
console.dir(fileName); /* displays in the console- helpful for debugging */
}
To talk through what I had hoped this function would do:
Load each of the images into a different element of the array (so 1.png would be in numbers[0], 2.png in numbers[1], etc. )
It would then take the global variable 'image', and assign its source to the contents of numbers[0]
Then draw that image at the specified position on the canvas.
Then increment the value of the variable fileName by 1, giving it a value of '2'
Next it would increment the value of the Y co-ordinate where it will draw the image on the canvas by 20- moving the position of the image to be drawn down by 20 pixels
After that it would go back to the start of the loop and draw the next image (2.png) on the canvas in a position that is 20 pixels below the position of the first image that was drawn.
It should continue doing this while the value of the variable 'fileName' is less than 11, i.e. it should draw 10 images each new one below the last one that was drawn.
However, for some reason, my function only draws the first image. Could someone point out what I'm doing wrong, and how I could correct this?
Thanks very much.
Edited and commented some points of your code.
The most effective change was at imageY = imageY+20; that was edited to use imagePositionY variable.
function drawLevelOneElements() {
/*First, clear the canvas */
context.clearRect(0, 0, myGameCanvas.width, myGameCanvas.height);
/*This line clears all of the elements that were previously drawn on the canvas. */
/*Then redraw the game elements */
drawGameElements();
/*Draw the elements needed for level 1 (26/04/2012) */
var fileName = 1;
var imagePositionX = 20;
var imagePositionY = 30;
while(fileName < 11){
/*Create an array of images here, move to next element of array on each iteration */
var numbers = new Array();
/* what is not used is not necessary :)
numbers[0] = "1.png"
numbers[1] = "2.png"
numbers[3] = "3.png"
numbers[4] = "4.png"
numbers[5] = "5.png"*/
image.src = fileName+".png";
// image.src = numbers[0];
image.onload = function(){
context.drawImage(image, imagePositionX, imagePositionY, 50, 50);
}
fileName = fileName+1;
imagePositionY = imagePositionY+20; //before: imageY = imageY+20;
console.dir(fileName); /* displays in the console- helpful for debugging */
}
If you take the drawImg stuff and shove it in its own function you can clean this up a bit :) Now we've yanked the async stuff out of the loop, so the image variable doesn't get over-written each time you loop. You're also using a for loop now, which to me is clearer to understand.
function drawLevelOneElements() {
// your stuff
for (var i = 0; i > 5; i++) {
drawImg(ctx, i, x, y);
// update x or y and whatever else
}
}
// put all your image drawing stuff here
function drawImg(ctx, i, x, y) {
var img = new Image();
img.src = i + ".png";
img.onload = function(){
ctx.drawImage(img, x, y, 50, 50);
}
}

Canvas in HTML5: Removing Previous Rectangle

I've been messing around with the canvas element in html5, and this is what I've got after a bit of experimenting
function canvasMove(e) {
var canvas = document.getElementById('game');
if(canvas.getContext) {
var draw = canvas.getContext('2d');
draw.fillStyle = 'rgba(0,0,0,0.5)';
draw.fillRect('10', '10', '100', '100');
var code;
if (!e) var e = window.event;
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
var character = String.fromCharCode(code);
if(character == '&') { draw.translate(0, -10); }
if(character == '(') { draw.translate(0, 10); }
if(character == '%') { draw.translate(-10, 0); }
if(character == "'") { draw.translate(10, 0); }
}
}
What it does is moves the rectangle whenever you press the arrow keys [Arrow keys were showing up as &, (, % and ', not sure if this is the same for everyone but it's just an experiment]. Anyway, I can move the rectangle about but it leaves a sort of residue, as in it doesn't delete it's previous form, so what I get is a very basic etch-n'-sketch using a very thick brush.
What I want to do is be able to delete the previous form of the rectangle so that only the new translated version is left.
On top of that I'd like to know how to make it move say, horizontally, by pressing maybe left and up simultaneously. I am aware my code probably isn't very versatile, but any help us much appreciated.
Thanks :)
I made an example for you. Your HTML has to call my init() function. I used:
<body onLoad="init()">
Let me know if you have any problems with it
var canvas;
var draw;
var WIDTH;
var HEIGHT;
var x = 10;
var y = 10;
// in my html I have <body onLoad="init()">
function init() {
canvas = document.getElementById('game');
HEIGHT = canvas.height;
WIDTH = canvas.width;
draw = canvas.getContext('2d');
// every 30 milliseconds we redraw EVERYTHING.
setInterval(redraw, 30);
// canvas.keydown = canvasMove;
document.onkeydown = canvasMove;
}
//wipes the canvas context
function clear(c) {
c.clearRect(0, 0, WIDTH, HEIGHT);
}
//clears the canvas and draws the rectangle at the appropriate location
function redraw() {
clear(draw);
draw.fillStyle = 'rgba(0,0,0,0.5)';
draw.fillRect(x, y, '100', '100');
}
function canvasMove(e) {
if(e.keyCode == '38') { y -= 1; }
if(e.keyCode == '40') { y += 1; }
if(e.keyCode == '37') { x -= 1; }
if(e.keyCode == "39") { x += 1; }
}
To answer the first question here is a function to clear a canvas. A is a reference to canvas element though you could edit what parameters it takes. You would need to call this every time before you draw a new rectangle.
function clear(a){
a.getContext('2d').clearRect(0,0,a.width,a.height);
}
I think in the second question you meant move at an angle. As far as I know that would be a little difficult because you would have record the key press and then set a timeout to see if another one was pressed within some amount of time. Then create a function to move both of those directions or just one if no other arrow keys were pressed. Right now your function would kind of work if both key were pressed, but the rectangle would jerk left and then up.
If you wanted to do this without redrawing the canvas, you can do a save on the context, set your transformation and then clear the rectangle off your screen while still remembering where that rectangle would have been drawn. Then call the translate to move the rectangle to its new position.
<!DOCTYPE html>
<body>
<button onClick="canvasMove('down');">Down</button>
<canvas id="game" style="padding-top:200px;background-color:white;"
height="300" width="300" />
</body>
<script>
const canvas = document.getElementById('game');
const draw = canvas.getContext('2d');
draw.fillStyle = 'rgba(0,0,0,0.5)';
draw.fillRect('10', '10', '100', '100');
function canvasMove(direction) {
// save the current state of the canvas and then clear the canvas,
// which removes any object on your canvas
draw.save();
draw.setTransform(1, 0, 0, 1, 0, 0);
draw.clearRect(0, 0, canvas.height, canvas.width);
// reverts canvas back to its saved state
draw.restore();
if(direction === 'down') {
draw.translate(0, 10);
draw.fillStyle = 'rgba(0,0,0,0.5)';
draw.fillRect('10', '10', '100', '100');
}
}
</script>
To get the same effect, you don't even need to do the save and restore. Context.globalCompositeOperation = "copy" will only draw the new object.
function canvasMove(direction) {
draw.globalCompositeOperation = "copy";
if(direction === 'down') {
draw.translate(0, 10);
draw.fillStyle = 'rgba(0,0,0,0.5)';
draw.fillRect('10', '10', '100', '100');
}
}

Categories