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

Run JavaScript upon the webpage loading without the dependency of jQuery

Is there a way to ensure that JavaScript code is executed only after the entire page has loaded, without relying on jQuery? Take this example... <html> <head> <script type="text/javascript"> var box = document.ge ...

Angularv9 - mat-error: Issue with rendering interpolated string value

I have been working on implementing date validation for matDatepicker and have run into an issue where the error messages do not show up when the start date is set to be greater than the end date. The error messages are supposed to be displayed using inter ...

Is node.js required for utilizing Angularjs?

Considering incorporating angular.js into my website's Image Editing Tool. Is node.js necessary for this integration? I'm a bit puzzled here. Are there instances where both nodejs and angularjs are used together, or can they operate independentl ...

Is there a Javascript library available that can generate calendar links for Google, Yahoo, Outlook, and iCal?

I recently came across a sleek and professional web page widget that includes links (for example, and meetup.com). Could someone assist me in finding the library responsible for creating these links? I explored the discussion on Need a service that build ...

AngularJS: Unable to locate element using getElementById function

Using a third party JavaScript application, I have integrated and invoked a function within one of my AngularJS factories. This function requires the id of a DOM element as a parameter, as it manipulates this element. ThirdPartyApp.doSomething("#elementId ...

What is the process for incorporating an array of models?

let userData = await db.user.findOne({ attributes: attributes, include: ['charges', 'availability', 'practice',// 'services', 'education',// 'user_role', ...

Preserving content following the use of jQuery's fadeIn/fadeOut methods

I have a jQuery function that fades in and out a sprite (sprite without text) from one background to another, and it's working as intended. However, this function is also fading out the text within an element. HTML: <div id="menu"> <ul c ...

Submitting a form with multiple actions using Ajax

Is it possible to submit data from multiple forms on the same page to different API tables using AJAX for each form? I have one form that needs to write to one table and another form that needs to write to a different table. How can I achieve this with o ...

Valid ways to ensure that a CSS image expands outside the boundaries of the containing div

I have inserted an image using the following CSS code: .imgtemp { float:right; top:0px; left:0px; overflow:visible; width:100%; } Although I have added the div tag to display it on the page, ...

Storing retrieved data in React without using Redux involves using either the Context API or

When using Redux, I have a common store where fetched data is stored. For example, if I navigate away from my videos page (/videos) and then return to it, the videos are still available in the videos reducer. This allows me to show the user already loaded ...

Error: The callback function is no longer supported in Model.find() in Mongoose

I am having trouble retrieving all users from the database because I keep getting an error that says Model.find() no longer accepts a callback. Can someone please help me understand how to fetch data from my mongodb database? router.get('/users', ...

The innerHTML of the p tag remains unaffected when using document.getElementsById("")

HTML {% load static %} <link rel="stylesheet" type="text/css" href="{% static 'lessons/style.css' %}" /> <script> function openNav() { document.getElementById("mySidenav").style.width = "20%"; document.getElementById("main" ...

React - How to properly pass a reference to a React portal

I have a Card component that needs to trigger a Modal component. Additionally, there is a versatile Overlay component used to display content above the application. Displayed here is the App component: class App extends Component { /* Some Code */ ...

Customize CSS styling

When it comes to the styling of my first line, I have a specific style in mind: * { text-align:left; } This style works well across most parts of the site as they are predominantly left aligned. However, there are few areas where I need the text alig ...

Images are suddenly encircled by a mysterious border

On my wordpress website, I encountered a strange issue with some photos. Specifically, an image of a cross in a circle is getting a weird border around it, but only on certain resolutions (such as width:1506). Despite inspecting the element, I couldn' ...

The Best Way to Align a Large Image in the Middle of a Browser Window Using Inline HTML

Currently, I am attempting to center an image that is 1920px wide within the browser window. The challenge lies in not having it fit entirely within the window, so using width:100% will not achieve the desired effect. Furthermore, this needs to be accomp ...

Having trouble retrieving react environment variables from process.env?

When developing my React App, I utilized a .env file to securely store some essential API keys. However, as I attempted to access these variables using process.env, I encountered an issue where the values appeared to be set as undefined. To launch the app ...

JavaScript nested function "invalid function"

Recently, I've encountered an issue with a JavaScript file that I'm including on my page. Here's a summary of the situation: var PageTransitions = (function() { function setCurrent(currentSet) { alert(currentSet); } fu ...

Tips for automatically filling in fields when a button is clicked in a React application

I'm attempting to pre-fill the form fields that are duplicated with data from already filled fields. When I click the "Add Fields" button, new fields are replicated, but I want them to be pre-populated with data from existing fields. How can I access ...

Manipulating a React table: Customizing a row in the table

Below is the code snippet: const [data, setData] = useState([ {id: 1, name: 'paper', qty: 10}, {id: 2, name: 'bottle', qty: 10}, ]); const [isEditable, setEditable] = useState([]); useEffect(()=>{ data.map(each=>{ ...