fabricjs 对象选择不适用于平移

fabricjs Object Selection is not working with panning

我一直在使用 fabricjs 创建绘图工具。有一个大图像作为 canvas 背景和平移功能。用户可以在 canvas 上拖放对象。但是当我们平移 canvas.

时,未选择 canvas 上的对象放置

要检查问题,请将圆圈拖放到视口下部,然后将 canvas 向上平移到一半,然后检查放在 canvas 上的对象是不可选择的。甚至认为如果您尝试将对象移到视口之外,它将无法选择。

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>

平移 (onMouseUp) 后,使用 setCoords.

设置所有对象坐标

演示版

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>