Fabric.js link/unlink 2 objects - javascript

I have 2 rectangles objects that i use as background template. Controls are disabled, they just can be dragged onto canvas. I would like them to behave like a group: when one of them is selected, the other one is too so they can be moved only together. I don't want to group them, mainly because at export, i need to remove one of them and i cannot find a way to do this... (unless there is a simple way to select item in a group but neither group.item().remove(), neither functions to ungroup my items or seems to work on my export function)

var canvas = new fabric.Canvas('c');
var evented = false;
var rect1 = new fabric.Rect({
left: 50,
top: 30,
fill: 'blue',
width: 150,
height: 150,
hasBorders:false,
hasControls:false
});
var rect2 = new fabric.Rect({
left: 210,
top: 30,
fill: 'magenta',
width: 150,
height: 150,
hasBorders:false,
hasControls:false
});
canvas.add(rect1,rect2);
function rect1MouseDown(option){
this.mousesDownLeft = this.left;
this.mousesDownTop = this.top;
this.rect2Left = rect2.left;
this.rect2Top = rect2.top;
}
function rect1MouseMove(option){
rect2.left = this.rect2Left+ this.left - this.mousesDownLeft ;
rect2.top = this.rect2Top+ this.top- this.mousesDownTop;
rect2.setCoords();
}
function rect2MouseDown(option){
this.mousesDownLeft = this.left;
this.mousesDownTop = this.top;
this.rect1Left = rect1.left;
this.rect1Top = rect1.top;
}
function rect2MouseMove(option){
rect1.left = this.rect1Left+ this.left - this.mousesDownLeft ;
rect1.top = this.rect1Top+ this.top- this.mousesDownTop;
rect1.setCoords();
}
register();
function register(){
if(evented) return;
rect1.on('moving', rect1MouseMove);
rect1.on('mousedown', rect1MouseDown);
rect2.on('moving',rect2MouseMove);
rect2.on('mousedown',rect2MouseDown);
evented = true;
}
function unRegister(){
rect1.off('moving');
rect1.off('mousedown');
rect2.off('moving');
rect2.off('mousedown');
evented = false;
}
canvas {
border: blue dotted 2px;
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.min.js"></script>
<button onclick='register()'>Register Event</button>
<button onclick='unRegister()'>Unregister Event</button><br>
<canvas id="c" width="400" height="400"></canvas>
Alternatively you can set left and top to another object while moving one, so when ever you are moving one object according to that position set position for rest.

Related

How do I get the Mouseover Target's X&Y for a CreateJS Shape

I am trying to change the FILL COLOR of a SHAPE on Mouseover & Mouseout. Most examples I've seen use the events StageX and StageY coordinates. However, those coordinates are the X & Y positions of the mouse at the time of the event. This forces the object to suddently move when "redrawn".
Other examples ask you to call "GetBounds"...which is not available for Shape objects.
I don't want the object to move simply because I am changing the FillColor
I see nothing resembling the top/left in the EVENT.TARGET
Q: How do I calculate or retrieve the Shapes current X/Y (top/left) coordinates?
THE HTML & JAVASCRIPT:
<style>
.dashboard { height:600px; }
.dashboard header { }
.dashboard aside { vertical-align:top; display:inline-block; }
.dashboard aside.control-bar { border: solid 1px black; border-radius: 3px; padding: 5px; height: 100%; width:10%; margin-right: 5px; }
.dashboard aside.control-bar {}
.dashboard aside.control-bar .btn { width:95%; }
.dashboard section { vertical-align:top; display:inline-block; }
.dashboard section.desktop { height:100%; min-width:80%; border:solid 1px black; border-radius: 3px; }
.dashboard section.desktop canvas { height:98%; width:99%; }
.dashboard footer { margin-top:5px; padding:5px; }
</style>
<div class="row">
<div class="col-md-12">
<main role="main" class="dashboard pb-3">
<header qwik-control="header">
<h3>Dashboard</h3>
</header>
<aside class="control-bar">
<center>
<h6>UI Controls</h6>
<a id="btnCreateNode" class="btn btn-sm btn-dark">Create Node</a>
</center>
</aside>
<section class="desktop">
<canvas id="demoCanvas"></canvas>
</section>
<footer>
<center>
<h5 style="color:#C7C9CD;">Button Controls</h5>
</center>
</footer>
</main>
</div>
</div>
<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<script type="text/javascript">
var stage = null,
loader = null;
// GLOBALS
var _PROPERTIES = { node: { y: 100, x: 200, fillColor: '#F9FAFB', fillOverColor: '#FCFCC2', strokeColor: '#000' } };
function node_create(){
console.log('node_create');
var top = Math.random() * 500;
var left = Math.random() * 500;
var width = _PROPERTIES.node.x;
var height = _PROPERTIES.node.y;
// Create
var node = new createjs.Shape();
node.graphics.beginStroke(_PROPERTIES.node.strokeColor);
node.graphics.beginFill(_PROPERTIES.node.fillColor);
node.graphics.setStrokeStyle(1);
node.snapToPixel = true;
node.graphics.drawRect(left, top, width, height);
node.graphics.endFill();
node.name = name;
node.overColor = _PROPERTIES.node.fillOverColor;
node.outColor = _PROPERTIES.node.fillColor;
// Events
node.on("mouseover", node_mouseover);
node.on("mouseout", node_mouseout);
// Display
stage.addChild(node);
stage.update();
};
function node_mouseover(evt) {
console.log('node_mouseover');
// COORDS is the events coordinates...not the targets Top/Left (e.g. cursor)
// How do I get the X/Y of the target? (getBounds does not exist for Shapes)
var target = evt.target;
var coords = { x: evt.stageX, y: evt.stageY };
target.graphics.clear();
target.graphics.beginStroke(_PROPERTIES.node.strokeColor);
target.graphics.beginFill(target.overColor);
target.graphics.drawRect(coords.x, coords.y, _PROPERTIES.node.x, _PROPERTIES.node.y);
target.graphics.endFill();
stage.update();
};
function node_mouseout(evt) {
console.log('node_mouseout');
// COORDS is the events coordinates...not the targets Top/Left (e.g. cursor)
// How do I get the X/Y of the target? (getBounds does not exist for Shapes)
var target = evt.target;
var coords = { x: evt.stageX, y: evt.stageY };
target.graphics.clear();
target.graphics.beginStroke(_PROPERTIES.node.strokeColor);
target.graphics.beginFill(target.outColor);
target.graphics.drawRect(coords.x, coords.y, _PROPERTIES.node.x, _PROPERTIES.node.y);
target.graphics.endFill();
stage.update();
};
$(document).ready(function () {
// Stage
stage = new createjs.Stage('demoCanvas');
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
// Stage - Events
stage.enableMouseOver(10);
// Queue
loader = new createjs.LoadQueue();
// DOM - EVENTS
$('#btnCreateNode').on('click', function(e){
node_create();
});
});
</script>
The solution is to put your Shapes into a Container. Shapes don't have the same properties & methods useful in determining location that other objects do: like images or bitmaps etc.
As such...Containers are the "solution" to that.
THE JAVASCRIPT:
var _PROPERTIES = { node: { y: 100, x: 200, fillColor: '#F9FAFB', fillOverColor: '#FCFCC2', strokeColor: '#000' } };
function node_create(){
console.log('node_create');
var top = Math.random() * 500;
var left = Math.random() * 500;
var width = _PROPERTIES.node.x;
var height = _PROPERTIES.node.y;
// Create
var node = new createjs.Shape();
node.graphics.beginStroke(_PROPERTIES.node.strokeColor);
node.graphics.beginFill(_PROPERTIES.node.fillColor);
node.graphics.setStrokeStyle(1);
node.snapToPixel = true;
node.graphics.drawRect(0, 0, width, height); //<-- changed
node.graphics.endFill();
node.name = name;
node.overColor = _PROPERTIES.node.fillOverColor; //<-- added
node.outColor = _PROPERTIES.node.fillColor; //<-- added
// Events
node.on("mouseover", node_mouseover);
node.on("mouseout", node_mouseout);
// Container
var container = new createjs.Container(); //<-- added
container.x = top;
container.y = left;
container.addChild(node);
// Display
stage.addChild(container); //<-- changed
stage.update();
};
function node_mouseover(evt) {
console.log('node_mouseover');
// Because of the container...you no longer need to calculate a TOP or LEFT
var target = evt.target;
target.graphics.clear();
target.graphics.beginStroke(_PROPERTIES.node.strokeColor);
target.graphics.beginFill(target.overColor);
target.graphics.drawRect(0, 0, _PROPERTIES.node.x, _PROPERTIES.node.y); //<-- changed
target.graphics.endFill();
stage.update();
};
function node_mouseout(evt) {
console.log('node_mouseout');
// Because of the container...you no longer need to calculate a TOP or LEFT
var target = evt.target;
target.graphics.clear();
target.graphics.beginStroke(_PROPERTIES.node.strokeColor);
target.graphics.beginFill(target.outColor);
target.graphics.drawRect(0, 0, _PROPERTIES.node.x, _PROPERTIES.node.y); //<-- changed
target.graphics.endFill();
stage.update();
};

Sending multiple objects forward changes their order (z-index)

The following snippet has a green square above a red square
Select both squares by dragging over them.
Click the bring forward button
After clicking bring forward the squares have switched order. It is my understanding that the items should stay in the same order, but be moved increasingly above other non-selected items as the button is further clicked.
If you deselect, and repeat the experiment you will see that they switch again.
Any ideas?
var canvas = new fabric.Canvas('c',
{
preserveObjectStacking : true
});
var rect = new fabric.Rect({
left: 10, top: 10,
fill: 'red',
width: 100, height: 100,
hasControls: true
});
canvas.add(rect);
var rect2 = new fabric.Rect({
left: 40, top: 40,
fill: 'green',
width: 100, height: 100,
hasControls: true
});
canvas.add(rect2);
$("#bringForward").click(function()
{
var items = canvas.getActiveObject() || canvas.getActiveGroup();
if(items)
items.bringForward();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.6/fabric.min.js"></script>
<button id="bringForward">Bring Forward</button>
<canvas id="c" width="640" height="480"></canvas>
This can be considered a bug or not, depending on what do you expect the function to do.
The documentation for the feature says:
Moves an object or a selection up in stack of drawn objects
And is actually doing so.
The object on top cannot go more on top, the one under can and goes.
Still for a dev this can look like a weird behaviour, to me not really. But guess is personal.
Here is your widget with a modified snippet to try a better solution.
var removeFromArray = fabric.util.removeFromArray;
// modified function to avoid snapping
fabric.StaticCanvas.prototype.bringForward = function (object, intersecting) {
if (!object) {
return this;
}
var activeGroup = this._activeGroup,
i, obj, idx, newIdx, objs, latestIndex;
if (object === activeGroup) {
objs = activeGroup._objects;
latestIndex = this._objects.length;
for (i = objs.length; i--;) {
obj = objs[i];
idx = this._objects.indexOf(obj);
if (idx !== this._objects.length - 1 && idx < latestIndex - 1) {
newIdx = idx + 1;
latestIndex = newIdx;
removeFromArray(this._objects, obj);
this._objects.splice(newIdx, 0, obj);
} else {
latestIndex = idx;
}
}
}
else {
idx = this._objects.indexOf(object);
if (idx !== this._objects.length - 1) {
// if object is not on top of stack (last item in an array)
newIdx = this._findNewUpperIndex(object, idx, intersecting);
removeFromArray(this._objects, object);
this._objects.splice(newIdx, 0, object);
}
}
this.renderAll && this.renderAll();
return this;
};
var canvas = new fabric.Canvas('c',
{
preserveObjectStacking : true
});
var rect = new fabric.Rect({
left: 10, top: 10,
fill: 'red',
width: 100, height: 100,
hasControls: true
});
canvas.add(rect);
var rect2 = new fabric.Rect({
left: 40, top: 40,
fill: 'green',
width: 100, height: 100,
hasControls: true
});
canvas.add(rect2);
var rect3 = new fabric.Rect({
left: 70, top: 70,
fill: 'blue',
width: 100, height: 100,
hasControls: true
});
canvas.add(rect3);
var rect4 = new fabric.Rect({
left: 100, top: 100,
fill: 'orange',
width: 100, height: 100,
hasControls: true
});
canvas.add(rect4);
$("#bringForward").click(function()
{
var items = canvas.getActiveObject() || canvas.getActiveGroup();
if(items)
items.bringForward();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.6/fabric.min.js"></script>
<button id="bringForward">Bring Forward</button>
<canvas id="c" width="640" height="480"></canvas>

Why does shape appears on click of canvas element after removing it?

When I remove the object and click on canvas element , the removed shape re-appears and I am not able to select it also.
var delete = function(){
var canvas = document.getElementById("canvas").fabric;
canvas.remove(canvas.getActiveObject());
}
You have to register an handler to object selection on canvas, then remove the object.
Check the runnable snippet below if could work for your needs:
$(function() {
var canvas = new fabric.Canvas('c')
var operation = '';
var circle = new fabric.Circle({
radius: 20,
fill: 'green',
left: 100,
top: 100
});
var triangle = new fabric.Triangle({
width: 20,
height: 30,
fill: 'blue',
left: 50,
top: 50
});
canvas.add(circle, triangle);
canvas.on('object:selected', doOperationHandler);
function doOperationHandler() {
if (operation == 'remove') {
remove();
}
}
function remove() {
canvas.remove(canvas.getActiveObject());
}
$('#btn_select').on('click', function() {
operation = '';
});
$('#btn_delete').on('click', function() {
operation = 'remove';
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='c'>
</canvas>
<button id='btn_select'>Select</button>
<button id='btn_delete'>Delete</button>

How to select covered objects via mouse in fabricJS?

I'm trying to develop a way to select objects that are layered below and (totally) covered by other objects. One idea is to select the top object and then via doubleclick walk downwards through the layers. This is what I got at the moment:
var canvas = new fabric.Canvas("c");
fabric.util.addListener(canvas.upperCanvasEl, "dblclick", function (e) {
var _canvas = canvas;
var _mouse = _canvas.getPointer(e);
var _active = _canvas.getActiveObject();
if (e.target) {
var _targets = _canvas.getObjects().filter(function (_obj) {
return _obj.containsPoint(_mouse);
});
//console.warn(_targets);
for (var _i=0, _max=_targets.length; _i<_max; _i+=1) {
//check if target is currently active
if (_targets[_i] == _active) {
//then select the one on the layer below
_targets[_i-1] && _canvas.setActiveObject(_targets[_i-1]);
break;
}
}
}
});
canvas
.add(new fabric.Rect({
top: 25,
left: 25,
width: 100,
height: 100,
fill: "red"
}))
.add(new fabric.Rect({
top: 50,
left: 50,
width: 100,
height: 100,
fill: "green"
}))
.add(new fabric.Rect({
top: 75,
left: 75,
width: 100,
height: 100,
fill: "blue"
}))
.renderAll();
canvas {
border: 1px solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.min.js"></script>
<canvas id="c" width="300" height="200"></canvas>
As you can see, trying to select the red rectangle from within the blue one is not working. I'm only able to select the green or the blue. I guess that after the first doubleclick worked (green is selected), clicking again just selects blue so the following doubleclick is only able to get green again.
Is there a way around this? Any other ideas?
After some time I finally was able to solve that by myself. Clicking on an object brings it to the top. On double-clicking I try to get the object one layer behind the current object. On another dblclick I get the one behind and so on. Works great for me and also allows for the selection of fully covered objects without the need to move others.
var canvas = new fabric.Canvas("c");
canvas.on("object:selected", function (e) {
if (e.target) {
e.target.bringToFront();
this.renderAll();
}
});
var _prevActive = 0;
var _layer = 0;
//
fabric.util.addListener(canvas.upperCanvasEl, "dblclick", function (e) {
var _canvas = canvas;
//current mouse position
var _mouse = _canvas.getPointer(e);
//active object (that has been selected on click)
var _active = _canvas.getActiveObject();
//possible dblclick targets (objects that share mousepointer)
var _targets = _canvas.getObjects().filter(function (_obj) {
return _obj.containsPoint(_mouse) && !_canvas.isTargetTransparent(_obj, _mouse.x, _mouse.y);
});
_canvas.deactivateAll();
//new top layer target
if (_prevActive !== _active) {
//try to go one layer below current target
_layer = Math.max(_targets.length-2, 0);
}
//top layer target is same as before
else {
//try to go one more layer down
_layer = --_layer < 0 ? Math.max(_targets.length-2, 0) : _layer;
}
//get obj on current layer
var _obj = _targets[_layer];
if (_obj) {
_prevActive = _obj;
_obj.bringToFront();
_canvas.setActiveObject(_obj).renderAll();
}
});
//create something to play with
canvas
//fully covered rect is selectable with dblclicks
.add(new fabric.Rect({
top: 75,
left: 75,
width: 50,
height: 50,
fill: "black",
stroke: "black",
globalCompositeOperation: "xor",
perPixelTargetFind: true
}))
.add(new fabric.Circle({
top: 25,
left: 25,
radius: 50,
fill: "rgba(255,0,0,.5)",
stroke: "black",
perPixelTargetFind: true
}))
.add(new fabric.Circle({
top: 50,
left: 50,
radius: 50,
fill: "rgba(0,255,0,.5)",
stroke: "black",
perPixelTargetFind: true
}))
.add(new fabric.Circle({
top: 75,
left: 75,
radius: 50,
fill: "rgba(0,0,255,.5)",
stroke: "black",
perPixelTargetFind: true
}))
.renderAll();
canvas {
border: 1px solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<canvas id="c" width="300" height="200"></canvas>
Just add one property during adding object to the canvas.
perPixelTargetFind: true
My task is a bit different - the mission is to pick the overlapping obj behind the current one:
[Left-click]+[Cmd-Key] on an selected-object to pick the first overlapping object which is right behind it.
If no overlapping objs are found, restart the search from top-layer
The idea is, for each click-event, intercept the selected object and replace it with our desired object.
A Fabric mouse-down event is like this:
User clicks on canvas
On canvas: find the target-obj by the coordinates of mouse cursor and stores it in the instance-variable (canvas._target)
Run event-handlers for mouse:down:before
Compare the target-obj found from step(2) with current selected object, fire selection:cleared/update/create events according to the results of comparison.
Set new activeObject(s)
Run event-handlers for mouse:down
We can use a customized event handler on mouse:down:before to intercept the target-obj found on Step(2) and replace it by our desired-object
fCanvas = new fabric.Canvas('my-canvas', {
backgroundColor: '#cbf1f1',
width: 800,
height: 600,
preserveObjectStacking: true
})
const r1 = new fabric.Rect({ width: 200, height: 200, stroke: null, top: 0, left: 0, fill:'red'})
const r2 = new fabric.Rect({ width: 200, height: 200, stroke: null, top: 50, left: 50, fill:'green'})
const r3 = new fabric.Rect({ width: 200, height: 200, stroke: null, top: 100, left: 100, fill:'yellow'})
fCanvas.add(r1, r2, r3)
fCanvas.requestRenderAll()
fCanvas.on('mouse:down:before', ev => {
if (!ev.e.metaKey) {
return
}
// Prevent conflicts with multi-selection
if (ev.e[fCanvas.altSelectionKey]) {
return
}
const currActiveObj = fCanvas.getActiveObject()
if (!currActiveObj) {
return
}
const pointer = fCanvas.getPointer(ev, true)
const hitObj = fCanvas._searchPossibleTargets([currActiveObj], pointer)
if (!hitObj) {
return
}
let excludeObjs = []
if (currActiveObj instanceof fabric.Group) {
currActiveObj._objects.forEach(x => { excludeObjs.push(x) })
} else {
// Target is single active object
excludeObjs.push(currActiveObj)
}
let remain = excludeObjs.length
let objsToSearch = []
let lastIdx = -1
const canvasObjs = fCanvas._objects
for (let i = canvasObjs.length-1; i >=0 ; i--) {
if (remain === 0) {
lastIdx = i
break
}
const obj = canvasObjs[i]
if (excludeObjs.includes(obj)) {
remain -= 1
} else {
objsToSearch.push(obj)
}
}
const headObjs = canvasObjs.slice(0, lastIdx+1)
objsToSearch = objsToSearch.reverse().concat(headObjs)
const found = fCanvas._searchPossibleTargets(objsToSearch, pointer)
if (found) {
fCanvas._target = found
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<html>
<h4>Left-click + Cmd-key on overlapping area to pick the obj which is behind current one</h4>
<canvas id="my-canvas"></canvas>
</html>

Drag And Drop Image to Canvas (FabricJS)

The Problem
I want to do this with an image instead of a canvas object. Meaning you have to add the thing you want to add TO AND AS A PART of the canvas before you can add it. The images are actually part of the website so it doesn't need to do some intricate stuff. This code I found here only works for when it's an object not an actual element. And by the way I'm using FabricJS just to let you know that I'm not using the default HTML5 canvas stuff.
As for any alternatives that are possibly going to work without using my current code. Please do post it down below in the comments or in the answers. I would really love to see what you guys got in mind.
Summary
Basically I want to be able to drag and drop images through the canvas while retaining the mouse cursor position. For example if I drag and image and the cursor was at x: 50 y: 75 it would drop the image to that exact spot. Just like what the code I found does. But like the problem stated the CODE uses an object for you to drag it to the canvas then it clones it. I want this functionality using just plain old elements. E.g: <img>.
THE CODE -
JsFiddle
window.canvas = new fabric.Canvas('fabriccanvas');
var edgedetection = 8; //pixels to snap
canvas.selection = false;
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.setHeight(window.innerHeight);
canvas.setWidth(window.innerWidth);
canvas.renderAll();
}
// resize on init
resizeCanvas();
//Initialize Everything
init();
function init(top, left, width, height, fill) {
var bg = new fabric.Rect({
left: 0,
top: 0,
fill: "#eee",
width: window.innerWidth,
height: 75,
lockRotation: true,
maxHeight: document.getElementById("fabriccanvas").height,
maxWidth: document.getElementById("fabriccanvas").width,
selectable: false,
});
var squareBtn = new fabric.Rect({
top: 10,
left: 18,
width: 40,
height: 40,
fill: '#af3',
lockRotation: true,
originX: 'left',
originY: 'top',
cornerSize: 15,
hasRotatingPoint: false,
perPixelTargetFind: true,
});
var circleBtn = new fabric.Circle({
radius: 20,
fill: '#f55',
top: 10,
left: 105,
});
var triangleBtn = new fabric.Triangle({
width: 40,
height: 35,
fill: 'blue',
top: 15,
left: 190,
});
var sqrText = new fabric.IText("Add Square", {
fontFamily: 'Indie Flower',
fontSize: 14,
fontWeight: 'bold',
left: 6,
top: 50,
selectable: false,
});
var cirText = new fabric.IText("Add Circle", {
fontFamily: 'Indie Flower',
fontSize: 14,
fontWeight: 'bold',
left: 95,
top: 50,
selectable: false,
});
var triText = new fabric.IText("Add Triangle", {
fontFamily: 'Indie Flower',
fontSize: 14,
fontWeight: 'bold',
left: 175,
top: 50,
selectable: false,
});
var shadow = {
color: 'rgba(0,0,0,0.6)',
blur: 3,
offsetX: 0,
offsetY: 2,
opacity: 0.6,
fillShadow: true,
strokeShadow: true
};
window.canvas.add(bg);
bg.setShadow(shadow);
window.canvas.add(squareBtn);
window.canvas.add(circleBtn);
window.canvas.add(triangleBtn);
window.canvas.add(sqrText);
window.canvas.add(cirText);
window.canvas.add(triText);
canvas.forEachObject(function (e) {
e.hasControls = e.hasBorders = false; //remove borders/controls
});
function draggable(object) {
object.on('mousedown', function() {
var temp = this.clone();
temp.set({
hasControls: false,
hasBorders: false,
});
canvas.add(temp);
draggable(temp);
});
object.on('mouseup', function() {
// Remove an event handler
this.off('mousedown');
// Comment this will let the clone object able to be removed by drag it to menu bar
// this.off('mouseup');
// Remove the object if its position is in menu bar
if(this.top<=75) {
canvas.remove(this);
}
});
}
draggable(squareBtn);
draggable(circleBtn);
draggable(triangleBtn);
this.canvas.on('object:moving', function (e) {
var obj = e.target;
obj.setCoords(); //Sets corner position coordinates based on current angle, width and height
canvas.forEachObject(function (targ) {
activeObject = canvas.getActiveObject();
if (targ === activeObject) return;
if (Math.abs(activeObject.oCoords.tr.x - targ.oCoords.tl.x) < edgedetection) {
activeObject.left = targ.left - activeObject.currentWidth;
}
if (Math.abs(activeObject.oCoords.tl.x - targ.oCoords.tr.x) < edgedetection) {
activeObject.left = targ.left + targ.currentWidth;
}
if (Math.abs(activeObject.oCoords.br.y - targ.oCoords.tr.y) < edgedetection) {
activeObject.top = targ.top - activeObject.currentHeight;
}
if (Math.abs(targ.oCoords.br.y - activeObject.oCoords.tr.y) < edgedetection) {
activeObject.top = targ.top + targ.currentHeight;
}
if (activeObject.intersectsWithObject(targ) && targ.intersectsWithObject(activeObject)) {
} else {
targ.strokeWidth = 0;
targ.stroke = false;
}
if (!activeObject.intersectsWithObject(targ)) {
activeObject.strokeWidth = 0;
activeObject.stroke = false;
activeObject === targ
}
});
});
}
More codes that I found but doesn't answer my problem:
var canvas = new fabric.Canvas('c');
canvas.on("after:render", function(){canvas.calcOffset();});
var started = false;
var x = 0;
var y = 0;
var width = 0;
var height = 0;
canvas.on('mouse:down', function(options) {
//console.log(options.e.clientX, options.e.clientY);
x = options.e.clientX;
y = options.e.clientY;
canvas.on('mouse:up', function(options) {
width = options.e.clientX - x;
height = options.e.clientY - y;
var square = new fabric.Rect({
width: width,
height: height,
left: x + width/2 - canvas._offset.left,
top: y + height/2 - canvas._offset.top,
fill: 'red',
opacity: .2
});
canvas.add(square);
canvas.off('mouse:up');
$('#list').append('<p>Test</p>');
});
});
This code adds a rectangle to the canvas. But the problem is this doesn't achieve what I want which is basically, as previously stated, that I want to be able to drag an img element and then wherever you drag that image on the canvas it will drop it precisely at that location.
CREDITS
Fabric JS: Copy/paste object on mouse down
fabric.js Offset Solution
it doesn't need to do some intricate stuff.
A logical framework under which to implement image dragging might be to monitor mouse events using event listeners.
On mouse down over an Image element within the page but not over the canvas, record which image element and the mouse position within the image. On mouse up anywhere set this record back to null.
On mouse over of the canvas with a non empty image record, create a Fabric image element from the Image element (as per documentation), calculate where it goes from the recorded image position and current mouse position, paste it under the mouse, make it draggable and simulate the effect of a mouse click to continue dragging it.
I have taken this question to be about the design and feasibility stages of program development.

Categories