The HTML canvas drawImage method overlays images when the source is modified

Trying to implement a scroll animation on my website, I came across a guide for creating an "Apple-like animation" using image sequences. Even though I'm new to Angular, I attempted to adapt the code to work with Angular.

However, instead of animating smoothly, the images are just stacking on top of each other as I scroll through the page.

Here is the current outcome:

https://i.stack.imgur.com/TXoPn.jpg

This is the snippet of code I have been working with:

ngOnInit(): void {
  const canvas = <HTMLCanvasElement> document.getElementById("canvas1")!;
  const context = canvas.getContext("2d")!;

  canvas.width = 1158;
  canvas.height = 770;

  const img = new Image()
  img.onload = () => {
    context.drawImage(img, 0, 0, canvas.width, canvas.height);
  }
  img.src = this.currentFrame(1);

  window.addEventListener('scroll', () => {
    const scrollLength = window.scrollY;
    const maxScrollTop = this.document.body.scrollHeight - window.innerHeight;
    const scrollFraction = scrollLength / maxScrollTop;
    let frameIndex = Math.min(
      this.frameCount,
      Math.floor(scrollFraction * this.frameCount)
    );
    if (frameIndex + 1 > this.frameCount) frameIndex = this.frameCount + 2;
    requestAnimationFrame(() => this.updateImage(frameIndex, canvas, context, img));
  });
  this.preloadImages();
}

updateImage(index: number, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, img: HTMLImageElement) {
  if (index == 0 || index > this.frameCount) {
    context.clearRect(0, 0, img.width , img.height);
  } else {
    img.src = this.currentFrame(index);
    context.drawImage(img, 0, 0, canvas.width, canvas.height);
  }
}

Answer №1

To avoid any overlapping, make sure to clear the canvas before updating with a new image. You can modify the following function:

refreshCanvas(index: number, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, img: HTMLImageElement) {
  if (index == 0 || index > this.totalFrames) {
    context.clearRect(0, 0, img.width , img.height);
  } else {
    context.clearRect(0, 0, img.width , img.height);
    img.src = this.getFrameData(index);
    context.drawImage(img, 0, 0, canvas.width, canvas.height);
  }
}

Answer №2

Zorion had an insightful idea with his answer

However, implementing it in that way causes the canvas to go blank until the next image loads, resulting in a laggy and sluggish experience. To resolve this issue, the canvas needs to be cleared once the next image has finished loading.

The solution involves adding context.clearRect within the img.onload function:

img.onload = () => {
  context.clearRect(0, 0, img.width , img.height);
  context.drawImage(img, 0, 0, canvas.width, canvas.height);
}

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

Unable to position the button next to the search bar

I'm struggling to get my search bar aligned next to my dropdown button. I've tried various alignment methods without success. Both buttons are within a container, causing the search bar to span 100% of the container's width. I also attempted ...

Submenu animation that "bursts onto the scene"

I'm facing an issue with my menu that has sub-items inside it. To achieve the animation effect I desire, I need to extract the width, height, and first-child's height of the sub-menu. While my animation is working most times, there are instances ...

I have an HTML table with multiple cells containing inner HTML tables. I have implemented a function along with buttons to filter the main table, excluding the inner tables

My HTML Table is generated from my database, containing information about machines and their status pulled from emails with HTML Tables. Each row has a click option to open/hide the <td> tag showing the original table for more details and better trac ...

Is it possible to create an observable with RXJS that emits only when the number of items currently emitted by the source observables matches?

I am dealing with two observables, obs1 and obs2, that continuously emit items without completing. I anticipate that both of them will emit the same number of items over time, but I cannot predict which one will emit first. I am in need of an observable th ...

What is the best way to modify React state?

Can someone help me troubleshoot an issue with toggling React state after a button click? I want to be able to change "Work From Office" to "Work From Home" and vice versa, but it's only working once. Is there a way to achieve this using an if stateme ...

Best location to define numerous dialog components

Currently, I have 8 custom Modals that are called in various places within my app. These modals are currently located inside the app.component.html as shown below: <agc class="app-content" [rows]="'auto 1fr'" [height]=" ...

Guide on incorporating pinching gestures for zooming in and out using JavaScript

I have been working on implementing pinch zoom in and out functionality in JavaScript. I have made progress by figuring out how to detect these gestures using touch events. var dist1=0; function start(ev) { if (ev.targetTouches.length == 2) {//checkin ...

Filtering, cleaning, and confirming the validity of data input allowed for HTML tags

While there is a lot of information available on sanitizing, filtering, and validating forms for simple inputs like email addresses, phone numbers, and addresses, the security of your application ultimately relies on its weakest link. What if your form inc ...

How can I align two div buttons at the top and bottom to appear on the left and right sides?

How can I change the position of two buttons from top and bottom to left and right as shown in the second image? I am utilizing Floating Vue, so the buttons will be contained within a div. Is it feasible to adjust their position accordingly? Check out th ...

Is it possible to create dynamic meta tags in Angular that will appear in a Twitter-style card preview?

My project involves building a dynamic website using a Java app that serves up a REST-ish JSON API along with an Angular9 front end. A key requirement is the ability to share specific URLs from the app on platforms like Twitter and Slack, which support Twi ...

Troubleshooting: :before element not being hidden by jQuery slidetoggle()

My elements are all behaving correctly with the slidetoggle() function, except for my li:before class. I've attempted to manipulate the overflow, visibility, display, and other properties of both the :before pseudo-element and the li element, but the ...

What is the best way to include a mat-paginator with mat-cards?

Just starting out with Angular and trying to implement pagination for mat-cards instead of just tables. I have a lot of code and want to display only 8-10 cards per page. How can I achieve this? Below is my HTML and TypeScript code. .html file <div cl ...

Data within object not recognized by TableCell Material UI element

I am currently facing an issue where the content of an object is not being displayed within the Material UI component TableCell. Interestingly, I have used the same approach with the Title component and it shows the content without any problems. function ...

In the latest version of Expo SDK 37, the update to [email protected] has caused a malfunction in the onShouldStartLoadWithRequest feature when handling unknown deeplinks

After updating to Expo SDK 37, my original code that was previously functioning started encountering issues with <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0e7c6b6f6d7a23606f7a67786b23796b6c78667f79627a">[email prot ...

Count characters in multiple text inputs with AngularJS

Currently, I am developing an Angular JS application with the help of Angular-UI (bootstrap). In this app, there are multiple input boxes where users can enter data, which is then displayed in a div. My goal is to have a character count that applies to al ...

Steps to alter background image and adjust its height upon function activation

I am working on a search page with an advanced search option that only certain users can access. I need the height of my div to increase accordingly and also change the background image to a larger size when the advanced search is selected. How can I make ...

Encountering problems when attempting to effectively filter a list of products using data

<div id="prod"> <div class="content" data-brand="Andrew" data-price="1000" data-store="JCPenny">Andrew</div><br /> <div class="content" data-brand="Hill" d ...

Refine the category based on a specified key

Currently, I am in the process of developing a React Hook using TypeScript. In this hook, I am passing both a key and a value that will be associated with this key as arguments. My objective is to constrain the type of the value based on the specified key. ...

Why do selected items in Ionic 3 ion-option not get deselected even after reloading or reinitializing the array

HTML File: <ion-item class="inputpsection" *ngIf="showDeptsec"> <ion-label floating class="fontsize12">Department</ion-label> <ion-select (ionChange)="showDepartmentChosen($event)" multiple="true" formControlName=" ...

Error message "NoSuchKey" encountered on CloudFront when accessing an Angular application

I recently developed an Angular application and successfully uploaded it to an S3 bucket. To make my website accessible, I deployed a CloudFront distribution. However, when trying to access a specific route on the website (such as /login), I encountered an ...