Drawing mouse selection area (rubber band) with Pixi.js / html Canvas

3.1k Views Asked by At

I have a pixi.js html canvas with thousands of objects on it and I want the user to be able to zoom into it with the usual rectangular selection area. The brute force way to implement this would be to draw the rectangle on each mouse move and rerender the whole stage. But this seems like a waste of CPU. Plus this is so common in user interfaces, that I suspect that there is already some function in pixi.js or a plugin that solves this.

If there is no plugin: If I could save the whole buffer to some 2nd buffer when the user presses the mouse button, I could draw the rectangle on top, and on every mouse move, copy back the 2nd buffer to the primary buffer before drawing the rectangle. This would mean that I didn't have to redraw everything on every mouse move. But I don't think that one can clone the current buffer to some named secondary buffer.

Another alternative would be to move a rectangular DOM object on top of the canvas, but then I am afraid that the current pixel position will be hard to relate to the pixi.js / html5 canvas pixels.

Is there a better way? Or some plugin / search engine keyword that I'm missing? How would you implement a rubber band in html canvas or pixi.js ?

1

There are 1 best solutions below

0
On

I ended up solving this with a separate DOM object that is moved over the canvas. The solution also requires the new interaction manager in PIXI 4, that offers a single callback for any mouse movement over the canvas.

In the following, I assume that the canvas is placed at canvasLeft and canvasTop pixels with CSS.

   $(document.body).append("<div style='position:absolute; display:none; border: 1px solid black' id='tpSelectBox'></div>");

   renderer = new PIXI.CanvasRenderer(0, 0, opt);

   // setup the mouse zooming callbacks
   renderer.plugins.interaction.on('mousedown', function(ev) {
       mouseDownX = ev.data.global.x;
       mouseDownY = ev.data.global.y;                                                                                                    $("#tpSelectBox").css({left:mouseDownX+canvasLeft, top:mouseDownY+canvasTop}).show();
   });

   renderer.plugins.interaction.on('mousemove', function(ev) {
       if (mouseDownX == null)
           return;
       var x = ev.data.global.x;
       var y = ev.data.global.y;
       var selectWidth = Math.abs(x - mouseDownX);
       var selectHeight = Math.abs(y - mouseDownY);
       var minX = Math.min(ev.data.global.x, mouseDownX);
       var minY = Math.min(ev.data.global.y, mouseDownY);
       var posCss = {
            "left":minX+canvasLeft, 
            "top":minY+canvasTop,
            "width":selectWidth,
             "height":selectHeight
       };
       $("#tpSelectBox").css(posCss);
   });

   renderer.plugins.interaction.on('mouseup', function(ev) {
       $("#tpSelectBox").hide();
       mouseDownX = null;
       mouseDownY = null;
       $("#tpSelectBox").css({"width":0, "height":0});
   });

For older version of PIXI, here is an example of pan/zoom without a rectangle https://github.com/Arduinology/Pixi-Pan-and-Zoom/blob/master/js/functions.js In May 2015, the Interaction Manager got extended to allow easier pan/zoom handling https://github.com/pixijs/pixi.js/issues/1825 which is what I'm using here.