3

I'm testing an effect where pixels follow your mouse. http://dukevin.com/pixel

But my current implementation is very resource heavy; create divs that fill the page and query them to change the color.

Is there a way that can produce a similar effect without being so resource heavy? An idea I have is to generate the divs on the fly as the mouse moves, and remove them when faded out.

$(document).ready(function(){
    var body = $('body');
    for(var i = 0; i < Math.floor($(window).width()/30)*Math.floor($(window).height()/30) ; i++)
        body.append("<div class=box></div>");
});

var colors = ["#f00","#c00","#d00","#e00"];
$(document).on('mouseenter', '.box', function (event) {
    $(this).css({
        backgroundColor: colors[Math.floor(Math.random() * colors.length)],
        opacity: 1
    });
}).on('mouseleave', '.box', function (event) {
    $(this).css('opacity', 0);
});
body {
    width: 100%;
    margin: 0;
    padding: 0;
    line-height: 0px;
    background-color: #333;
}
.box {
    display: inline-block;
    vertical-align:top;
    z-index: -1;
    width: 30px;
    height: 30px;
    transition: all 2s ease;
}
.box:hover {
    transition: all 0s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Mulan
  • 129,518
  • 31
  • 228
  • 259
dukevin
  • 22,384
  • 36
  • 82
  • 111
  • do you have to make it with DOM? why not canvas/webGL or SVG? – Rafał Łużyński Apr 12 '15 at 09:05
  • no I don't have to, anything that creates a similar effect is ok. As I said, I don't think I need a grid to cover the whole page, or even a canvas. I just need colors to follow the mouse – dukevin Apr 12 '15 at 09:08
  • That looks really cool. It's out of my league, but would setting `box:hover` in the css work better, or is that basically what you are already doing? EDIT: Maybe use http://stackoverflow.com/questions/4517198/how-to-get-mouse-position-in-jquery-without-mouse-events to get the mouse position and generate the divs under it? – Drazisil Apr 12 '15 at 09:08
  • If you use `Math.floor` the last color will never be selected (well, almost never)... – Shikkediel Apr 12 '15 at 09:17

2 Answers2

6

<3

Click Run code snippet below, then drag your mouse around in the black area.

All in all, this is pretty efficient tho. Once a pixel is created, it is added to the pixels array. In the loop, the update(delta) function is called on each pixel, and true is returned if the pixel's alpha is above 0. Once a pixel's alpha drops below zero, it is deleted.

This only uses:

  • 1x HTML element
  • 2x event listeners

var canvas    = document.querySelector("canvas");
var ctx       = canvas.getContext("2d");
var pixelSize = 20; // px, 20px squares
var speed     = 33; // ms, (1000ms / 33ms = ~30fps)
var ttl       = 2000.0; // ms, (pixel fade out time)

// Pixel constructor
function Pixel(x, y, size, rgba) {
  
  if (!(this instanceof Pixel)) {
    return new Pixel(x, y, size, rgba);
  }
  
  // pixel update function; called once per tick
  function update(delta) {
    // decrease the alpha of this pixel by delta/ttl;
    rgba.a = (rgba.a - (delta/ttl)).toFixed(2);
    // redraw this pixel
    ctx.clearRect(x, y, size, size);
    ctx.fillStyle = rgbaStr(rgba);
    ctx.fillRect(x, y, size, size);
    // return true if pixel alpha is still above 0
    // return false when this pixel is no longer visible (garbage collect)
    return rgba.a > 0;
  }
  
  // export the update function
  this.update = update;
}

// rgba helper
function rgba(r, g, b, a) {
    return {r:r, g:g, b:b, a:a};
}

// convert rgba to CSS string
function rgbaStr(rgba) {
    var args = [rgba.r, rgba.g, rgba.b, rgba.a];
    return "rgba(" + args.join(",") + ")";
}

// all "active" pixels
var pixels = [];

// Pixel factory function
function createPixel(x, y, size, rgba) {
  // create pixel
  var pixel = new Pixel(
    Math.round(x / size) * size, // snap to grid
    Math.round(y / size) * size, // snap to grid
    size,                        // pixel/grid size
    rgba                         // rgba color
  );
  // add pixel to array
  pixels.push(pixel);
  // return constructed pixel      
  return pixel;
}

// the loop
function loop(now) {
    // calculate delta
    var delta = Date.now() - now;
    // loop through each pixel
    pixels.forEach(function(pixel, idx) {
        // if pixel is faded out completely...
        if (!pixel.update(delta)) {
           // delete the pixel
           pixels.splice(idx, 1); 
        }
    });
    // loop again (based on speed)
    setTimeout(loop.bind(null, Date.now()), speed);
}

// size canvas to full size of window
function resizeCanvas(event) {
  ctx.canvas.width = window.innerWidth;
  ctx.canvas.height = window.innerHeight;
}

// setup
function init() {
  // set canvas size
  resizeCanvas();

  // resize canvas size whenever window is resized
  window.addEventListener("resize", resizeCanvas);
  
  // create pixels on mousemove
  canvas.addEventListener("mousemove", function(event) {
    createPixel(
      event.clientX,  // mouse x position
      event.clientY,  // mouse y position
      pixelSize,      // the pixelSize defined above
      rgba(255,0,0,1) // start at 100% red
    );
  });
  
  // start the loop
  loop(Date.now());
}

// start everything
init();
html, body {
    width: 100%;
    height: 100%;
    margin: 0;
}

body {
    background-color: black;
    color: white;
}
<canvas></canvas>
Mulan
  • 129,518
  • 31
  • 228
  • 259
0

I would create canvas overlay that is on top of everything and render it there, it should be significantly faster since you don't create and remove DOM objects all the time.

Rafał Łużyński
  • 7,083
  • 5
  • 26
  • 38
  • Ok, but what about objects that get created on the fly? For example, a dom object is created whenever the mouse moves, and deletes after it has faded out. There will be only around 5 objects then and doesn't need to fill the whole screen. Is this feasable? – dukevin Apr 12 '15 at 09:13
  • @Kevin Duke sounds feasible. It will never be as performant as the canvas approach however. That may or may not matter. – Ben Aston Apr 12 '15 at 09:18
  • @Kevin Duke if you ask me if it's feasable with canvas, then sure it is. Canvas is transparent beside things you render and you clear it every frame to update drawings. – Rafał Łużyński Apr 12 '15 at 09:23