Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rendering a line that follows the mouse eventually slows down with multiple lines on the canvas using Fabricjs

I have code that draws lines with each click on the canvas. It shows a previee of the line with the mouse move, and ends that line and starts a new one from the same position when mouse:down.4

I smashed two example codes together to generate this code. One of the examples allow me to simply draw lines as long as I hold the mouse down, and this forms the main guts of the app. The strange part is that with the original example code, it doesn't matter how many lines I have on the canvas, the new lines always move smoothly on mouse:move. When I added the function of not having to hold down the mouse when drawing, for some reason after I add more lines to the canvas the movable line get very slow in reacting to the mouse. You can see what I mean below. The first canvas with red lines is the original example code. The second is my tweak with a bunch of lines. The third is my tweak again, I just refreshed the page and got rid of all the lines. You can see how fast the line reacts with only a few lines on the screen.

enter image description here

The mouse:move code is pretty much identical in both of these, so I can't for the life of me figure out why my tweak is so excruciatingly slow after about 50 objects are added.

Red line fiddle, notice how reactive the new line is when the mouse is moving.: https://codepen.io/morrowsend/pen/YgBxvj?editors=0010

My tweak fiddle, notice the newest line is delayed in reacting to mouse moves. This is due to the mouse:move having the canvas.renderAll() in it. The more lines on the canvas the less reactive it is.: https://codepen.io/morrowsend/pen/EMeWpM

Now you might say "Just use an animation" well, I tried that. I don't quite get those yet, but I tried that using Fabric's animate, which I didn't really like. The fastest it can go is 1ms, but that isn't really very reactive and it gives me artifacts after a few lines are drawn, sometimes the new line gets offset from the mouse. Also, it puts animates the previous line such that is moves the endpoint to the newest line. It is easier to see this behavior. In the image below, I have a lot of lines already in the animate code and you can see it behaves poorly. When I refresh and start with a clean canvas, things seem to work ok at first, but the more lines I get, the more incorrect the result become:enter image description here

Here's my animate code using Fabric's built-in animate. https://codepen.io/morrowsend/pen/YgBxvj?editors=0010

I found an example of just using animate with canvas, but I'd like to have my lines editable by the user after they've been drawn (yet to be implemented just yet). It seems smooth, however it doesn't have man objects on the canvas yet, so it may bog down just like mine if more were added: html canvas a line following mouse relavent fiddle: http://jsfiddle.net/dFjodorov/XQpzU/5171/

How can I smoothly animate drawing a line even if there are a few hundred objects on the screen? I feel like I've tried a lot of things to no avail.

like image 715
morrows_end Avatar asked Nov 04 '25 04:11

morrows_end


1 Answers

Set x2 and y2 value of line on mouse move handler, no need to use animation. And remove mouse handleres on edit mode and add on draw mode.

var canvas = new fabric.Canvas('c');

var isDown;

var point1;
var line = null;
var drawMode = true;

function addListener() {
  canvas.on('mouse:down', onMouseDown);
  canvas.on('mouse:dblclick', onDblClick);
  canvas.on('mouse:move', onMouseMove)
}

function removeListener() {
  canvas.off('mouse:down', onMouseDown);
  canvas.off('mouse:dblclick', onDblClick);
  canvas.off('mouse:move', onMouseMove)
}

function setSelectable(value) {
  canvas.forEachObject(function(object) {
    object.selectable = value;
    object.setCoords()
  })
  canvas.selection = value;
}

function onMouseDown(options) {
  isDown = true;
  var pointer = canvas.getPointer(options.e);
  var points = [pointer.x, pointer.y, pointer.x, pointer.y];
  line = new fabric.Line(points, {
    stroke: 'black',
    hasControls: false,
    hasBorders: false,
    lockMovementX: false,
    lockMovementY: false,
    hoverCursor: 'default',
    selectable: false
  });
  canvas.add(line);
};

function onDblClick() {
  //alert('dblclick')
  line.setCoords()
  isDown = false;
  line = null;
};

function onMouseMove(o) {
  if (!isDown) return;
  var pointer = canvas.getPointer(o.e);
  line.set({
    x2: pointer.x,
    y2: pointer.y
  });
  canvas.requestRenderAll();
}; //end mouse:move

document.addEventListener("keydown", function(event) {
  var keyPressed = event.keyCode;
  if (keyPressed === 27) { //escape key code
    edit();
  } //end if delete  
}); //end keydown


function draw() {
  drawMode = true
  addListener()
  setSelectable(false);
  // console.log(drawMode)
}


function edit() {
  // bool = false
  if (drawMode) {
    var canvas_objects = canvas._objects;
    var sel = canvas_objects[canvas_objects.length - 1]; //Get last object 
    canvas.remove(sel);
    drawMode = false;
  }
  removeListener();
  setSelectable(true);
}
var enableDraw = document.getElementById("drawEnabled");
enableDraw.addEventListener("click", draw);
var enableEdit = document.getElementById("editEnabled");
enableEdit.addEventListener("click", edit);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script>
        
<button id="drawEnabled">Draw Mode</button>
<button id="editEnabled">Edit Mode</button>
<canvas id="c" width="500" height="500" style="border:1px solid #aaa"></canvas>
like image 64
Durga Avatar answered Nov 07 '25 15:11

Durga



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!