Drag and Drop Image issue on New Fabric JS - javascript

In the latest version of Fabric JS, image drop feature is not working as expected.
function handleDrop(e) {
e.preventDefault();
// this / e.target is current target element.
if (e.stopPropagation) {
e.stopPropagation(); // stops the browser from redirecting.
}
var obj = document.querySelector('#draggable_items_container .drag_item.draggable_items');
if($(obj).attr('data-type')=='image')
{
var image_url = $(obj).attr('data-value');
var new_image = new fabric.Image(obj, {
width: obj.width,
height: obj.height,
left: e.layerX,
top: e.layerY,
});
canvas.add(new_image);
}
else
{
var text_value = $(obj).attr('data-value');
if(text_value=='')
{
alert('You cant drag blank text');
return false;
}
var params = {
canvas_obj : canvas,
text: text_value,
left : e.layerX,
top : e.layerY,
font_size : 22,
editable: false,
};
add_text_canvas(params);
}
return false;
}
Working Fiddle:
http://jsfiddle.net/Ahammadalipk/w8kkc/185/
Not working in 2.0.0-beta.7
http://jsfiddle.net/dhavalsisodiya/w8kkc/412/
So how to fix this?

var newImage = new fabric.Image(img, {
width: img.naturalWidth,
height: img.naturalHeight,
scaleX: setImageWidth/img.naturalWidth,
scaleY: setImageHeight/img.naturalHeight,
// Set the center of the new object based on the event coordinates relative
// to the canvas container.
left: e.layerX,
top: e.layerY
});
You need to set actual width and height of image, then scale X and Y,according to your requirement. Here is updated fiddle

Related

Basic Fabric.js Image Selection with Mask

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>

Prevent save state hover in fabricjs

I have an application, with fabricjs. It works with the zoom of the mousewheel, when I do mousewheel up, my canvas save the state, like as if I do mousewheel down.
I have a state in the canvas when I put the mouse over on an object.
When you are hovering an object and do zoom-in and zoom-out, the state of the canvas is saved with the hover state. I don't want this to happen. How I can prevent this?
//click on the rect to see that the color red is saved in the json
//shown in the console log, but i want the original State, the red fill
canvas.on("mouse:over", function(event){
if("target" in event){
color = event.target.fill;
event.target.fill="red";
canvas.renderAll();
}
});
canvas.on("mouse:out", function(event){
if("target" in event){
event.target.fill=color;
canvas.renderAll();
}
});
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'blue',
width: 20,
height: 20,
angle: 45
});
canvas.add(rect);
You can check the fiddle at:
https://jsfiddle.net/samael205/xtcmokuy/9/
There is no way to just ignore the hover state. You can save the color in some other field and pass that field in to the toObject() call and the reset it after the export.
var canvas = new fabric.Canvas('canvas', {
perPixelTargetFind: true
});
canvas.on("mouse:down", function(event) {
var state = canvas.toObject(['trueColor']);
state.objects.forEach(o => {
if (o.trueColor) {
o.fill = o.trueColor;
}
})
console.log(state);
});
//click on the rect to see that the color red is saved in the json
//shown in the console log, but i want the original State, the red fill
canvas.on("mouse:over", function(event) {
if (event.target) {
event.target.trueColor = event.target.fill;
event.target.fill = "red";
canvas.renderAll();
}
});
canvas.on("mouse:out", function(event) {
if (event.target) {
event.target.fill = event.target.trueColor;
canvas.renderAll();
}
});
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'blue',
width: 20,
height: 20,
angle: 45
});
canvas.add(rect);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.6/fabric.min.js"></script>
<canvas id="canvas"></canvas>

Clip an Image and Show in Another Canvas

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();
})
}

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.

bring node name on edge mouseover for arborjs

I am thinking of making a mouseover detect on edges for arbor.js data chart. I can detect the mouse on the edges by employing canvas getImageData technique
function detectLine(x, y,content) {
var imageData = ctx.getImageData(0, 0, w, h),
inputData = imageData.data,
pData = (~~x + (~~y * w)) * 4;
if (inputData[pData]===16 && edg._id) {
var z=sys.getEdges(ion,lbl);
console.log(z)
$('<div id="tooltip">' + content + '</div>').css( {
position: 'absolute',
display: 'none',
top: y + 5,
left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80
}).appendTo("body").fadeIn(200);
return true;
}
else{
$("#tooltip").remove();
return false;
}
}
and then calling this:
canvas.addEventListener("mousemove", function(e){
var x = parseInt(e.offsetX);
var y = parseInt(e.offsetY);
detectLine(x,y,nd._id);
// console.log(edg.target.data.label);
});
This surely detects the edges on mouseover, but i can't get to get the id or name of each edge on the tooltip. This works by detecting the color pixel of the edges which I have kept same for all the edges. Any idea on how to call the particular edge name or id when mouseover is detected.

Categories