What are some ways to slow down the speed of a canvas animation?

I am currently working on an animation project using JavaScript and canvas. I have a reference fiddle where objects are generated randomly and fall from the top right corner to the bottom left corner, which is fine. However, the issue is that the objects are moving at a high speed. I would like to slow down and smooth out the animation flow.

(function() {
  var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
    function(callback) {
      window.setTimeout(callback, 1000 / 60);
    };
  window.requestAnimationFrame = requestAnimationFrame;
})();

var particleArr = [],
  canvas = document.getElementById("canvas"),
  ctx = canvas.getContext("2d"),
  flakeCount = 700,
  mouseX = -100,
  mouseY = -100,
  xMultiplier = 0.015


canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

function getRandomColor() {
  // Random Color Generate
  const colorArr = ["rgba(215,88,69, 1)", "rgba(117, 161, 199, 1)"]; // Blue & Orange Color
  const randomColor = colorArr[Math.floor(Math.random() * colorArr.length)];

  return randomColor;
}

function flow() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (var i = 0; i < flakeCount; i++) {
    var flake = particleArr[i],
      x = mouseX,
      y = mouseY,
      minDist = 150,
      x2 = flake.x,
      y2 = flake.y;

    var dist = Math.sqrt((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y)),
      dx = x2 - x,
      dy = y2 - y;

    if (dist < minDist) {
      var force = minDist / (dist * dist),
        xcomp = (x - x2) / dist,
        ycomp = (y - y2) / dist,
        deltaV = force / 2;

      flake.velX -= deltaV * xcomp;
      flake.velY -= deltaV * ycomp;

    } else {
      flake.velX *= .98;
      if (flake.velY <= flake.speed) {
        flake.velY = flake.speed
      }
      flake.velX += Math.cos(flake.step += .05) * flake.stepSize;
    }

    ctx.fillStyle = getRandomColor();

    flake.y += flake.velY;
    flake.x += flake.velX;

    if (flake.y >= canvas.height || flake.y <= 0) {
      reset(flake);
    }

    if (flake.x >= canvas.width || flake.x <= 0) {
      reset(flake);
    }

    ctx.beginPath();
    ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);
    ctx.fill();
  }
  requestAnimationFrame(flow);

};

function reset(flake) {
  let temp = (Math.random() * 1) + 0.5;
  flake.x = canvas.width;
  flake.y = 50;
  flake.size = (Math.random() * 3) + 5;
  flake.speed = (Math.random() * 7) + 0.5;
  flake.velY = flake.speed;
  flake.velX = -xMultiplier * canvas.width * temp;
}

function init() {
  for (var i = 0; i < flakeCount; i++) {
    var x = canvas.width,
      y = 50,
      size = (Math.random() * 3) + 5,
      speed = 0;

    particleArr.push({
      speed: speed,
      velY: speed,
      velX: -xMultiplier * canvas.width * speed,
      x: x,
      y: y,
      size: size,
      stepSize: (Math.random()) / 30,
      step: 0,
      angle: 360
    });
  }

  flow();
};

canvas.addEventListener("mousemove", function(e) {
  mouseX = e.clientX,
    mouseY = e.clientY
});

window.addEventListener('resize', onWindowResize, false);

function onWindowResize() {

  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
}

init();
canvas {
  background-color: #000000 !important;
}

body {
  margin: 0;
  overflow: hidden;
}
<canvas id="canvas"></canvas>

Answer №1

It was a bit difficult to figure out which part you wanted to slow down due to the various FX and interactions in the code.

Since your code is a bit outdated, I decided to start fresh.

Instead of tweaking the constants like before, I introduced a global variable called rate at the beginning of the source code to regulate the animation speed, including user interaction.

I've included two buttons now to adjust the speed of the animation.

I hope this solution proves helpful :)

var rate = 1;
slower.addEventListener("click", () => rate *= 1 / 1.2);
faster.addEventListener("click", () => rate *= 1.2);

const flakes = [], flakeCount = 700, xMultiplier = 0.015;
const minDist = 150,  minDistSqr = minDist * minDist;
const colors = ["#F99","#F83","#AF9","#ED9","#AC8","#FA9" ];
const ctx = canvas.getContext("2d");

const mouse = {x: -100, y: -100};
const randPick = (arr, len = arr.length) => arr[Math.random() * len | 0];
Math.rand = (min, range) => Math.random() * range + min;
   
function Flake() {
  this.reset();
  this.stepSize = Math.random() / 30;
  this.step = 0;
}
Flake.prototype = {
  reset() {
    this.x = canvas.width;
    this.y = 50;
    this.size = Math.rand(5, 3);
    this.speed = Math.rand(0.5, 7);
    this.velY = this.speed;
    this.velX = -xMultiplier * canvas.width * Math.rand(0.5, 1);
    this.col = randPick(colors);
  },
  draw() {
    ctx.fillStyle = this.col;
    const s = this.size, sh = -s / 2;
    ctx.fillRect(this.x + sh, this.y + sh, s, s);
  },
  update(w, h) {
    const f = this;
    const dx = f.x - mouse.x;
    const dy = f.y - mouse.y;
    const distSqr = dx * dx + dy * dy;
    if (distSqr < minDistSqr) {
      const deltaV = 2 * minDist * rate / distSqr ** 1.5;
      f.velX -= deltaV * dx;
      f.velY -= deltaV * dy;
    } else {
      f.velX -= 0.1 * rate * f.velX;
      if (f.velY <= f.speed ) { f.velY = f.speed }
      f.velX += Math.cos(f.step += 0.05 * rate) * f.stepSize  * rate;
    }
    f.y += f.velY * rate;
    f.x += f.velX * rate;
    if (f.y >= h || f.y <= 0 || f.x >= w || f.x <= 0) { this.reset() }
    else { this.draw() }
  }
};

init();
mainLoop();
function mainLoop() {
  if (innerWidth !== canvas.width || innerHeight !== canvas.height) { resize() }
  else { ctx.clearRect(0, 0, canvas.width, canvas.height) }
  for (const f of flakes) { f.update(canvas.width, canvas.height) }
  requestAnimationFrame(mainLoop);
}

function init() {
  var i = flakeCount;
  while (i--) { flakes.push(new Flake()) }
}

canvas.addEventListener("mousemove", e => { mouse.x = e.clientX; mouse.y = e.clientY });
function resize() { canvas.width = innerWidth;  canvas.height = innerHeight }
canvas {
  background-color: #000;
}

body {
  margin: 0;

}
.buttons {
  position: absolute;
  top: 12px;
  left: 12px;
  color: #000;
  background-color: #AAA; 
}
.buttons > div {
  margin: 3px;
  padding: 3px;
  background-color: #EEE;
  cursor: pointer;
}
.buttons > div:hover {
  background-color: #DEF; 
}
<canvas id="canvas"></canvas>
<div class = "buttons">
<div id="slower">Slower</div>
<div id="faster">Faster</div>
</div>

Answer №2

The requestAnimationFrame() function is optimized to run efficiently on any computer it's running on, so it's best not to tamper with your render loop.

If you want to tweak the speed of your particles, try adjusting the flake.speed or xMultiplier variables. These play a key role in determining how fast the particles move. Each iteration of the flow() loop updates the position of each particle based on its velocity properties, ultimately rendering the arc using

ctx.arc(flake.x, flake.y, flake.size, 0, Math.PI * 2);

Any variable passed to ctx.arc() influences the position of the particles, with many of these variables recalculated in each loop cycle.

I'm not an expert, but experimenting with different variable values may yield interesting results.

Check out this link for reference.

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

In Angular, when a promise returns an "undefined" value, how does this interact with .NET

When I execute this code snippet, I am encountering an issue where the response returned is "undefined" instead of the expected value. Here is the code in question: $scope.SameNameFunction = function() { var payload = { itemname: $scope.EventD ...

What is the best way to send a POST request with parameters to a PHP backend using AJAX?

This is an updated version of a previous question that was identified as a duplicate (How can I send an AJAX POST request to PHP with parameters?) I am currently sending an AJAX request to a PHP backend. Here are the JavaScript functions used to make and ...

How can DataTables (JQuery) filter multiple columns using a text box with data stored in an array?

I've been attempting to create a multi-column filter similar to what's shown on this page () using an array containing all the data (referred to as 'my_array_data'). However, I'm facing issues with displaying those filter text boxe ...

Implementing a Tri-state Checkbox in AngularJS

I've come across various discussions on implementing a 3-state checkbox with a directive or using CSS tricks (such as setting 'indeterminate=true' which doesn't seem to work). However, I'm curious if there's another method to ...

How can I add divs to an HTML page with a Javascript animated background?

I am currently facing an issue with my JavaScript animated background, consisting of just 3 pictures. I am trying to display some Div elements on it with content inside. Here is the code snippet I have right now: In my CSS file, I have defined styles for ...

I'm looking to mirror images within the Bootstrap framework - any suggestions on how to achieve this using jQuery

When hovering over the image, it should flip to reveal text or a link: <div class="clearfix visible-xs"></div> <div class="col-md-3 col-sm-3 col-xs-6"> <div class="speaker-item animated hiding" data-animation="fade ...

Displaying divs inline with overlapping child divs

As a beginner in CSS, I tackle the challenges of learning it every day. Despite feeling clumsy and not naturally adept at it, my determination is strong (although sometimes my brain feels like an old computer on the verge of overheating). My current goal i ...

Encountering an error when integrating my library with MUI

Currently, I am working on developing a library that wraps MUI. One of the components I created is a Button, and here is the code snippet for it: import React, { ReactNode } from 'react' import Button from '@mui/material/Button'; impor ...

Transforming JSON dates to Javascript dates using AngularJS and ASP.NET

How can I convert a JSON date /Date(1454351400000)/ into a proper date format using AngularJS and ASP.NET? $http.get('/home/GetProducts') .success(function (result) { $scope.products = result; }) .error(function (data) { ...

unusual behavior of UTF-8 characters in Blogger when utilizing JavaScript

I'm facing a peculiar issue with JavaScript in my Blogger code when dealing with certain characters like '¡' and '?'. For instance, the following code snippet displays ñéúüëò¡!¿? inside the div but shows ñéúüëò&# ...

Create a selection menu in an HTML dropdown list using unique values from a JSON object that is separated by commas

I'm working with a JSON object that contains multiple key value pairs, one of which is the Languages key. This Languages key holds comma-separated values such as "English, Hindi, French," and so on. My goal is to extract distinct values from the Lang ...

Exploring the Power of Vue 3 in Manipulating the DOM

Hello everyone, I am a beginner with Vue and I am interested in learning how to modify the DOM without relying on methods such as document.querySelector or getElementById. Let's take for instance this input: <input id="myInputId" class=& ...

Preventing Jquery Append from Adding Previous Elements

I am struggling to figure out how to display the star rating for each hotel individually. I have 5 hotels, each with a different star rating. Here is my Javascript code: function GetStarHotel() { var parent = $(' p.star '), imagePat ...

Is there a way to target the mat-icon element using the icon's name in CSS

I have a group of mat-icons that are automatically generated, meaning I cannot assign them ids or classes. The only way to distinguish between them is by their names: <mat-icon role="img">details</mat-icon> <mat-icon role="img ...

Execute the function only in response to changes in the data

I have a scenario where I am making an ajax call every 3 seconds to keep my app updated with rapidly changing information. However, the expensive function that I run inside the $.done() callback is causing my app to slow down. I want to optimize this proce ...

Is each individual character displayed on the web page twice when using a controlled component?

Currently, I am diving into the extensive React documentation. A particular section caught my attention - controlled components. It delves into how form elements, such as an <input>, manage their own internal state. The recommendation is to utilize c ...

Customizing text in Contact Form 7 using jQuery on form submission

I am trying to add functionality to a simple radio button on CF7 [radio radio-698 id:domanda2 use_label_element default:1 "0.Nessuna Risposta" "risposta1" "risposta2"] [submit "Invia"] My goal is to have the text &q ...

Regular pattern with Kubernetes cluster endpoint utilizing either IP address or fully qualified domain name

In my Angular/typescript project, I am working on building a regex for a cluster endpoint that includes an IP address or hostname (FQDN) in a URL format. For instance: Example 1 - 10.210.163.246/k8s/clusters/c-m-vftt4j5q Example 2 - fg380g9-32-vip3-ocs.s ...

Is it possible to apply a border-bottom to the li element using jQuery?

<li><a target="_blank" href="#">example one</li> <li><a target="_blank" href="#">example two</li> <li><a target="_blank" href="#">example three</li> <li><a target="_blank" href="#">example f ...

Adding a value to an element in JavaScript to interact with it using Selenium

After implementing the provided code snippet, I noticed that it replaces the value in the element with a new one. However, I am looking to append or insert a new line rather than just replacing the existing value. Is there an alternative command like app ...