How to use constructors and images in javascript? - javascript

I have the following segment of code intented to draw an image onto the canvas. This code works:
var ctx = canvas.getContext('2d');
class Rectangle {
constructor(x, y, scale, source) {
this.scale = scale;
this.source = source;
this.x = x;
this.y = y;
}
update() {
var sprite = new Image();
sprite.src = this.source;
sprite.onload = function () {
ctx.drawImage(sprite, this.x, this.y, sprite.width/3, sprite.height/3);
}
}
}
const rect = new Rectangle(0, 0, 1, 'circle.jpeg');
rect.update();
But, when I replace this line:
ctx.drawImage(sprite, this.x, this.y, sprite.width/3, sprite.height/3);
with this:
ctx.drawImage(sprite, this.x, this.y, sprite.width/this.scale, sprite.height/this.scale);
the canvas doesn't show anything at all. All this should be doing is replacing the 3 from before with the value from the constructor (which I set to 1 when I create a new instance). Why isn't it drawing anything?

I would move the sprite to the constructor that way when we call the update we don't have to create a new image, the idea behind that is the update function could be called multiple times efficiently.
See this sample below:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
class Rectangle {
constructor(x, y, scale, source) {
this.scale = scale;
this.x = x;
this.y = y;
this.sprite = new Image();
this.sprite.src = source;
this.sprite.onload = () => {
this.update()
};
}
update() {
if (this.sprite) {
ctx.drawImage(this.sprite, this.x, this.y, this.sprite.width / this.scale, this.sprite.height / this.scale);
}
}
}
const rect = new Rectangle(0, 0, 3, 'http://i.stack.imgur.com/UFBxY.png');
canvas.addEventListener("click", () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
rect.x += 5;
rect.update();
});
<canvas id="canvas"></canvas>
You can see that as suggested in the comments by #skara9 I'm using the () => instead of function() and I'm calling the update there.
On this sample I also added canvas.addEventListener("click" that shows how when the user clicks on the canvas the drawing move a little to the right, the logic is simple we increase the value of rect.x and then we call the rect.update(); that will draw the image on the new location.

Related

Canvas drawImage() function not working on pressing a specific key in JavaScript?

I am trying to create a 2D game in JavaScript, I want the BackgroundRight() BackgroundLeft() BackgroundStop() functions to run on clicking different keys, like on click A the background should go Right, and my character go to left, but when I press A or any key the the Background Functions are not drawing anything, but when I console.log() some random text in the Background functions, it normally prints in the console, it shows that its working but still it's not drawing anything, and if I run any Background Function Specifically(calling the function any where in the code like how we normally call them) then its working totally fine but not on key press. Please Help me out I am struggling on this from 1 week 😭
The Main code is the Layer class and the document.onKeyDown Function at the last of the code. please help me out.
/** #type {HTMLCanvas Element} */
// Collecting Elements from HTML code
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
// Giving Height Width to the Canvas
const CANVAS_HEIGHT = canvas.height = 324;
const CANVAS_WIDTH = canvas.width = 576;
// GameSpeed
let gameSpeed = 2;
let gameFrame = 0;
staggeredFrame = 8;
// Scrolling Background Images declaration
const backgroundLayer1 = new Image();
backgroundLayer1.src = 'IMG/2 Background/1.png';
const backgroundLayer2 = new Image();
backgroundLayer2.src = 'IMG/2 Background/2.png';
const backgroundLayer3 = new Image();
backgroundLayer3.src = 'IMG/2 Background/3.png';
const backgroundLayer4 = new Image();
backgroundLayer4.src = 'IMG/2 Background/4.png';
const backgroundLayer5 = new Image();
backgroundLayer5.src = 'IMG/2 Background/5.png';
const backgroundLayer6 = new Image();
backgroundLayer6.src = 'IMG/layer_02_1920 x 1080.png';
// Scrolling Background Class
class Layer { // The Class Which Have the Background functions
constructor(image, speedModifier) {
this.x = 0;
this.y = 0;
this.height = 324;
this.width = 576;
this.image = image;
this.speedModifier = speedModifier;
}
// To Move Background Left
backgroundLeft() {// on going through this function it should draw something but not drawing.
this.speed = this.speedModifier * gameSpeed;
if (this.x <= -this.width) this.x = 0;
else this.x -= this.speed;
ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
ctx.drawImage(this.image, this.x + this.width, this.y, this.width, this.height);
// at this above line it should draw but its not working, but if I console.log some text after
// this line it will be printed out but nothing drawing
}
// To Move Background To Right
backgroundRight() { // same as the backgroundLeft() function it's not working
this.speed = this.speedModifier * gameSpeed;
if (this.x >= this.width) this.x = 0;
else this.x += this.speed;
this.draw();
}
// To Stop Background
backgroundStop() { // same as others this is also not working
this.draw();
}
// To draw Background Images
draw() {
ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
ctx.drawImage(this.image, this.x - this.width, this.y, this.width, this.height);
}
}
// Scrolling Background objects
const layer1 = new Layer(backgroundLayer1, 0.2);
const layer2 = new Layer(backgroundLayer2, 0.3);
const layer3 = new Layer(backgroundLayer3, 0.4);
const layer4 = new Layer(backgroundLayer4, 0.6);
const layer5 = new Layer(backgroundLayer5, 0.8);
const layer6 = new Layer(backgroundLayer6, 0.8);
layer6.height = 420;
backLayer = [layer1, layer2, layer3, layer4, layer5, layer6];
// Player Class
class Player {
constructor() {
this.x = 0;
this.y = CANVAS_HEIGHT - 60;
this.height = 44;
this.width = 44;
this.image = new Image();
this.image.src = 'IMG/1 Characters/1 Biker/Walk1.png';
this.playerHeight = 48;
this.playerWidth = 48;
this.frameX = 0;
this.frameY = 0;
this.frames = 6;
}
// Update Class For Player
playerUpdate() {
this.position = Math.floor(gameFrame / staggeredFrame) % this.frames;
this.frameX = this.position * this.playerWidth;
ctx.drawImage(this.image, this.frameX, this.frameY, this.playerHeight, this.playerWidth, this.x, this.y, this.width, this.height);
}
}
// Player Object
const player = new Player();
player.image.src = 'IMG/1 Characters/1 Biker/Run_attack.png';
// Main Function
function animate() {
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
drawBackground();
player.playerUpdate();
gameFrame++;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
// Drawing Background
function drawBackground() {
backLayer.forEach(element => {
element.backgroundRight();
});
}
document.onkeydown = (e) => { // here it should work on key press but it's not working.
const keyPress = e.keyCode;
if (keyPress === 65) {
console.log(e.keyCode, "A pressed"); // it's printing in console and showing key Pressed
backLayer.forEach((layer, i) => { // but still its not drawing anything
layer.backgroundLeft();
console.log("background should run to left")
})
}
}

How to make moving canvas objects with images in HTML with JavaScript

So I'm working on a project where I have a canvas filled with moving balls. Its an extension/inspired by this codepen project : https://codepen.io/zetyler/pen/LergVR .
It essentially runs with the same physics in place as the codepen, but now I'm trying to draw the moving and colliding balls with images instead of random colors.
The original draw() method looks like this :
var pen = canvas.getContext('2d');
const W = canvas.width;
const H = canvas.height;
var numBalls = 30;
var grav = [0,-0.1];
function Ball(x,y,dx,dy,r) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.r = r;
this.color = 'hsl('+(Math.random()*360)+',90%,50%)';
this.draw = function() {
pen.fillStyle = this.color;
pen.beginPath();
pen.arc(this.x,this.y,this.r,0,2*Math.PI);
pen.fill();
}
I'm refactoring the draw method to try and work with an image instead of a random color fill, and so far I can't even get an image to show up. Currently my draw method looks like this:
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
let numBalls = 1;
let grav = [0,-0.1];
//try feeding the ball function an object
//and destructuring the inputs
class Ball {
constructor (x, y, dx, dy, r) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.r = r;
//probably won't need this
//this.color = 'hsl(' + (Math.random() * 360) + ', 90%, 50%)';
}
draw() {
var thumbImg = document.createElement('img');
thumbImg.src = './svgs/javascriptIcon.svg';
thumbImg.onload = function() {
context.save();
context.beginPath();
context.arc(25, 25, 25, 0, Math.PI * 2, true);
context.closePath();
context.clip();
context.drawImage(thumbImg, 0, 0, 50, 50);
context.beginPath();
context.arc(0, 0, 25, 0, Math.PI * 2, true);
context.clip();
context.closePath();
context.restore();
};
}
It's been so long since I've used the html canvas. I can't figure out what I'm doing wrong. I thought I would at least be able to get the image to show up, but no such luck.
Thanks for checking it out! Please let me know what you think.
I am not sure why you are using ctx.clip,
but if you just want to replace the coloured balls with images try this in your draw method
this.draw = function() {
// make sure the img is loaded
//pen.fillStyle = this.color;
pen.beginPath();
pen.arc(this.x,this.y,this.r,0,2*Math.PI);
pen.drawImage(img,this.x, this.y,this.r, this.r)
pen.fill();
}
after that play with IMG x and y positions for example pen.drawImage(img,this.x + somefactor, this.y - somefactor,this.r + somefactor, this.r + somefatcor)
just to make sure that img is perfectly cover the coloured ball so it behavies just like it

Safari drawing images not working properly

I am trying to make a game, where I draw images to a canvas. Here is a small code snippet:
class Sprite {
constructor(x, y, width, height, src, xDirection, yDirection) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.src = src;
this.xDirection = xDirection;
this.yDirection = yDirection;
}
update() {
var image = new Image();
image.src = this.src;
context.imageSmoothingEnabled = false;
context.drawImage(image, this.x, this.y, this.width, this.height);
this.x += this.xDirection;
this.y += this.yDirection;
}
}
const player = new Sprite(0, 0, 100, 100, "Images/Game/sprite-right.png", 0, 0);
const mainInterval = setInterval(() => {
player.draw();
}, 1);
This is only a small code snippet and I am also drawing many more images to the screen in the main interval. In Google Chrome and in Firefox, this works perfectly, as all of the images are correctly drawn to the screen. However, in Safari, the image is not drawn. I tried changing the 'update' method to this...
update() {
var image = new Image();
image.src = this.src;
image.onload = () => {
context.imageSmoothingEnabled = false;
context.drawImage(image, this.x, this.y, this.width, this.height);
this.x += this.xDirection;
this.y += this.yDirection;
};
}
...this works, but since I am drawing lots of things to the screen in the main interval, all of the images appear to sort of 'flash' and flicker. I need help with coming up with a way of drawing lots of images to the screen, without them flickering (bearing in mind that the method at the top only doesn't work in Safari, as far as I know). Thanks very much.
PrimeCubed
Not exactly sure why it isn't working on Safari but here are a few things that can be improved:
It's better to load all the images before you actually start drawing them.
You should use requestAnimationFrame for drawing frames.
Call the draw function only when all the resources have loaded. window.onload = draw;
Here is a snippet with these changes and this works on Safari too.
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
var image = new Image();
image.src = "https://placekitten.com/100/100";
class Sprite {
constructor(x, y, width, height, image, xDirection, yDirection) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.image = image;
this.xDirection = xDirection;
this.yDirection = yDirection;
}
update() {
context.imageSmoothingEnabled = false;
context.drawImage(this.image, this.x, this.y, this.width, this.height);
this.x += this.xDirection;
this.y += this.yDirection;
}
}
const player = new Sprite(0, 0, 100, 100, image, 0, 0);
function draw() {
player.update();
requestAnimationFrame(draw);
}
window.onload = draw;
<canvas />

Rectangle is not moving

I am trying to code a rectangle moving but it is not working and I do not understand why. I created a class for the rectangle and gave the parameters value. Then I drew the rectangle. I am trying to add the value of 5 to the rectangle's x but nothing is happening.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var width = 50;
class Rectangle{
constructor(x, y){
this.x = x;
this.y = y;
}
draw(){
ctx.beginPath();
ctx.rect(this.x, this.y, width, height);
ctx.fillStyle = "0095DD";
ctx.fill();
ctx.closePath();
}
}
function movingRectangle(){
var rect = new Rectangle(canvas.width/2 - width/2, 300);
rect.draw();
rect.x += 5;
}
function draw(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
movingRectangle();
}
var interval = setInterval(draw, 10);
You're recreating rect on each movingRectangle() call, effectively resetting its position to the initial value.
Move this:
var rect = new Rectangle(canvas.width/2 - width/2, 300);
Out of the movingRectangle() function - you only need to create the Rectangle instance once.
I managed to do a short example, based in your code, and leaving the Rectangle constructor outside:
function movingRectangle(){
let x = rect ? rect.x + 5 : canvas.width/2 - width/2;
if (!rect)
rect = new Rectangle(x, 300);
rect.x = x;
rect.draw();
}
https://jsfiddle.net/nicoavn/vc254q0a/4/

How to use Prototypal Pattern to make an html5 rectangle for reuse

I'm trying to understand Prototypal Inheritance using the Prototypal pattern by making a rectangle object and an instance of the rectangle. Seems easy, but I'm not getting it. The RectanglePrototype's method is not drawing the rectangle onto the canvas. If I use the same function as the method it works. Where am I going wrong? Also, I understand that I will need to make an initialization function, but I'm thinking I can do that later after I get the first basic steps down.
javascript:
window.onload = function () {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var RectanglePrototype = {
// Properties
x: 0,
y: 0,
width: 100,
height: 100,
color: "white",
// Method
get:function (x, y, width, height, color) {
context.translate(0 , 0);
context.beginPath();
context.rect(x, y, width, height);
context.fillStyle = color;
context.fill();
return this.x, this.y, this.width, this.height, this.color;
}
};
console.log(RectanglePrototype.get);
// Instance of RectanglePrototype
var rectangle1 = Object.create(RectanglePrototype);
rectangle1.x = 200;
rectangle1.y = 100;
rectangle1.width = 300;
rectangle1.height = 150;
rectangle1.color = '#DBE89B';
// Draw Rectangle Function
function rect(x, y, width, height, color) {
context.translate(0 , 0);
context.beginPath();
context.rect(x, y, width, height); // yLoc-canvas.height = -300
context.fillStyle = color;
context.fill();
};
rect(0, 450, 50, 50, '#F7F694');
}
</script>
Prototypes are extensions of objects that result from a constructor. Method lookups go through the object properties before looking into prototype.
I proper JS design, you would only add the non-function properties in your constructor.
//Your constructor
function Rectangle(){
// Properties
this.x = 0;
this.y = 0;
this.width = 100;
this.height = 100;
this.color = 'red';
}
And then put the methods in your prototype:
//I prefer the term 'draw'
Rectangle.prototype.draw = function(ctx){
ctx.save();
ctx.beginPath();
ctx.rect(this.x, this.y, this.width, this.height);
ctx.fillStyle = this.color;
ctx.fill();
ctx.restore();
};
Then, to use in your project:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
//List of your shapes to draw on the canvas
var shapes = [];
//Instance of Rectangle
var rectangle1 = new Rectangle();
rectangle1.x = 200;
rectangle1.y = 100;
rectangle1.width = 300;
rectangle1.height = 150;
rectangle1.color = '#DBE89B';
shapes.push(rectangle1);
//Draw your shapes
function draw(){
window.requestAnimationFrame(draw); //See MDN for proper usage, but always request next fram at the start of your draw loop!
for(var i = 0; i<shapes.length; i++){
shapes[i].draw(context);
}
}
This is the 'proper' way of drawing to the canvas. For anything larger scale, please look into existing engines that do a looooot of hard work for you and have thought of everything so you don't have to. I have worked on such engines.

Categories

Resources