Preventing an image from being repeated when using Canvas drawImage() without having to clear the entire canvas

How can I prevent multiple instances of the same image from smearing across the canvas when drawing it? The platforms seem to stick together and not separate properly. Why do I have to clear the entire rectangle for everything to disappear?

Does anyone have any suggestions?

Code:

//////////////////////////////////////////////////////////
//
// MOVE PLATFORMS

var cFunc = 0;
var setRand = 1;
function setR() {
    setRand = setTimeout(setR, 10);
    cTAdd = Math.floor(Math.random() * 100 + 1);
}

var block00;
var block01; // ADD SEPARATION BLOCK(BLOCK HOLE)
var block02;
var block03;
var block04; // ADD SEPARATION BLOCK(BLOCK HOLE)
var block05;

function landT() {
    setThis = setTimeout(landT, 10);

    var canvas = document.getElementById("canvas");
    var context = canvas.getContext('2d');

    ///////////////////////////////////////////////
        //
        // X POSITION OF (BLOCK HOLE)

        ///////////////////////////////////////////////////////////////////////////
        //
        // BOTTOM ROW

        block00 = document.createElement("img");
        block01 = document.createElement("img");
        block02 = document.createElement("img");

        if (cTAdd > 0 && cTAdd < 40) {
            block00.src = "images/sep2.png";
            context.drawImage(block00, moveBlock1, 315, 400, 28);
        }

        if (cTAdd > 40 && cTAdd < 80) {
            block01.src = "images/sep1.png"; // ADD SEPARATION BLOCK(BLOCK HOLE)
            context.drawImage(block01, moveBlock2, 315, 200, 28);
        }

        if (cTAdd > 80 && cTAdd < 100) {
            block02.src = "images/platform00.png";
            context.drawImage(block03, moveBlock3, 315, 158, 28);
        }


        ///////////////////////////////////////////////////////////////////////////
        //
        // BLOCK02 GET X POS OF ADDED BLOCK 

        if (getX1 == 0) { //////////////////////////////////////////// SET (BLOCK HOLE) X
            var doc2 = block02.getBoundingClientRect();
            gX1 = doc2.left;
            getX1 = 1;
        }


        ///////////////////////////////////////////////////////////////////////////
        //
        // TOP ROW

        block03 = document.createElement("img");
        block04 = document.createElement("img");
        block05 = document.createElement("img");

        if (cTAdd > 0 && cTAdd < 40) {
            block03.src = "images/sep2.png";
            context.drawImage(block03, moveBlock1, 165, 400, 28);
        }

        if (cTAdd > 40 && cTAdd < 80) {
            block04.src = "images/sep1.png"; // ADD SEPARATION BLOCK(BLOCK HOLE)
            context.drawImage(block04, moveBlock1, 165, 200, 28);
        }

        if (cTAdd > 80 && cTAdd < 100) {
            block05.src = "images/platform00.png";
            context.drawImage(block05, moveBlock1, 165, 158, 28);
        }

}

//////////////////////////////////////////////////////////
//
// MOVE PLATFORMS 

var thisSet = 1;
var cPlayer = 1;
var moveSpeed = 5;
var xPos = 50;
var yPos = 380;
function moveLand() {

    var canvas = document.getElementById("canvas");
    var context = canvas.getContext('2d');

    thisSet = setTimeout(moveLand, 30);

    if (xPos >= 350 && moveL == 1) {
        moveBlock1 = moveBlock1 - 15;
    }

    if (gX1 > 0 && moveL == 1 && xPos >= 350) {
        gX1 = gX1 - 15;
    }
    if (getX1 == 1 && gX1 == 0) {
        getX1 = 0;
    }
    if (gX1 < 0) {
        gX1 = 0;    
    }


    console.log("X1: " + gX1); // CONSOLE/LOG gX1 

    if (moveBlock1 <= -1500) {
        moveBlock1 = 1000;
        moveBlock2 = 1300;
        moveBlock3 = 1600;
        context.clearRect(0, 0, 1023, 300); 
    }

}

Answer №1

Canvas does not retain any memory, so clearing it will result in everything disappearing.

However, the canvas is known for its speed. If you do not have complex image filtering operations (which would require using the slow getImageData()/putImageData), you can design your script to redraw everything at each frame:

var ctx = canvas.getContext('2d');

function draw(){
  drawBackground();
  player.draw();
  requestAnimationFrame(draw);
}

var drawBackground= function(){
  ctx.clearRect(0,0,canvas.width,canvas.height);
  ctx.fillStyle="#CCC";
  ctx.fillRect(0,0,canvas.width, canvas.height);
  ctx.fillStyle="#000";
  ctx.fillRect(0,canvas.height-50,canvas.width, 50);
  ctx.beginPath();
  ctx.fillStyle="orange";
  ctx.arc(25,25,12,0,2*Math.PI,0);
  ctx.fill()
}

var player = new Player();
function Player(){
  var that = this;
  that.img = new Image;
  that.img.onload=draw;
  (that.init = function(){  
    that.img.src='http://lorempixel.com/50/50?121';
    that.x=canvas.width/2;
    that.y=canvas.height/2;
    that.width = 22;
    that.height = 22;
  })()
  that.update = function(){
    that.x += Math.random()*3-1.5;
    that.y += Math.random()*3-1.5;
    if(that.x-(that.width/2)<=0)that.x=that.width/2;
    if(that.x+(that.width/2)>=canvas.width)that.x=canvas.width-that.width/2;
    if(that.y-(that.height/2)<=0)that.y=that.height/2;
    if(that.y+(that.height/2)>=canvas.height-50)that.y=canvas.height-50-that.height/2;
  }
  that.draw = function(){
    console.log(that.x)
    that.update();
    ctx.drawImage(that.img, that.x, that.y, that.width, that.height);
  }
  return that;
}
<canvas id="canvas"></canvas>

If you are performing complex background operations and just want to draw a moving object on it at some point, you can create a clone of your canvas. Use the drawImage() method to copy your canvas onto the clone. Then, on each frame, redraw your original canvas along with the moving object:

// save the original
var cloned = canvas.cloneNode(true).getContext('2d');
cloned.drawImage(canvas, 0,0);

// Then after in the draw loop
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(cloned.canvas, 0,0);
drawYourMovingObject();

Answer №2

I created this animation using the Tryit editor on w3schools.com. The code below demonstrates how to move an image across a canvas without repeating.

<!DOCTYPE html>
<html>
<body onLoad = "getII()">

<canvas id="myCanvas" width="1200" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

<script>
var inum = 10;

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var imgData = ctx.createImageData(2000, 2000);
var target_area = ctx.getImageData(10, 10, 2000, 2000);

var i;
for (i = 0; i < imgData.data.length; i += 4) {
    imgData.data[i+0] = 255;
    imgData.data[i+1] = 0;
    imgData.data[i+2] = 0;
    imgData.data[i+3] = 255;
}

function getII() {
  var setThis = setTimeout(getII, 100);
  inum = inum + 10;

  ctx.putImageData(target_area, 10, 10);
  ctx.fillRect(inum, 10, 100, 100);
  target_area = context.getImageData(10, 10, 2000, 2000);
  ctx.putImageData(target_area, 10, 10);
  ctx.fillRect(inum, 10, 100, 100);
}


</script>

</body>
</html>

Answer №3

One way to create animation of a shape on a Canvas is by following these steps. Firstly, you need to copy the specific portion of the canvas where you want to draw the shape. Next, draw the shape in that area. When it's time to move the shape, redraw the copied area to erase the shape and repeat the process in the new location. To achieve this, you can utilize Canvas.getImageData() and putImageData(). You can find more information about these methods on this w3cschools page

I have successfully used this approach to make a shape on a canvas follow the mouse cursor.

Below is an example code snippet to demonstrate this concept:

var context = myCanvas.getContext("2d");

// Copy the current area of the canvas
var target_area = context.getImageData(10, 10, 50, 50);

// Draw a rectangle in that area
context.fillRect(15, 15, 40, 40);

// Redraw the copied area to "erase" only the rectangle
context.putImageData(target_area, 10, 10);

// Repeat the process for another area
target_area = context.getImageData(10, 15, 50, 50);

// Draw a rectangle at the new location
context.fillRect(15, 20, 40, 40);

// Restore the copied image to "erase" the previous rectangle
context.putImageData(target_area, 10, 15);

// Keep repeating these steps for animation

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The Tailwind preset is generating CSS code, but the webpage is not displaying the intended styles

Can anyone explain why the preset is generating CSS in the output file, but the styles are not displaying in the browser? When I manually write CSS in newstyle.css, it gets outputted to output.css and renders correctly in the browser. I attempted adding t ...

Error encountered during Ajax request - two files being transmitted instead of one

Can someone assist me with a basic ajax call for a login button? I need help with the form submission and sending the request to a php file to handle the login action. However, I am encountering an issue where two files are being sent instead of one when ...

Encountering a npm script error while running on a Windows operating

While using webpack to build my application, I encountered the following error message in the command prompt: [email protected] dev D:\Myprograms\java script\forkify webpack --mode development The error mentioned: Insufficient num ...

Can users arrange a lineup of choices?

As a beginner, I have a task that seems pretty simple to others but not so much for me. I need to create a feature where a client can order 10 different services in the order they prefer. The idea is to use a dropdown menu or checkboxes to allow the user ...

The PHP file on the server is missing the mandatory "event" parameter for the EventSource

Explaining this issue was a bit of a challenge. I've set up a Javascript EventSource object with some customized event handlers like so: var source = new EventSource('updates.php'); source.addEventListener('add', addHandler, fals ...

What is causing the spinner with CSS rotation transform to bounce rhythmically?

In my design, I have created a simple Spinner icon in Figma. It's meant to be perfectly aligned at the center of an enclosing frame that has dimensions of 64x64. To achieve the rotating effect, I applied CSS rotation as shown below: let rotation = ...

a new webpage beginning from a different location

Starting from scratch here, I must apologize for not providing any code upfront. The issue at hand is this - I've got a page full of information sorted by ID (retrieved from the database). These IDs are linked from another page where users can click ...

Having trouble getting Fullcalendar to show up on my cordova app

Currently, I am in the process of building a mobile application using ionic and cordova. My goal is to incorporate a timetable system utilizing Fullcalendar and a PHP page that showcases MySQL data in JSON format. Despite my efforts, I am encountering diff ...

When utilizing Javascript's Array.push method, a nested array is generated that is inaccessible using the index

I have reviewed several articles discussing the issue of asynchronous calls returning undefined. Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference Get data from fs.readFile However, none of these articles ...

Having trouble getting my ReactJS page to load properly

I am currently linked to my server using the command npm install -g http-server in my terminal, and everything seems to be working smoothly. I just want to confirm if my h1 tag is functional so that I can proceed with creating a practice website. I have a ...

Mapping an array based on specific conditions

When using my photo gallery app, I faced a challenge in mapping an array of images based on the user-selected album name. The current setup works perfectly for the 'Cambodia' album where all images are correctly logged in the console after mappin ...

When transmitting an ajax POST FormData object, the key and value may not be transmitted as originally configured

In my code, I am setting up a FormData object like this: const formData = new FormData(); formData.append('id', '12345678'); Next, I make a POST request using angular HttpClient. However, when I use Postman to debug the reques ...

When trying to search for 'elForm' using the 'in' operator within the context of a "datetime" type, the error "Unable to find 'elForm' in undefined" occurs

I am attempting to implement a datepicker with time options from Element UI. I am encountering an issue within the ElementUI component. It functions correctly if the type option is set as date, but throws an error with datetime. Below is my code snippet an ...

Date Selector Tool for Input Field

Does anyone know how to change the color of the date picker that appears in certain browsers but is unsure about the selector's name? HTML: <form action="pledge.html" method="post"> <input type="date" id="birthday" name="user_bda ...

At what point in time does the LoadingFrameComplete event in Awesomium typically happen?

According to the documentation from Awesomium, the event WebView.LoadingFrameComplete is triggered when a frame finishes loading. This description seems somewhat ambiguous. Does this event coincide with the JavaScript load event of the window? Or perhap ...

"Trouble encountered while trying to display Angular in an HTML document

In my Angular project, there is a feature where a list of orders is displayed in one view. Each order is represented by its title only. When the user clicks on the title, they should be redirected to a new view showing the complete content of that particul ...

Customizing Ngx-bootstrap Carousel Indicator, Previous, and Next Button Styles

<carousel > <a href=""> <slide *ngFor="let slide of slides"> <img src="{{slide.imgUrl}}" alt="" style="display: block; width: 100%;"> </slide> 1. Is there a way to substitute the indicators with images ...

What is the best way to align two HTML elements in a single column?

I need to adjust the layout of an existing <td> in order to display a checkbox and one additional field on the same line. The current setup places these elements on separate lines. Below is the provided HTML code: <!DOCTYPE html> <html> ...

Issue with jQuery AJAX call: When submitting an HTML form, control is not being returned to the calling

I am facing an issue with my HTML form where it is loaded correctly into the DOM through a jQuery ajax call. The problem arises when I submit the form data to a PHP module using another jQuery ajax call. Even though the network traffic shows that the form ...

Successive vows

I'm trying to retrieve responses from four promises, but I currently have to call each function in sequence one after the other. In my code, you can see that I trigger the next function within the promise callback of the previously called function. H ...