Kathak Dabhi
Kathak Dabhi

Reputation: 399

fabricjs Object Selection is not working with panning

I have been working with fabricjs for creating a drawing tool. There is a large image as the canvas background and panning feature. User can drag and drop objects on the canvas. But the Object drop on the canvas is not selected when we pan the canvas.

To check the issue drag and drop the circle on lower part of the view-port than pan the canvas upward to half and check the object put on the canvas is not selectable. Even thought if you try to move the object outside the view-port it will be not selectable.

var canvas = new fabric.Canvas('c', {
  selection: false
});
canvas.perPixelTargetFind = true;
canvas.targetFindTolerance = 4;

fabric.Image.fromURL('https://image.ibb.co/gFtpp7/8c39a3193e05996911c0d9c1df001a80.jpg', function(img) {
  var imgObj = img.set({
    left: 0,
    top: 0,
  });
  imgObj.scaleToWidth(canvas.width);
  canvas.width = imgObj.width;
  canvas.setBackgroundImage(imgObj, canvas.renderAll.bind(canvas));
});
var drawingPointer = {
  status: false,
  previousObj: false
};

function handleDragStart(e) {
  var draggables = document.querySelectorAll('#manholes div.drag-obj');
  [].forEach.call(draggables, function(img) {
    img.classList.remove('img_dragging');
  });
  this.classList.add('img_dragging');
}

function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault(); // Necessary. Allows us to drop.
  }
  e.dataTransfer.dropEffect = 'copy'; // See the section on the DataTransfer object.
  // NOTE: comment above refers to the article (see top) -natchiketa
  return false;
}

function handleDragEnter(e) {
  // this / e.target is the current hover target.
  this.classList.add('over');
}

function handleDragLeave(e) {
  this.classList.remove('over'); // this / e.target is previous target element.
}

function handleDrop(e) {
  // this / e.target is current target element.
  if (e.stopPropagation) {
    e.stopPropagation(); // stops the browser from redirecting.
  }

  var img = document.querySelector('#manholes div.drag-obj.img_dragging');
  if (img.getAttribute('draggable') != "true") {
    return;
  }
  img.setAttribute('draggable', false);
  var objName = img.getAttribute('data-name');
  var circle = new fabric.Circle({
    radius: 28,
    fill: '#00FFFF',
    originX: 'center',
    originY: 'center'
  });
  var pointer = canvas.getPointer(e)
  var text = new fabric.Text(objName, {
    fontSize: 13,
    originX: 'center',
    originY: 'center'
  });
  var group = new fabric.Group([circle, text], {
    left: pointer.x - 28,
    top: pointer.y - 28,
    hasRotatingPoint: false,
    dataName: objName,
  });
  group.setControlsVisibility({
    mt: false,
    ml: false,
    mr: false,
    mb: false
  });
  group.setCoords();
  canvas.add(group);
  return false;
}

function handleDragEnd(e) {
  var draggables = document.querySelectorAll('#manholes div.drag-obj');
  [].forEach.call(draggables, function(img) {
    img.classList.remove('img_dragging');
  });
}
if (Modernizr.draganddrop) {
  var draggables = $('#manholes div.drag-obj');
  [].forEach.call(draggables, function(img) {
    img.addEventListener('dragstart', handleDragStart, false);
    img.addEventListener('dragend', handleDragEnd, false);
  });
  // Bind the event listeners for the canvas
  var canvasContainer = document.querySelector('.canvas-container');
  canvasContainer.addEventListener('dragenter', handleDragEnter, false);
  canvasContainer.addEventListener('dragover', handleDragOver, false);
  canvasContainer.addEventListener('dragleave', handleDragLeave, false);
  canvasContainer.addEventListener('drop', handleDrop, false);
} else {
  // Replace with a fallback to a library solution.
  alert("This browser doesn't support the HTML5 Drag and Drop API.");
}

var drawingPointer = {
  status: false
};

function handleMouseMovement(o) {
  if (drawingPointer.status == 'panning') {
    //var delta = new fabric.Point(o.e.movementX, o.e.movementY);
    //canvas.relativePan(delta);
    var e = o.e,
      imgH = this.backgroundImage.height,
      imgW = this.backgroundImage.width;
    var zoom = canvas.getZoom();
    if (zoom < 0.4) {
      this.viewportTransform[4] = 200 - imgW * zoom / 2;
      this.viewportTransform[5] = 200 - imgH * zoom / 2;
    } else {
      this.viewportTransform[4] += e.clientX - this.lastPosX;
      this.viewportTransform[5] += e.clientY - this.lastPosY;
      if (this.viewportTransform[4] >= 0) {
        this.viewportTransform[4] = 0;
      } else if (this.viewportTransform[4] < canvas.getWidth() - imgW * zoom) {
        this.viewportTransform[4] = canvas.getWidth() - imgW * zoom;
      }
      if (this.viewportTransform[5] >= 0) {
        this.viewportTransform[5] = 0;
      } else if (this.viewportTransform[5] < canvas.getHeight() - imgH * zoom) {
        this.viewportTransform[5] = canvas.getHeight() - imgH * zoom;
      }
    }
    this.requestRenderAll();
    this.lastPosX = e.clientX;
    this.lastPosY = e.clientY;
  }
}

function handleMouseDown(o) {
  if (!o.target && !drawingPointer.status) {
    this.lastPosX = o.e.clientX;
    this.lastPosY = o.e.clientY;
    canvas.defaultCursor = 'url("https://image.ibb.co/g94jNS/icon_grab.png") 8 2 ,auto';
    drawingPointer.status = 'panning';
  }
}

function handleMouseUp(o) {
  if (!o.target && drawingPointer.status == 'panning') {
    drawingPointer.status = false;
    canvas.defaultCursor = 'default';
  }
}
canvas.on('mouse:move', handleMouseMovement);
canvas.on('mouse:down', handleMouseDown);
canvas.on('mouse:up', handleMouseUp);
.drag-obj {
    width: 56px;
    height: 56px;
    display: inline;
    margin: 5px;
    border: 1px solid #CCCCCC;
    float: left;
    padding: 16px 5px;
    text-align: center;
    border-radius: 50%;
    background-color: #CCCCCC;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<canvas id="c" width="400" height="400" style="border: 1px solid;margin: 10px;"></canvas>
<div class="row">
  <div class="col-xs-10 col-xs-offset-1">
    <h3>Drag and Drop Objects</h3>
    <div class="drag-obj-container" id="manholes">
      <div draggable="true" class="drag-obj" data-name="OV01">
        OV01
      </div>
      <div draggable="true" class="drag-obj" data-name="OV02">
        OV02
      </div>
      <div draggable="true" class="drag-obj" data-name="OV03">
        OV03
      </div>
    </div>
  </div>
</div>
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>

Upvotes: 3

Views: 2420

Answers (1)

Durga
Durga

Reputation: 15614

After panning (onMouseUp) , set all object coords using setCoords.

DEMO

var canvas = new fabric.Canvas('c', {
  selection: false
});
canvas.perPixelTargetFind = true;
canvas.targetFindTolerance = 4;

fabric.Image.fromURL('https://image.ibb.co/gFtpp7/8c39a3193e05996911c0d9c1df001a80.jpg', function(img) {
  var imgObj = img.set({
    left: 0,
    top: 0,
  });
  imgObj.scaleToWidth(canvas.width);
  canvas.width = imgObj.width;
  canvas.setBackgroundImage(imgObj, canvas.renderAll.bind(canvas));
});
var drawingPointer = {
  status: false,
  previousObj: false
};

function handleDragStart(e) {
  var draggables = document.querySelectorAll('#manholes div.drag-obj');
  [].forEach.call(draggables, function(img) {
    img.classList.remove('img_dragging');
  });
  this.classList.add('img_dragging');
}

function handleDragOver(e) {
  if (e.preventDefault) {
    e.preventDefault(); // Necessary. Allows us to drop.
  }
  e.dataTransfer.dropEffect = 'copy'; // See the section on the DataTransfer object.
  // NOTE: comment above refers to the article (see top) -natchiketa
  return false;
}

function handleDragEnter(e) {
  // this / e.target is the current hover target.
  this.classList.add('over');
}

function handleDragLeave(e) {
  this.classList.remove('over'); // this / e.target is previous target element.
}

function handleDrop(e) {
  // this / e.target is current target element.
  if (e.stopPropagation) {
    e.stopPropagation(); // stops the browser from redirecting.
  }

  var img = document.querySelector('#manholes div.drag-obj.img_dragging');
  if (img.getAttribute('draggable') != "true") {
    return;
  }
  img.setAttribute('draggable', false);
  var objName = img.getAttribute('data-name');
  var circle = new fabric.Circle({
    radius: 28,
    fill: '#00FFFF',
    originX: 'center',
    originY: 'center'
  });
  var pointer = canvas.getPointer(e)
  var text = new fabric.Text(objName, {
    fontSize: 13,
    originX: 'center',
    originY: 'center'
  });
  var group = new fabric.Group([circle, text], {
    left: pointer.x - 28,
    top: pointer.y - 28,
    hasRotatingPoint: false,
    dataName: objName,
  });
  group.setControlsVisibility({
    mt: false,
    ml: false,
    mr: false,
    mb: false
  });
  group.setCoords();
  canvas.add(group);
  return false;
}

function handleDragEnd(e) {
  var draggables = document.querySelectorAll('#manholes div.drag-obj');
  [].forEach.call(draggables, function(img) {
    img.classList.remove('img_dragging');
  });
}
if (Modernizr.draganddrop) {
  var draggables = $('#manholes div.drag-obj');
  [].forEach.call(draggables, function(img) {
    img.addEventListener('dragstart', handleDragStart, false);
    img.addEventListener('dragend', handleDragEnd, false);
  });
  // Bind the event listeners for the canvas
  var canvasContainer = document.querySelector('.canvas-container');
  canvasContainer.addEventListener('dragenter', handleDragEnter, false);
  canvasContainer.addEventListener('dragover', handleDragOver, false);
  canvasContainer.addEventListener('dragleave', handleDragLeave, false);
  canvasContainer.addEventListener('drop', handleDrop, false);
} else {
  // Replace with a fallback to a library solution.
  alert("This browser doesn't support the HTML5 Drag and Drop API.");
}

var drawingPointer = {
  status: false
};

function handleMouseMovement(o) {
  if (drawingPointer.status == 'panning') {
    //var delta = new fabric.Point(o.e.movementX, o.e.movementY);
    //canvas.relativePan(delta);
    var e = o.e,
      imgH = this.backgroundImage.height,
      imgW = this.backgroundImage.width;
    var zoom = canvas.getZoom();
    if (zoom < 0.4) {
      this.viewportTransform[4] = 200 - imgW * zoom / 2;
      this.viewportTransform[5] = 200 - imgH * zoom / 2;
    } else {
      this.viewportTransform[4] += e.clientX - this.lastPosX;
      this.viewportTransform[5] += e.clientY - this.lastPosY;
      if (this.viewportTransform[4] >= 0) {
        this.viewportTransform[4] = 0;
      } else if (this.viewportTransform[4] < canvas.getWidth() - imgW * zoom) {
        this.viewportTransform[4] = canvas.getWidth() - imgW * zoom;
      }
      if (this.viewportTransform[5] >= 0) {
        this.viewportTransform[5] = 0;
      } else if (this.viewportTransform[5] < canvas.getHeight() - imgH * zoom) {
        this.viewportTransform[5] = canvas.getHeight() - imgH * zoom;
      }
    }
    this.requestRenderAll();
    this.lastPosX = e.clientX;
    this.lastPosY = e.clientY;
  }
}

function handleMouseDown(o) {
  if (!o.target && !drawingPointer.status) {
    this.lastPosX = o.e.clientX;
    this.lastPosY = o.e.clientY;
    canvas.defaultCursor = 'url("https://image.ibb.co/g94jNS/icon_grab.png") 8 2 ,auto';
    drawingPointer.status = 'panning';
  }
}

function handleMouseUp(o) {
  if (!o.target && drawingPointer.status == 'panning') {
    drawingPointer.status = false;
    canvas.defaultCursor = 'default';
    canvas.forEachObject(function (object){
      object.setCoords();
    });
    canvas.requestRenderAll();
  }
}
canvas.on('mouse:move', handleMouseMovement);
canvas.on('mouse:down', handleMouseDown);
canvas.on('mouse:up', handleMouseUp);
.drag-obj {
    width: 56px;
    height: 56px;
    display: inline;
    margin: 5px;
    border: 1px solid #CCCCCC;
    float: left;
    padding: 16px 5px;
    text-align: center;
    border-radius: 50%;
    background-color: #CCCCCC;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<canvas id="c" width="400" height="400" style="border: 1px solid;margin: 10px;"></canvas>
<div class="row">
  <div class="col-xs-10 col-xs-offset-1">
    <h3>Drag and Drop Objects</h3>
    <div class="drag-obj-container" id="manholes">
      <div draggable="true" class="drag-obj" data-name="OV01">
        OV01
      </div>
      <div draggable="true" class="drag-obj" data-name="OV02">
        OV02
      </div>
      <div draggable="true" class="drag-obj" data-name="OV03">
        OV03
      </div>
    </div>
  </div>
</div>
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js"></script>

Upvotes: 5

Related Questions