Search code examples
javascriptclassanimation

JavaScript Parallax side scrolling not updating scroll speed for each layer


I am creating a parallax side scrolling animation, and the speed seems to be not updating for each of the layers, I created a parallax layers class where it takes an array of images and array of speed values for each of the layer, that outputs a parallax animation on a canvas, and it animates all the layers, but does not update the speed for the individual layer but instead uses the same speed value for all the layers.

const canvas = document.getElementById('canvas2')
const ctx = canvas.getContext('2d')
const CANVAS_WIDTH = canvas.width = 1600;
const CANVAS_HEIGHT = canvas.height = 720;





class parralaxLayer {
    constructor(imageARR, speedARR) {
        this.x = 0;
        this.y = 0;
        this.width = 3840;
        this.height = 720;
        this.x2 = this.width;
        this.imageArray = imageARR;
        this.speedArray = speedARR;
        this.i = 0;
        this.imgSet = []
        this.speed = []
        this.gameSpeed = 5

        //puts a object of images in an array
        Object.values(this.imageArray).forEach((layer) => {
            let img = new Image()
            img.src = layer
            this.imgSet.push(img)

        })

        //loads speed values for each layer
        this.speedArray.forEach((layer) => {
            let speedVals = layer * this.gameSpeed
            this.speed.push(speedVals)

        })




    }

    updateDraw(img, speed) {
        //try a for loop or for each loop here
        if (this.x <= -this.width) {
            this.x = this.width + this.x2 - speed
        }

        if (this.x2 <= -this.width) {
            this.x2 = this.width + this.x - speed;
        }


        this.x = Math.floor(this.x - speed);
        this.x2 = Math.floor(this.x2 - speed);

        ctx.drawImage(img, this.x, this.y, this.width, this.height);
        ctx.drawImage(img, this.x2, this.y, this.width, this.height);
    }



    //when loading indvidually speed is correct rendering it sets it to one speed
    animate() {
        ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
        //draws each of the layers and images
        for (let i = 0; i < this.imgSet.length; i++) {
            let speed = this.gameSpeed * this.speedArray[i]
            this.updateDraw(this.imgSet[i], speed);

        }

        requestAnimationFrame(() => this.animate());
    }



}



let arrayofSpeed = [0.2, 0.4, 1, 2, 4]

let dayScene = new parralaxLayer(timeCollection[0], arrayofSpeed)

console.log(dayScene.speed)


dayScene.animate()

I tried putting all the function inside the class, as animate was initially outside, of the class, and also changed game speed from a global variable to the variable inside the class, and update and draw were separate method, so I combined it with it taking the speed value and image of the current parallax layer, but nothing seems to be working.


Solution

  • Your class code was a mixture of trying to draw all images and only drawing one image.

    The speed problem was due to using the essentially the same x value for all images. You were only offsetting the x of each image from the first image.

    In your constructor, this.width should be CANVAS_WIDTH and this.height should be CANVAS_HEIGHT.

    If your images are not all the same width, then you should draw them at this.img.width

    See the NOTE's on specific changes in the code snippet below.

    const canvas = document.getElementById('canvas2');
    const ctx = canvas.getContext('2d');
    // NOTE Changed canvas size for debugging purposes
    const CANVAS_WIDTH = canvas.width = 800; // 1600;
    const CANVAS_HEIGHT = canvas.height = 320; // 720;
    
    
    class parralaxLayer {
      constructor(imageName, speed) {
        this.x = 0;
        this.y = 0;
        // NOTE This probably needs to be the canvas width
        this.width = CANVAS_WIDTH; // 3840;
        // NOTE This should probably be the same as the canvas height
        this.height = CANVAS_HEIGHT; // 720;
        this.x2 = this.width;
        this.gameSpeed = 3;
        this.speed = speed * this.gameSpeed;
    
        this.img = new Image();
        this.img.src = imageName;
      }
    
    
      updateDraw( ) {
        //try a for loop or for each loop here
        if (this.x <= -this.width) {
          this.x = this.width + this.x2 - this.speed;
        }
    
        if (this.x2 <= -this.width) {
          this.x2 = this.width + this.x - this.speed;
        }
    
        this.x = Math.floor(this.x - this.speed);
        this.x2 = Math.floor(this.x2 - this.speed);
    
        // NOTE For debug with my images, I need to draw at the image's own size
        // ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
        // ctx.drawImage(this.img, this.x2, this.y, this.width, this.height);
        ctx.drawImage(this.img, this.x, this.y, this.img.width, this.img.height);
        ctx.drawImage(this.img, this.x2, this.y, this.img.width, this.img.height);
      }
    }
    
    
    let timeCollection = [
      "https://www.ripspics.com/test/images/background-800x320.png",
      "https://www.ripspics.com/test/images/bush-400x320.png",
      "https://www.ripspics.com/test/images/mushroom-400x320.png",
      "https://www.ripspics.com/test/images/ground-plant-256x320.png",
      "https://www.ripspics.com/test/images/toad-140x320.png"
    ];
    
    let arrayofSpeed = [0.2, 0.4, 1, 2, 3];
    
    // NOTE A place to store all the objects we create with the parralaxLayer class
    let imageObjects = [];
    
    // NOTE Create an object for each image and store it in the new imageObjects array
    for( let imageIndex = 0; imageIndex < timeCollection.length; ++imageIndex ) {
      imageObjects.push(new parralaxLayer(timeCollection[imageIndex], arrayofSpeed[imageIndex]));
    }
    
    // Start the animation 
    animate();
    
    // NOTE Moved the animation outside of the class, with appropriate edits 
    function animate() {
        ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
    
        //draws each of the layers and images
        for( let imageIndex = 0; imageIndex < timeCollection.length; ++imageIndex ) {
          imageObjects[imageIndex].updateDraw( );
        }
    
      requestAnimationFrame( animate );
    }
    <canvas id="canvas2"></canvas>