I have a unique (but hopefully simple) issue to fix with Fabric.js to fix.
I have this very simple example below:
I have
2 Images
Both have an absolutePositioned mask via clipPath property on the fabric.Image instance
My issue is:
I want the image to only be selectable (and hoverable) whenever the selection happens within the bounds of the mask, not anywhere on image (even outside of the bounds of its mask).
This image shows the mouse hovering over the red door picture (even though the mouse is outside of the mask bounds, but not outside the image bounds:
Here's a code snippet of the door image snippet:
fabric.Image.fromURL(url1, function(img){
canvas.add(img.set({
left: 0,
top: 0,
clipPath: rect1,
hasControls: false,
}));
img.on('mouseover', () => {
const filter = new fabric.Image.filters.BlendColor({
color: 'white',
alpha: 0.7,
mode: 'tint'
})
img.filters.push(filter)
img.applyFilters()
canvas.renderAll()
})
img.on('mouseout', () => {
img.filters.pop()
img.applyFilters()
canvas.renderAll()
})
}, {crossOrigin: "Anonymous"});
JS Fiddle Example showing the current behavior that I'm trying to change.
A possible solution is to listen to the mouse event in the canvas and toggle the activeObject based on the mouse position:
canvas.observe('mouse:move', function(options) {
const pos = canvas.getPointer(options.e);
if (!imageRight || !imageLeft) return
if (pos.x > 200) {
activeImage = imageRight
} else {
activeImage = imageLeft
}
const activeObj = canvas.getActiveObject();
if (activeImage !== activeObj) {
canvas.setActiveObject(activeImage);
canvas.renderAll()
}
});
This is a very simplified way to detect if the mouse is over the image. It checks if the x > 200 since that is where the line between both images is positioned. This could be improved to be more accurate, but it works just to illustrate the idea.
var canvas = window._canvas = new fabric.Canvas('c');
const url1 = 'https://picsum.photos/300/200'
const url2 = 'https://picsum.photos/320'
let imageRight
let imageLeft
let activeImage
canvas.observe('mouse:move', function(options) {
const pos = canvas.getPointer(options.e);
if (!imageRight || !imageLeft) return
if (pos.x > 200) {
activeImage = imageRight
} else {
activeImage = imageLeft
}
const activeObj = canvas.getActiveObject();
if (activeImage !== activeObj) {
canvas.setActiveObject(activeImage);
canvas.renderAll()
}
});
const baseProps = {
width: 200,
height: 200,
top: 0,
fill: '#000',
absolutePositioned: true,
hasControls: false,
evented: false,
selectable: false
}
const rect1 = new fabric.Rect({
...baseProps,
left: 0,
})
const rect2 = new fabric.Rect({
...baseProps,
left: 200,
})
canvas.add(rect1)
canvas.add(rect2)
fabric.Image.fromURL(url2, function(img) {
imageRight = img
canvas.add(img.set({
left: 200,
top: 0,
clipPath: rect2,
hasControls: false
}))
}, {
crossOrigin: "Anonymouse"
})
fabric.Image.fromURL(url1, function(img) {
imageLeft = img
canvas.add(img.set({
left: 0,
top: 0,
clipPath: rect1,
hasControls: false,
}));
}, {
crossOrigin: "Anonymous"
});
canvas {
border: 2px red solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.6.0/fabric.min.js"></script>
<canvas id="c" width="420" height="220"></canvas>
Related
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>
I have two canvas on one page. I need to clip an image in first one and transfer it to second one as clipped.
I have a code like below but when i click to send i just see a black clipped image in second canvas. Plus, selectable area is big as before clipping.
public clip() {
var canvas = new fabric.Canvas('cLeft');
let src = this.srcLeft;
loadImage(src, canvas);
// If there is enough width -> clipTo
if (Math.abs(this.firstX - this.lastX) > 20 || Math.abs(this.firstY - this.lastY) > 20) {
canvas.clipTo = (ctx) => {
console.log("-->> clipTo: " + this.firstX + " " + this.firstY);
var shp = new fabric.Rect({
top: this.firstY,
left: this.firstX,
width: Math.abs(this.lastX - this.firstX),
height: Math.abs(this.lastY - this.firstY),
hasControls: false,
selectable: false,
evented: false
});
shp.render(ctx);
};
this.data = canvas.toDataURL();
}
}
public send() {
var canvas = new fabric.Canvas('cRight');
fabric.Image.fromURL(this.data, function (img) {
img.left = 10;
img.top = 10;
canvas.add(img);
canvas.renderAll();
})
}
I've got a question about fabric.js and the polygon-object.
I have an example of my problem in this fiddle:
Click me
First 4 fabric.Circle subobjects called linePoint are drawn.
The linePoint objects have an extra x(same as left) and y(same as top) coordinate and a reference to which polygon they belong to:
fabric.LinePoint = fabric.util.createClass(fabric.Circle,
{
initialize: function (options) {
options || (options = {});
this.callSuper('initialize', options);
options &&
this.set('type', 'line_point'),
this.set('x', this.left),
this.set('y', this.top),
this.set('polygon', options.polygon)
},
setPointCoordinates: function(new_left, new_top) {
this.set('x', new_left);
this.set('y', new_top);
this.set('left', new_left);
this.set('top', new_top);
}
With the now given x and y coordinates there is a Polygon drawn between the Points.
The problem is now, when you move the Circles, the Polygon is moved correctly, but its border (or I don't know how to exactly call it) will stay the same small rectangle as it was.
I want to update the polygon Coords too, I tried .setCoords(), but nothing happened.
Maybe you can help me. :) Thanks!
In reply also to:
https://groups.google.com/forum/#!topic/fabricjs/XN1u8E0EBiM
This is your modified fiddle:
https://jsfiddle.net/wum5zvwk/2/
fabric.LinePoint = fabric.util.createClass(fabric.Circle,
{
initialize: function (options) {
options || (options = {});
this.callSuper('initialize', options);
this.set('type', 'line_point'),
this.set('x', this.left),
this.set('y', this.top),
this.set('polygon', options.polygon)
},
setPointCoordinates: function(new_left, new_top) {
this.set('x', new_left);
this.set('y', new_top);
this.set('left', new_left);
this.set('top', new_top);
}
});
var canvas = new fabric.Canvas('canvas');
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
document.getElementById("canvas").tabIndex = 1000;
drawPolygonToCanvas();
canvas.on('object:moving', function(event) {
var object = event.target;
switch(object.type) {
case 'line_point':
//move polygon
object.setPointCoordinates(object.left, object.top);
object.polygon.setCoords();
object.polygon.initialize(object.polygon.points);
object.polygon.top += object.polygon.height / 2;
object.polygon.left += object.polygon.width / 2;
canvas.renderAll();
break;
}
});
function drawPolygonToCanvas()
{
//creting end_points and set them
old_position = canvas.getPointer(event.e);
var end_point_1 = createLinePoint(100, 100);
var end_point_2 = createLinePoint(100, 150);
var end_point_3 = createLinePoint(150, 150);
var end_point_4 = createLinePoint(150, 100);
end_points_in_use = [end_point_1, end_point_2, end_point_3, end_point_4];
canvas.add(end_point_1, end_point_2, end_point_3, end_point_4);
drawPoly(end_points_in_use);
canvas.deactivateAll();
canvas.renderAll();
}
function drawPoly(point_array)
{
var poly = new fabric.Polygon(point_array, {
left: (100 + ((150 - 100) /2)),
top: (100 + ((150 - 100) /2)),
fill: 'lightblue',
lockScalingX: true,
lockScalingY: true,
lockMovementX: true,
lockMovementY: true,
perPixelTargetFind: true,
opacity: 0.5,
type: 'polygon'
});
for (var i = 0; i < point_array.length; i++) {
point_array[i].polygon = poly;
}
canvas.add(poly);
poly.sendToBack();
}
function createLinePoint(left, top) {
return new fabric.LinePoint({
left: left,
top: top,
strokeWidth: 2,
radius: 15,
fill: '#fff',
stroke: '#666',
related_poly_point: 0,
lockScalingX: true,
lockScalingY: true
});
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<div id="canvas-wrapper" style="position:relative;width:704px;float:left;">
<canvas id="canvas" width="700" height="600" style="border:1px solid #000000;"></canvas>
</div>
Modifying the polygon points is not enough to have the bounding box adjusted. The easies thing i can think of is to re initialize the polygon with the new points coordinates.
I am using FabricJS to draw circle in canvas:
var circle = new fabric.Circle({radius: 100,
fill: '',
stroke: 'red',
strokeWidth: 3,
originX: 'center',
originY: 'center'
});
var text = new fabric.Text('HELLO WORLD.',
{ fontSize: 30,
originX: 'center',
originY: 'center',
fill : 'red'
});
var group = new fabric.Group([ circle, text ], {
left: 150, top: 100
});
canvas.add(group);
This code draws a normal circle but I need to freely draw circle with mouse.
Any code help will be appreciated.
According to your previous code for drawing rectangle http://jsfiddle.net/Subhasish2015/8u1cqasa/2/ Here is the code for drawing circle:
$(document).ready(function(){
//Getting the canvas
var canvas1 = new fabric.Canvas("canvas2");
var freeDrawing = true;
var divPos = {};
var offset = $("#canvas2").offset();
$(document).mousemove(function(e){
divPos = {
left: e.pageX - offset.left,
top: e.pageY - offset.top
};
});
$('#2').click(function(){
//Declaring the variables
var isMouseDown=false;
var refCircle;
//Setting the mouse events
canvas1.on('mouse:down',function(event){
isMouseDown=true;
if(freeDrawing) {
var circle=new fabric.Circle({
left:divPos.left,
top:divPos.top,
radius:0,
stroke:'red',
strokeWidth:3,
fill:''
});
canvas1.add(circle);
refCircle=circle; //**Reference of rectangle object
}
});
canvas1.on('mouse:move', function(event){
if(!isMouseDown)
{
return;
}
//Getting yhe mouse Co-ordinates
if(freeDrawing) {
var posX=divPos.left;
var posY=divPos.top;
refCircle.set('radius',Math.abs((posX-refCircle.get('left'))));
canvas1.renderAll();
}
});
canvas1.on('mouse:up',function(){
canvas1.add(refCircle);
//alert("mouse up!");
isMouseDown=false;
//freeDrawing=false; // **Disables line drawing
});
});
});
var Circle = (function() {
function Circle(canvas) {
this.canvas = canvas;
this.className = 'Circle';
this.isDrawing = false;
this.bindEvents();
}
Circle.prototype.bindEvents = function() {
var inst = this;
inst.canvas.on('mouse:down', function(o) {
inst.onMouseDown(o);
});
inst.canvas.on('mouse:move', function(o) {
inst.onMouseMove(o);
});
inst.canvas.on('mouse:up', function(o) {
inst.onMouseUp(o);
});
inst.canvas.on('object:moving', function(o) {
inst.disable();
})
}
Circle.prototype.onMouseUp = function(o) {
var inst = this;
inst.disable();
};
Circle.prototype.onMouseMove = function(o) {
var inst = this;
if (!inst.isEnable()) {
return;
}
var pointer = inst.canvas.getPointer(o.e);
var activeObj = inst.canvas.getActiveObject();
activeObj.stroke = 'red',
activeObj.strokeWidth = 5;
activeObj.fill = 'red';
if (origX > pointer.x) {
activeObj.set({
left: Math.abs(pointer.x)
});
}
if (origY > pointer.y) {
activeObj.set({
top: Math.abs(pointer.y)
});
}
activeObj.set({
rx: Math.abs(origX - pointer.x) / 2
});
activeObj.set({
ry: Math.abs(origY - pointer.y) / 2
});
activeObj.setCoords();
inst.canvas.renderAll();
};
Circle.prototype.onMouseDown = function(o) {
var inst = this;
inst.enable();
var pointer = inst.canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
var ellipse = new fabric.Ellipse({
top: origY,
left: origX,
rx: 0,
ry: 0,
transparentCorners: false,
hasBorders: false,
hasControls: false
});
inst.canvas.add(ellipse).setActiveObject(ellipse);
};
Circle.prototype.isEnable = function() {
return this.isDrawing;
}
Circle.prototype.enable = function() {
this.isDrawing = true;
}
Circle.prototype.disable = function() {
this.isDrawing = false;
}
return Circle;
}());
var canvas = new fabric.Canvas('canvas');
var circle = new Circle(canvas);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
Please draw circle here
<div id="canvasContainer">
<canvas id="canvas" width="400" height="400" style="border: solid 1px"></canvas>
</div>
Here is the detail blog with jsfiddle - https://blog.thirdrocktechkno.com/sketching-circle-of-a-html5-canvas-using-the-fabricjs-f7dbfa20cf2d
Here is an example of drawing a rectangle which I carefully made and works for latest versions. Krunan example seems to work OK- just in case this is a rectangle free drawing implementation that doesn't use DOM apis like e.clientX, offsetLeft, etc to track the coords, but fabric.js APIs only which I think is safer. Also unregister event listeners - I'm still trying to refine it since I need free drawing support for my project - Since there is no official support for this I wanted to reference the example here for others.
https://cancerberosgx.github.io/demos/misc/fabricRectangleFreeDrawing.html
An easy way to add a Circle to a canvas:
canvas.add(new fabric.Circle({ radius: 30, fill: "green", top: 100, left: 100 }));
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.