Interactive Physics Animations Javascript Canvas 17

I’m really enjoying giving a control panel of sorts to the end user and letting them control the physics rules of their canvas. Let’s add more motion to it by applying a jitter to the velocity of each dot. This is similar to earlier, when we first started animating by applying a random number to the coordinates, but now we’ll apply a (smaller) random number to the velocity. This will be much smoother and will give a sense of real life to the dots. Straight lines and trajectories is always theoretical and hypothetical, but to make things look more real, sometimes we have to break the clean, straight lines. This will be another checkbox to control the float. I’m really enjoying the fact that the more properties we add to our controls we get exponentially more possible configurations. Perhaps we should even give a slider to control the strength of gravity…
interactive physics animations via javascript & canvas | 17.

[cc lang=”javascript”]
$(function () {
var canvas, context, width, height, x, y, radius = 25, clickX, clickY, drag = false;
var total_dots = 10;
var fps = 24;

canvas = $(“#canvas”)[0];
context = canvas.getContext(“2d”);
var dots = new Array();
var drag_i = -1;
var gravity = 0;
var friction = .98;
var bounce = -.96;
var wrap = false;
var float = true;

var this_dot = {};
for (var i=0; i < total_dots; i++){ createDot(); } function createDot(x, y, r, vx, vy){ var this_dot = { x: typeof(x) != 'undefined' ? x : Math.random()*canvas.width, y: typeof(y) != 'undefined' ? y : Math.random()*canvas.height, radius: typeof(r) != 'undefined' ? r : Math.random()*20+10, vx: typeof(vx) != 'undefined' ? vx : Math.random()*30-10, vy: typeof(vy) != 'undefined' ? vy : Math.random()*30-10 }; dots.push(this_dot); } draw(); $("#canvas").mousedown(function (event) { var dx, dy, dist; for (var i=0; i < dots.length; i++){ dx = event.pageX - this.offsetLeft - dots[i].x; dy = event.pageY - this.offsetTop - dots[i].y; dist = Math.sqrt(dx * dx + dy * dy); if(dist < radius) { drag = true; drag_i = i clickX = dx; clickY = dy; continue; } } //none clicked if (!drag) { createDot(event.pageX - this.offsetLeft, event.pageY - this.offsetTop); } }); $("#canvas").mouseup(function (event) { drag = false; drag_i = -1; }); $("#canvas").mousemove(function (event) { if(drag) { dots[drag_i].old_x = dots[drag_i].x; dots[drag_i].old_y = dots[drag_i].y; dots[drag_i].x = event.pageX - this.offsetLeft - clickX; dots[drag_i].y = event.pageY - this.offsetTop - clickY; dots[drag_i].vx = dots[drag_i].x - dots[drag_i].old_x; dots[drag_i].vy = dots[drag_i].y - dots[drag_i].old_y; draw(); } }); function update(){ for (var i=0; i < dots.length; i++){ if (drag_i != i){ var this_dot = dots[i]; if (float){ this_dot.vx += Math.random() - .5; this_dot.vy += Math.random() - .5; } this_dot.vx *= friction; this_dot.vy = this_dot.vy * friction + gravity; this_dot.x += this_dot.vx; this_dot.y += this_dot.vy; if (wrap){ if (this_dot.x > canvas.width + this_dot.radius){
this_dot.x -= canvas.width + this_dot.radius*2;
}
else if(this_dot.x < 0 - this_dot.radius){ this_dot.x += canvas.width + this_dot.radius*2; } if (this_dot.y > canvas.height + this_dot.radius){
this_dot.y -= canvas.height + this_dot.radius*2;
}
else if(this_dot.y < 0 - this_dot.radius){ this_dot.y += canvas.height + this_dot.radius*2; } } else if (!wrap) { if (this_dot.x > canvas.width – this_dot.radius){
this_dot.x = canvas.width – this_dot.radius;
this_dot.vx = this_dot.vx * bounce;
}
else if(this_dot.x < 0 + this_dot.radius){ this_dot.x = this_dot.radius; this_dot.vx = this_dot.vx * bounce; } if (this_dot.y > canvas.height – this_dot.radius){
this_dot.y = canvas.height – this_dot.radius;
this_dot.vy = this_dot.vy * bounce;
}
else if(this_dot.y < 0 + this_dot.radius){ this_dot.y = this_dot.radius; this_dot.vy = this_dot.vy * bounce; } } } } } function draw() { context.clearRect(0, 0, canvas.width, canvas.height); for (var i=0; i < dots.length; i++){ context.beginPath(); context.arc(dots[i].x, dots[i].y, dots[i].radius, 0, Math.PI * 2, false); context.fill(); context.closePath(); } } setInterval(function() { update(); draw(); }, 1000/fps);$("#gravity").click(function(){ if($("#gravity").is(':checked')){ gravity = 2; } else{ gravity = 0; } }); $("#wrap").click(function(){ if($("#wrap").is(':checked')){ wrap = true; } else{ wrap = false; } }); $("#float").click(function(){ if($("#float").is(':checked')){ float = true; } else{ float = false; } }); }); [/cc]Follow the whole Interactive Physics Animations via Javascript & Canvas series.

6 thoughts on “Interactive Physics Animations Javascript Canvas 17

  1. Nice Javascript Animation Series! Makes fun to play with!
    But what if i wanna create a timebased animation loop?

    I’ve started with adding:

    setInterval(function() {
    time += 100;
    if(time == 16000) time = 0;
    });

    And building a if(time >= 4000) structure within the update()-function
    to create different timebased animations… but this isnt as easy as i thought.

    Got some ideas?

    complete code:

    $(function () {
    var canvas, canvas_w, canvas_h, context, width, height, x, y = false, radius = 10;
    var total_dots = 10;
    var fps = 24;

    canvas = $("#canvas")[0];
    canvas.width = $(window).width();
    canvas.height = $(window).height();
    canvas_w = $("#canvas").width();
    canvas_h = $("#canvas").height();
    context = canvas.getContext("2d");

    var dots = new Array();
    var drag_i = -1;
    var gravity = 0;
    var friction = .98;
    var bounce = -.96;
    var wrap = false;
    var time = 0;

    var circColor = [];
    circColor[0] = '#260053';
    circColor[1] = '#40001b';
    circColor[2] = '#000057';
    circColor[3] = '#000000';

    var this_dot = {};

    function createDot(x, y, r, vx, vy){

    for (var j = 0; j < circColor.length; j++) { for (var i = 0; i < total_dots; i++) { var this_dot = { x: typeof(x) != 'undefined' ? x : Math.random() * canvas.width, y: typeof(y) != 'undefined' ? y : Math.random() * canvas.height, radius: typeof(r) != 'undefined' ? r : (i / total_dots) * Math.random() * 10, vx: typeof(vx) != 'undefined' ? vx : Math.random() * 30 - 10, vy: typeof(vy) != 'undefined' ? vy : Math.random() * 30 - 10, color: typeof(color) != 'undefined' ? color : circColor[j], speed: typeof(speed) != 'undefined' ? speed : 5 }; dots.push(this_dot); } } } createDot(); draw(); function update(){ for (var i=0; i < dots.length; i++){ var this_dot = dots[i]; console.log(time); if (time >= 0) {
    this_dot.vx += this_dot.speed * 3;
    this_dot.vy += this_dot.speed * 3;
    wrap = true;
    this_dot.vy = this_dot.vy + gravity;
    this_dot.x += this_dot.vx / this_dot.radius / 300;
    this_dot.y += this_dot.vy / this_dot.radius / 300;
    }

    if (time >= 4000) {

    this_dot.vx += this_dot.speed * 3;
    this_dot.vy += this_dot.speed * 3;
    wrap = true;
    this_dot.vy = this_dot.vy + gravity;
    this_dot.x += this_dot.vx / this_dot.radius / 100;
    this_dot.y += this_dot.vy / this_dot.radius / 100;
    }
    if (time >= 8000) {
    }

    //if wrap dots are bounce back
    if (wrap){
    if (this_dot.x > canvas.width + this_dot.radius){
    this_dot.x -= canvas.width + this_dot.radius*2;
    }
    else if(this_dot.x < 0 - this_dot.radius){ this_dot.x += canvas.width + this_dot.radius*2; } if (this_dot.y > canvas.height + this_dot.radius){
    this_dot.y -= canvas.height + this_dot.radius*2;
    }
    else if(this_dot.y < 0 - this_dot.radius){ this_dot.y += canvas.height + this_dot.radius*2; } } else if (!wrap) { if (this_dot.x > canvas.width - this_dot.radius){
    this_dot.x = -canvas.width - this_dot.radius;
    this_dot.vx = this_dot.vx * bounce;
    }
    else if(this_dot.x < 0 + this_dot.radius){ this_dot.x = this_dot.radius; this_dot.vx = this_dot.vx * bounce; } if (this_dot.y > canvas.height - this_dot.radius){
    this_dot.y = +canvas.height - this_dot.radius;
    this_dot.vy = this_dot.vy * bounce;
    }
    else if(this_dot.y < 0 + this_dot.radius){ this_dot.y = this_dot.radius; this_dot.vy = this_dot.vy * bounce; } } } } function draw() { //clean frame context.clearRect(0, 0, canvas.width, canvas.height); //update all dots for (var i=0; i < dots.length; i++){ context.beginPath(); context.arc(dots[i].x, dots[i].y, dots[i].radius, 0, Math.PI * 2, false); context.fillStyle = dots[i].color; context.fill(); context.closePath(); } } setInterval(function() { time += 100; if(time == 16000) time = 0; }); setInterval(function() { update(); draw(); }, 1000/fps); $(window).resize(function(){ canvas.width = $(window).width(); canvas.height = $(window).height(); }); });

    1. @Thomas –

      Hey, that’s a great idea. Taking a look at your code I think you’re 90% of the way there. It seems like your timing logic is a little off. I made a couple modifications and see it working great: http://jsfiddle.net/circlecube/zWE9L/5/embedded/result/

      Most notable is your if (time == x) statements, it seems you’d want those to be if(), if else() and else(), rather than all if, no? Also, the evaluations do not look right, i think you’d want to check that the time was between a certain range rather than just greater than one value:
      if (time >= 0 && time < 4000) {

      Hope that helps! Be sure and share what you end up with!


      $(function () {
      var canvas, canvas_w, canvas_h, context, width, height, x, y = false, radius = 10;
      var total_dots = 10;
      var fps = 24;

      canvas = $("#canvas")[0];
      canvas.width = $(window).width();
      canvas.height = $(window).height();
      canvas_w = $("#canvas").width();
      canvas_h = $("#canvas").height();
      context = canvas.getContext("2d");

      var dots = new Array();
      var drag_i = -1;
      var gravity = 0;
      var friction = .98;
      var bounce = -.96;
      var wrap = false;
      var time = 0;

      var circColor = [];
      circColor[0] = '#260053';
      circColor[1] = '#40001b';
      circColor[2] = '#000057';
      circColor[3] = '#000000';

      var this_dot = {};

      function createDot(x, y, r, vx, vy){

      for (var j = 0; j < circColor.length; j++) { for (var i = 0; i < total_dots; i++) { var this_dot = { x: typeof(x) != 'undefined' ? x : Math.random() * canvas.width, y: typeof(y) != 'undefined' ? y : Math.random() * canvas.height, radius: typeof(r) != 'undefined' ? r : (i / total_dots) * Math.random() * 10, vx: typeof(vx) != 'undefined' ? vx : Math.random() * 30 - 10, vy: typeof(vy) != 'undefined' ? vy : Math.random() * 30 - 10, color: typeof(color) != 'undefined' ? color : circColor[j], speed: typeof(speed) != 'undefined' ? speed : 5 }; dots.push(this_dot); } } } createDot(); draw(); function update(){ for (var i=0; i < dots.length; i++){ var this_dot = dots[i]; console.log(time); if (time >= 0 && time < 4000) { this_dot.vx += this_dot.speed * 3; this_dot.vy += this_dot.speed * 3; wrap = true; this_dot.vy = this_dot.vy + gravity; this_dot.x += this_dot.vx / this_dot.radius / 300; this_dot.y += this_dot.vy / this_dot.radius / 300; } else if (time >= 4000 && time < 8000) { this_dot.vx += this_dot.speed * 3; this_dot.vy += this_dot.speed * 3; wrap = true; this_dot.vy = this_dot.vy + gravity; this_dot.x += this_dot.vx / this_dot.radius / 100; this_dot.y += this_dot.vy / this_dot.radius / 100; } else if (time >= 8000) {
      }

      //if wrap dots are bounce back
      if (wrap){
      if (this_dot.x > canvas.width + this_dot.radius){
      this_dot.x -= canvas.width + this_dot.radius*2;
      }
      else if(this_dot.x < 0 - this_dot.radius){ this_dot.x += canvas.width + this_dot.radius*2; } if (this_dot.y > canvas.height + this_dot.radius){
      this_dot.y -= canvas.height + this_dot.radius*2;
      }
      else if(this_dot.y < 0 - this_dot.radius){ this_dot.y += canvas.height + this_dot.radius*2; } } else if (!wrap) { if (this_dot.x > canvas.width - this_dot.radius){
      this_dot.x = -canvas.width - this_dot.radius;
      this_dot.vx = this_dot.vx * bounce;
      }
      else if(this_dot.x < 0 + this_dot.radius){ this_dot.x = this_dot.radius; this_dot.vx = this_dot.vx * bounce; } if (this_dot.y > canvas.height - this_dot.radius){
      this_dot.y = +canvas.height - this_dot.radius;
      this_dot.vy = this_dot.vy * bounce;
      }
      else if(this_dot.y < 0 + this_dot.radius){ this_dot.y = this_dot.radius; this_dot.vy = this_dot.vy * bounce; } } } } function draw() { //clean frame context.clearRect(0, 0, canvas.width, canvas.height); //update all dots for (var i=0; i < dots.length; i++){ context.beginPath(); context.arc(dots[i].x, dots[i].y, dots[i].radius, 0, Math.PI * 2, false); context.fillStyle = dots[i].color; context.fill(); context.closePath(); } } setInterval(function() { time += 100; if(time >= 16000) time = 0;
      });

      setInterval(function() {
      update();
      draw();
      }, 1000/fps);

      $(window).resize(function(){
      canvas.width = $(window).width();
      canvas.height = $(window).height();
      });

      });​

      1. @Evan:

        Thanks for your advice! My first approach was built with paper.js… but i’ve noticed some performance issues on smartphones. that’s why i’m also here… Need to recode it in pure javascript.

        Have a look here http://ntro-design.de/spielplatz/
        The Goal is to build a smooth animation which fits to some background music.

        Funny feature would be something like this onclick function here:
        http://ntro-design.de/spielplatz/index2.php

        Which you ve shown before in one of your Interactive Physics Animations Javascript Canvas Series 🙂

        1. @Thomas – Very nice. I has a very good sense of atmosphere. I guess music will do that. The click is fun too.

          Are you getting a sense of how much faster using your own script vs using a library to do your animations? I’m curious as to what you’re finding.

Comments are closed.