I am using Fabric.js and jQuery UI for a drag and drop scene creator. When I drag and drop an image into the canvas the image becomes blurry/pixelated and appears to be zoomed in. I've been searching Stackoverflow and Google forever and can't find any solutions. I'd appreciate any suggestions on how to fix it!
Here is my code:
// HTML
<canvas id="scene"></canvas>
// Draggable setup
$('.scene-item').draggable({
helper: 'clone',
appendTo: 'body',
containment: 'window',
scroll: false,
zIndex: 9999
});
// Drop Setup
var canvas = new fabric.Canvas('scene');
canvas.setDimensions({
width: '800px',
height: '500px'
}, {
cssOnly: true
});
document.getElementById("scene").fabric = canvas;
canvas.on({
'object:moving': function(e) {
e.target.opacity = 0.5;
},
"object:modified": function(e) {
e.target.opacity = 1;
}
});
$('#scene').droppable({
drop: function(e, ui) {
if ($(e.target).attr('id') === 'scene') {
var pointer = canvas.getPointer();
fabric.Image.fromURL(ui.draggable[0].currentSrc, function (img) {
console.log(img);
img.set({
left: pointer.x - 20,
top: pointer.y - 20,
width: 52,
height: 52,
scale: 0.1
});
//canvas.clear();
canvas.add(img);
canvas.renderAll();
});
}
}
});
This is what the images look like on and off the canvas:
Here is a demo jsfiddle.net/dmLr6cgx/1
I figured out the problem. I had to replace:
canvas.setDimensions({
width: '800px',
height: '500px'
}, {
cssOnly: true
});
with
canvas.setHeight(500);
canvas.setWidth(800);
canvas.renderAll();
And now it works fine.
Related
I have a Konva stage defined as the following code describes, and the stage is not responding to click, even though it is responding to drag and is automatically dragged.
Is there a way to debug the click problem??
The stage has a layer over it with some objects which don't fully cover it.
this.stage = new Konva.Stage({
container: divName,
width: this.width,
height: this.height,
draggable: true
});
this.stage.on("click mousedown", function() {
console.log("click");
});
this.stage.on("dragstart", function() {
console.log("dragstart");
});
this.nodeLayer = new Konva.Layer();
this.stage.add(this.nodeLayer);
The snippet below is built from your code. Click seems to work in this simple example. Could you post more of your code ?
var divName = 'container1';
var width = 300;
var height = 200;
this.stage = new Konva.Stage({
container: divName,
width: width,
height: height,
draggable: true
});
this.stage.on("click mousedown", function() {
console.log("click - stage");
});
this.stage.on("dragstart", function() {
console.log("dragstart - stage");
});
this.nodeLayer = new Konva.Layer();
this.nodeLayer.on("click mousedown", function() {
console.log("click - layer");
});
this.nodeLayer.on("dragstart", function() {
console.log("dragstart - layer");
});
this.stage.add(this.nodeLayer);
var layer = nodeLayer;
$('#addshape').on('click', function(){
var rect = new Konva.Rect({ x: 10, y: 10, width: 50, height: 50, fill: 'cyan'});
layer.add(rect)
layer.draw();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<p>Click the canvas first - note the 'click - stage' event fires but no 'click - layer'. Now add a shape and click it - note the layer click occurs before the stage click.</p>
<button style='position: absolute; z-index: 10;' id='addshape'>Add shape</button>
<div id='container1' style="width: 300px, height: 200px; background-color: silver;"></div>
<div id='img'></div>
I am trying to use FabricJS to animate any object when it is selected.
So when selected an object 'shrinks' from center on mouse:down (10px off the width and 10px off the height)
And on mouse:up it returns to the original size and state.
I don't know if I need to use FabricJS built in animation for this. I wanted it to grow and shrink linear though.
Right now my Fiddle does pretty much nothing, I can't get far enough without errors.
Does anyone have any examples of this or similar? I couldn't find anything about changing the appearance of FabricJS objects on mouse:up/:down when selected.
(function() {
var canvas = this.__canvas = new fabric.Canvas('canvas');
var touchedObject;
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 50,
height: 50,
fill: 'blue',
hasBorders: false,
});
canvas.add(rect);
var rect2 = new fabric.Rect({
left: 190,
top: 50,
width: 50,
height: 50,
fill: 'red',
hasBorders: false,
});
canvas.add(rect2);
canvas.on('object:selected', function(evn) {
/*animate({
'width': ,
'height':
}, {
duration: 200,
});*/
});
canvas.renderAll();
})();
You could accomplish that using the fabric.util.animate() method.
(function() {
var canvas = this.__canvas = new fabric.Canvas('canvas');
var touchedObject;
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 50,
height: 50,
fill: '#07C',
hasBorders: false,
});
canvas.add(rect);
// disable controls and set hover-cursor
canvas.forEachObject(function(o) {
o.hasBorders = o.hasControls = false;
});
canvas.hoverCursor = 'pointer';
// mouse events
canvas.on('mouse:down', function(e) {
animate(e, 1);
});
canvas.on('mouse:up', function(e) {
animate(e, 0);
});
function animate(e, p) {
if (e.target) {
fabric.util.animate({
startValue: e.target.get('height'),
endValue: e.target.get('height') + (p ? -10 : 50 - e.target.height),
duration: 200,
onChange: function(v) {
e.target.setHeight(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
fabric.util.animate({
startValue: e.target.get('width'),
endValue: e.target.get('width') + (p ? -10 : 50 - e.target.width),
duration: 200,
onChange: function(v) {
e.target.setWidth(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
fabric.util.animate({
startValue: e.target.get('top'),
endValue: e.target.get('top') + (p && 5),
duration: 200,
onChange: function(v) {
e.target.setTop(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
fabric.util.animate({
startValue: e.target.get('left'),
endValue: e.target.get('left') + (p && 5),
duration: 200,
onChange: function(v) {
e.target.setLeft(v);
canvas.renderAll();
},
onComplete: function() {
e.target.setCoords();
}
});
}
}
canvas.renderAll();
})();
#canvas {
border: 1px solid lightgrey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
<canvas id="canvas" width="150"></canvas>
I'm trying to bind an event to a background image:
var imgInstance = fabric.Image.fromURL(path, function(oImg) {
oImg.on("selected", function() {
alert();
});
canvas.setBackgroundImage(oImg, canvas.renderAll.bind(canvas), {
top: 0,
left: 0
});
oImg.on("selected", function() {
alert();
});
});
I can get it work. I'm trying to bind mouse events to handle drag, so i can move the image when it's bigger than the canvas, like background-cover css property.
Thoughts?
Thanks guys!
SOLUTION:
var DragStart = false;
canvas.on('mouse:down', function(options) {
if (!options.target) {
DragStart = true;
}else {
DragStart = false;
}
});
canvas.on('mouse:move', function(options) {
if (DragStart) {
var bounds = getBounds(options.e); // validate boundaries between image and canvas
canvas.backgroundImage.top = bounds.y;
canvas.backgroundImage.left = bounds.x;
canvas.renderAll();
}
});
It doesn't seem like you can add events to the background image... but the canvas does emit events and if you click the canvas and no target is set then they must be clicking the background. Background image are not forced to take up the whole background but I'm not sure reasons to not make it take up the whole background.
You do get the mouse event passed in though so if the background image does not take up the whole background you could do that math and figure out where they clicked.
window.canvas = new fabric.Canvas('c', { selection: false, preserveObjectStacking:true });
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(o) {
canvas.setBackgroundImage(o, canvas.renderAll.bind(canvas), {
top: 0,
left: 0
});
});
canvas.on('mouse:down', function(options) {
if (!options.target) {
console.log('target is null')
}
});
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<canvas id="c" width="600" height="600"></canvas>
Have such code :
var $canvas = $("#canvas");
var canvas = new fabric.Canvas('canvas', {
hoverCursor: 'pointer',
selection: false
});
var rect1 = new fabric.Rect({
width: 200,
height: 100,
left: 0,
top: 50,
angle: 30,
fill: 'rgba(255,0,0,0.5)'
});
canvas.add(rect1);
$canvas.droppable({
drop: dragDrop,
over: enterIn,
});
function enterIn(e, ui) {
event.preventDefault();
rect1.width = 400;
}
function dragDrop(e, ui) {
var element = ui.draggable;
var imgInstance = new fabric.Image(element.data("image"), {
left: x,
top: y,
});
canvas.add(imgInstance);
}
Problem:
rect1 width change is applied only after drop on canvas.
But I tested that enterIn function is working fine.
Could you advise something?
This is the code I have so far for my draggable-droppable-resizable functionality:
var cont1 = $(this.el).find('.image');
var cont2 = $(this.el).find('#container2');
console.log(cont1);
$(cont1).draggable({
helper: 'clone',
cursor: 'move',
revert: 'true'
});
$(cont2).droppable({
accept: 'img',
drop: function(event, ui) {
var $canvas = $(this);
var $container = $('<div>');
if (!ui.draggable.hasClass('canvas-element')) {
var $canvasElement = ui.draggable.clone();
$canvasElement.addClass('canvas-element');
$container.draggable({
cursor: "move",
// delay: 1000,
scroll: false
// containment: "parent"
}).resizable({
//containment: "parent",
handles: 'n, e, s, ne, nw, se, sw',
aspectRatio: true
});
$container.append($canvasElement).prependTo(cont2);
$canvasElement.css({
left: $container.left,
top: $container.top,
position: 'relative',
height: '100%',
width: '100%'
});
//$canvasElement.css('left', ui.position.left-80+'px').css('top', ui.position.top-80+'px').appendTo(this);
}
}
});
Here's the jsFiddle.
What I would like to achieve is for the dropped images to remain in the place where they were dropped.
There are also some other issues - the images that have already been dropped before change their positions after a new one gets dropped and I would like to get rid of this issue.
What can I change to implement that functionality?
You will need to set the .image elements to position: absolute when inside of the container. You then need to calculate their left and top based on the position of the drop event and the offset of the container.
CSS:
#container2 .image{
position: absolute;
}
JS:
var containerOffset = $container.offset();
$canvasElement.css({
left: ui.position.left - containerOffset.left,
top: ui.position.top - containerOffset.top
});
http://jsfiddle.net/E9y8Q/7/