0

enter image description here I have a canvas that has some dots. when the mouse comes near the dots, the dots spread away and then when mouse is far from that dots, they come back to their last position.

The idea of work is:

when the mouse reaches the red environment(hover on the canvas) the previous dots should be removed by clearRect operation and place the new coordinates.

var offsetX = canvas.offsetLeft;
var offsetY = canvas.offsetTop;
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);

for(var i=0, j=particles.length;i<j;i++){
    ctx.clearRect(particles[i].x, particles[i].y, 2, 2);
}
for(var i=0, j=particles.length;i<j;i++){
    var xDistance = particles[i].x - mouseX;
    var yDistance = particles[i].y - mouseY;
    var distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
   if (distance < 20) {
        angle = Math.atan2(yDistance,xDistance);
        particles[i].x += Math.cos(angle) * distance;
        particles[i].y += Math.sin(angle) * distance;
   }
    ctx.fillRect(particles[i].x, particles[i].y, 2, 2);
}
  1. The first problem is I dont know why the clearRect doesn't clear the previous dots completely because I want to remove the previous dots and placing dots with new coordinates
  2. The second problem is creating a jumping situation cuz I want to be like animation
  3. The third problem is returning the dots to their main positions. The point is that this code has been wrote without any library.

please help me to solve my problems.

here is demo of my code: jsfiddle

2 Answers2

3

The problem

Your problems is tracking what to clear. The draw function may at times draw over 3 or 4 pixels rather than 2 as you are not rounding the position to ints.

The simple solution.

Rather than clear each dot just clear the whole canvas. One call to clear is much quicker than many, the size makes little difference as it is hardware that does the clearing.

Replace the clear loops you have in the mouse events to

/* remove
for(var i=0, j=particles.length;i<j;i++){
    ctx.clearRect(particles[i].x, particles[i].y, 2, 2);
} */

ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);

For more details on this type of animation see https://stackoverflow.com/a/28391058/3877726

Community
  • 1
  • 1
Blindman67
  • 51,134
  • 11
  • 73
  • 136
1

How about the below? Here all the rendering comes in one setInterval function and the mouse event just updates parameters associated with each particle. These parameters are used by the rendering function whenever it is called. This allows you to introduce the "gradual" effect you were looking for. Also you don't have to worry about clearing tiny pieces of the canvas as the setInterval function clears everything and redraws everything based on values that were either initially set or what the mouse event set.

I added a an x0 and y0 property to the particle object to let it know where it should return to if it has been moved by the mouse event. deltaY and deltaX let us know how much to increment each coordinate element by.

Here is my fiddle: https://jsfiddle.net/kotman12/5p9tko50/

    var canvas    = document.getElementById("scene");
    var ctx       = canvas.getContext("2d");
    var particles = [];

    function drawScene(){

     canvas.width = png.width+200;
     canvas.height = png.height+200;
     //canvas.addEventListener("mouseover", doMouseOver, false);
      canvas.addEventListener('mousemove', move, false);
     //canvas.addEventListener('mouseout', doMouseOut, false);
     //canvas.addEventListener('click', doClick, false);

     ctx.drawImage(png, 0, 0);

     var data = ctx.getImageData(0, 0, png.width, png.height);
     ctx.clearRect(0,0,canvas.width, canvas.height);

     for (var y = 0, y2 = data.height; y < y2; y=y+4) {
      for (var x = 0, x2 = data.width; x < x2; x=x+4) {
       if (data.data[(y * 4 * data.width) + (x * 4) + 3] > 128) {
        var particle = {
         x : x+100,
         y : y+100,
              y0: y+100,
              x0: x+100,
              xDelta: 0,
              yDelta: 0
        };
        particles.push(particle);
       }
      }
     }

        console.log(particles);
     ctx.fillStyle = "White";

      var renderStuff = setInterval(function() {
      ctx.clearRect(0,0,canvas.width, canvas.height);
     for(var i=0, j=particles.length;i<j;i++){
      var particle = particles[i];
       ctx.save();
       // ctx.rotate((Math.PI/180)*2);
       // ctx.translate(100, 100);
        if(Math.sqrt(Math.pow(particle.x-particle.x0,2)+Math.pow(particle.y-particle.y0, 2)) > 1){
           particle.x += particle.xDelta/200;
           particle.y += particle.yDelta/200;
        }else{
           particle.x = particle.x0;
           particle.y =  particle.y0;
        }
      ctx.fillRect(particle.x, particle.y, 2, 2);
       // ctx.restore();
     }
      }, 1);

    }

function move(e){
 // madami k hover hast
 // console.log(2);
 mouseX = parseInt(e.clientX - offsetX);
  mouseY = parseInt(e.clientY - offsetY);
 //ctx.clearRect(0,0,canvas.width, canvas.height);
 /*for(var i=0, j=particles.length;i<j;i++){
  ctx.clearRect(particles[i].x, particles[i].y, 2, 2);
 }*/
 for(var i=0, j=particles.length;i<j;i++){
     var xDistance = particles[i].x - mouseX;
     var yDistance = particles[i].y - mouseY;
     var distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);

    if (distance < 20) {
      angle = Math.atan2(yDistance,xDistance);
      particles[i].x += Math.cos(angle) * distance;
      particles[i].y += Math.sin(angle) * distance;

      particles[i].yDelta = particles[i].y0 - particles[i].y;
      particles[i].xDelta = particles[i].x0 - particles[i].x;
    }

   /* else if(Math.abs(particle.x-particle.x0) > .01){
       particle.x += particle.xDelta/400;
       particle.y += particle.yDelta/400;
    }*/
  //ctx.fillRect(particles[i].x, particles[i].y, 2, 2);
 }
}
Luke Kot-Zaniewski
  • 1,161
  • 11
  • 12