Picture moving in canvas with imperfections

  • (2 Pages)
  • +
  • 1
  • 2

20 Replies - 412 Views - Last Post: 07 December 2017 - 11:39 AM

#1 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 411
  • View blog
  • Posts: 1,319
  • Joined: 27-December 13

Picture moving in canvas with imperfections

Posted 06 December 2017 - 11:45 AM

I'm a beginner in HTML/JS. Have some routine in Python.
My aim is to build a Christmas banner using a canvas.
The code works, but with severe imperfections; picture blinks and graphical imperfections at the right/left ends.
It took me a long time to get this far, - but now I'm stuck.
Hope someone will help.

HTML:
<html><body><pre>
<canvas id="myCanvas" width="1200" height="140"></canvas>
<script src="program.js">
</script></pre></body></html>


Javascript:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

// Create gradient
var grd = ctx.createLinearGradient(0,0,0,120);
grd.addColorStop(0,"blue");
grd.addColorStop(1,"white");


class Snow{
	constructor(){
		this.yPos = 15;
		this.xPos = Math.floor(Math.random() * 1180) + 1;
		this.radius = Math.floor(Math.random() * 3) + 2;
	}
	fall(){
		this.yPos += this.radius - 1;
	}
	draw(){
		ctx.beginPath();
		ctx.arc(this.xPos,this.yPos,this.radius,0,2*Math.PI);
		ctx.fillStyle = "white";
		ctx.fill();
	}
}

var allSnow = []
var j = -20

window.setInterval(snowfall, 20);

function snowfall(){
	// Fill with gradient
	ctx.fillStyle = grd;
	ctx.fillRect(10,10,1180,100);


	allSnow.push(new Snow());
	for (i = 0; i < allSnow.length; i++){
		allSnow[i].fall();
		allSnow[i].draw();
		if (allSnow[i].yPos > 100){
			allSnow.splice(i, 1);
		}
	}

	base_image = new Image();
  	base_image.src = 'julebryg.png';
  	base_image.onload = function(){
    	ctx.drawImage(base_image, j, 10);
    	j = j + 2;
    	if (j > 1220){
    		j = -20;
    	}
    }

}


Picture file: https://imgur.com/OVuhfUS

Is This A Good Question/Topic? 0
  • +

Replies To: Picture moving in canvas with imperfections

#2 andrewsw   User is offline

  • Bouncy!
  • member icon

Reputation: 6563
  • View blog
  • Posts: 26,615
  • Joined: 12-December 12

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 12:09 PM

As you are using the same image, one thing you can do is to move these lines outside the interval-code:

	base_image = new Image(); // declare with var
  	base_image.src = 'julebryg.png';

Nor will you then need to call the load event repeatedly.

I think 20 ms is too small an interval, guessing at 40 or 50 as a minimum.

I would also consider requestAnimationFrame (although I cannot describe the technicalities nor whether an improvement is guaranteed).
Was This Post Helpful? 1
  • +
  • -

#3 ArtificialSoldier   User is online

  • D.I.C Lover
  • member icon

Reputation: 2041
  • View blog
  • Posts: 6,264
  • Joined: 15-January 14

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 12:12 PM

You're asking it to do quite a bit 50 times every second. Is it necessary to load the same image or draw the same gradient 50 times per second?
Was This Post Helpful? 0
  • +
  • -

#4 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 411
  • View blog
  • Posts: 1,319
  • Joined: 27-December 13

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 12:54 PM

If I move the two lines mentioned by 'anderwsw' to outside the function (after line 28 in my code), - then the picture doesn't show at all.
EDIT: Should I declare it somehow?

Running the code without the picture (only snowfall) at 20 ms cycle time is flawless.
Reducing to 50 ms cycle time did no observable difference to the picture problems.

I agree: It should not be necessary to load the picture more than once - only, I can't get it to work.
I think, however, that re-doing the gradient is necessary in order to clear the previous picture.

This post has been edited by DK3250: 06 December 2017 - 12:58 PM

Was This Post Helpful? 0
  • +
  • -

#5 andrewsw   User is offline

  • Bouncy!
  • member icon

Reputation: 6563
  • View blog
  • Posts: 26,615
  • Joined: 12-December 12

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:04 PM

Having moved the two lines, and it doesn't now work, are there errors in your browser's console?

I included a comment suggesting that you declare using var.
Was This Post Helpful? 0
  • +
  • -

#6 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 411
  • View blog
  • Posts: 1,319
  • Joined: 27-December 13

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:25 PM

I've tried many variations, - no success yet.
If I modify the code to
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var allSnow = [];
var j = -20;
var base_image = new Image();
base_image.src = 'julebryg.png';


// Create gradient
var grd = ctx.createLinearGradient(0,0,0,120);
grd.addColorStop(0,"blue");
grd.addColorStop(1,"white");


class Snow{
	constructor(){
		this.yPos = 15;
		this.xPos = Math.floor(Math.random() * 1180) + 1;
		this.radius = Math.floor(Math.random() * 3) + 2;
	}
	fall(){
		this.yPos += this.radius - 1;
	}
	draw(){
		ctx.beginPath();
		ctx.arc(this.xPos,this.yPos,this.radius,0,2*Math.PI);
		ctx.fillStyle = "white";
		ctx.fill();
	}
}

window.setInterval(snowfall, 40);

function snowfall(base_image){
	// Fill with gradient
	ctx.fillStyle = grd;
	ctx.fillRect(10,10,1180,100);


	allSnow.push(new Snow());
	for (i = 0; i < allSnow.length; i++){
		allSnow[i].fall();
		allSnow[i].draw();
		if (allSnow[i].yPos > 100){
			allSnow.splice(i, 1);
		}
	}

	base_image.onload = function(){
    	ctx.drawImage(base_image, j, 10);
    	j = j + 2;
    	if (j > 1220){
    		j = -20;
    	}
    }

}


The console shows:
program.js:49 Uncaught TypeError: Cannot set property 'onload' of undefined
    at snowfall (program.js:49)


EDIT: Ups, that was due to a test in line 34 (including an argument to the function). Without this argument, I get no error in the console (and no moving picture, only snowfall).

This post has been edited by DK3250: 06 December 2017 - 01:29 PM

Was This Post Helpful? 0
  • +
  • -

#7 andrewsw   User is offline

  • Bouncy!
  • member icon

Reputation: 6563
  • View blog
  • Posts: 26,615
  • Joined: 12-December 12

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:32 PM

That's because base_image is coming from an argument, function snowfall(base_image){, which you haven't provided.

As you have declared base_image globally then you don't need the argument.
Was This Post Helpful? 0
  • +
  • -

#8 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 411
  • View blog
  • Posts: 1,319
  • Joined: 27-December 13

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:34 PM

As added in the edit above (crossing your answer): Without the argument: No error and no picture...
Was This Post Helpful? 0
  • +
  • -

#9 andrewsw   User is offline

  • Bouncy!
  • member icon

Reputation: 6563
  • View blog
  • Posts: 26,615
  • Joined: 12-December 12

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:36 PM

You have to wait for the image to load initially:

base_image.onload = function () {
    window.setInterval(snowfall, 40);
};

Was This Post Helpful? 1
  • +
  • -

#10 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 411
  • View blog
  • Posts: 1,319
  • Joined: 27-December 13

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:43 PM

Success!!!
Can't say I fully understand this, - but will eventually. :bigsmile:/>

Just for the record, the current code:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var allSnow = [];
var j = -20;
var base_image = new Image();
base_image.src = 'julebryg.png';


// Create gradient
var grd = ctx.createLinearGradient(0,0,0,120);
grd.addColorStop(0,"blue");
grd.addColorStop(1,"white");


class Snow{
	constructor(){
		this.yPos = 15;
		this.xPos = Math.floor(Math.random() * 1180) + 1;
		this.radius = Math.floor(Math.random() * 3) + 2;
	}
	fall(){
		this.yPos += this.radius - 1;
	}
	draw(){
		ctx.beginPath();
		ctx.arc(this.xPos,this.yPos,this.radius,0,2*Math.PI);
		ctx.fillStyle = "white";
		ctx.fill();
	}
}

window.setInterval(snowfall, 40);

function snowfall(){
	// Fill with gradient
	ctx.fillStyle = grd;
	ctx.fillRect(10,10,1180,100);


	allSnow.push(new Snow());
	for (i = 0; i < allSnow.length; i++){
		allSnow[i].fall();
		allSnow[i].draw();
		if (allSnow[i].yPos > 100){
			allSnow.splice(i, 1);
		}
	}

	base_image.onload = function(){
		window.setInterval(snowfall, 40);
	}
    ctx.drawImage(base_image, j, 10);
    j = j + 2;
    if (j > 1220){
    	j = -20;
    }
}

This post has been edited by DK3250: 06 December 2017 - 01:44 PM

Was This Post Helpful? 0
  • +
  • -

#11 andrewsw   User is offline

  • Bouncy!
  • member icon

Reputation: 6563
  • View blog
  • Posts: 26,615
  • Joined: 12-December 12

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:55 PM

Do you need to splice the array, might it work just as well if you just skipped the fallen snow:

    for (i = 0; i < allSnow.length; i++){
        if (allSnow[i].yPos <= 100){
            allSnow[i].fall();
            allSnow[i].draw();
            //allSnow.splice(i, 1);
        }
    }

The difference is probably insignificant, I'm just wary of such an operation within a loop.
Was This Post Helpful? 1
  • +
  • -

#12 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 411
  • View blog
  • Posts: 1,319
  • Joined: 27-December 13

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 01:59 PM

Early in the development of this program, I noticed a gradually slow-down of the snowfall (this was befor I added the moving picture).
Maybe it had a different origin, but the slowdown disappeared when I introduced the splice.
I'll try again...
Was This Post Helpful? 0
  • +
  • -

#13 andrewsw   User is offline

  • Bouncy!
  • member icon

Reputation: 6563
  • View blog
  • Posts: 26,615
  • Joined: 12-December 12

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 02:00 PM

The setting of base_image.onload is still within the snowfall function. I meant to call it once, outside the function.
Was This Post Helpful? 0
  • +
  • -

#14 baavgai   User is offline

  • Dreaming Coder
  • member icon


Reputation: 7197
  • View blog
  • Posts: 15,004
  • Joined: 16-October 07

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 02:00 PM

Your edge problem is just a bad fill. Also, globals bad.

I fiddled with this a bit. Though this is probably the most important change for you:
// ctx.fillRect(10, 10, 1180, 100);
ctx.fillRect(0, 10, 1200, 100);



And, my play:
class Snow {
  constructor(ctx) {
    this.ctx = ctx;
    this.yPos = 15;
    this.xPos = Math.floor(Math.random() * 1180) + 1;
    this.radius = Math.floor(Math.random() * 3) + 2;
  }
  fall() {
    this.yPos += this.radius - 1;
  }
  draw() {
    this.ctx.beginPath();
    this.ctx.arc(this.xPos, this.yPos, this.radius, 0, 2 * Math.PI);
    this.ctx.fillStyle = "white";
    this.ctx.fill();
  }
}

// capture all this in a nice package
function createAllSnow(ctx) {
  var items = [];
  return function() {
    items.push(new Snow(ctx));
    for (var i = 0; i < items.length; i++) {
      var item = items[i];
      item.fall();
      item.draw();
      if (item.yPos > 100) {
        items.splice(i, 1);
      }
    }
  }
}


function createClear(ctx) {
  var grd = ctx.createLinearGradient(0, 0, 0, 120);
  grd.addColorStop(0, "blue");
  grd.addColorStop(1, "white");
  return function() {
    ctx.fillStyle = grd;
    // ctx.fillRect(10, 10, 1180, 100);
    ctx.fillRect(0, 10, 1200, 100);
  }
}

// the big goal here is to only load that image once
function createMovingThingy(ctx, cb) {
  const initX = -20;
  const resetX = 1000;
  const velocity = 2;
  const y = 10;
  var x = initX;
  var img = new Image();
  img.src = 'julebryg.png';
  img.onload = function () { cb(doit);  }
  function doit() {
    ctx.drawImage(img, x, y);
    x += velocity;
    if (x > resetX) {
      x = initX;
    }
  }
}

(function() {
  const speed = 20;
  const c = document.getElementById("myCanvas");
  const ctx = c.getContext("2d");
  const clear = createClear(ctx);
  const allSnow = createAllSnow(ctx);
  // this is a little trickier, we're waiting for that image to load
  createMovingThingy(ctx, function(mover) {
    // loaded, and we're off
    window.setInterval(function() {
      clear();
      allSnow();
      mover();
    }, speed);
  })
})();



Hope this helps.
Was This Post Helpful? 1
  • +
  • -

#15 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 411
  • View blog
  • Posts: 1,319
  • Joined: 27-December 13

Re: Picture moving in canvas with imperfections

Posted 06 December 2017 - 02:06 PM

Yeah, with your if statement, it seem that the dormant instance get garbage collected.
Had to change the limit to if (allSnow[i].yPos <= 105) to avoid a build-up of imperfections to the bottom line (accumulation of non-moving snowballs).
More suggestions? I learn a lot...
Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2