I'm trying to create a function that pastes an image from the user's clipboard to the canvas as a new fabric.Image(). Any search result I find either describes cloning objects already on the canvas or pasting IText data. This SO question is related to what I'm asking about, but it's 4 years old and the function in the top answer doesn't work:
How to do Copy and paste the image from User system to Canvas using fabric.js
Here's the code I'm currently trying to use. I'm trying to set up a paste function I can call later:
var $wrapper = $('#content'),
canvas = new fabric.Canvas('canvas', {
width: 400,
height: 550
}),
pasteImage = function (e) {
var items=e.originalEvent.clipboardData.items;
e.preventDefault();
e.stopPropagation();
// Fabric.js image function
function canvasImage(url) {
var img = new fabric.Image(url);
img.scale(0.75).center().setCoords();
canvas.add(img).renderAll();
}
//Loop through files
for(var i=0;i<items.length;i++){
var file = items.items[i],
type = file.type;
if (type.indexOf("image")!=-1) {
var imageData = file.getAsFile();
var imageURL=window.webkitURL.createObjectURL(imageData);
canvasImage(imageURL);
}
}
};
$wrapper.on('paste', pasteImage);
Here's a fiddle to see it in action (or inaction, I guess). This will eventually be part of a Photoshop plugin, so thankfully I only need to worry about this working in Chrome.
I couldn't get your paste event handler to trigger, because i'm not sure if div can natively take the past event unless you make it a contenteditable div, which in your use case i doubt you want to do.
I just recently implemented this in an app of my own, but i wasn't using fabric, just native canvas and js.
You're going to have to rework your code, but try changing
$wrapper.on('paste', pasteImage);
to
$(window).on('paste', pasteImage);
Regardless, I tinkered with your current code, and this is what I got to work, albeit it might not have your settings being triggered completely, but it is pasting the image in:
(function() {
var $wrapper = $('#content'),
canvas = new fabric.Canvas('canvas', {
width: 400,
height: 550
}),
txtStyles = {
top: 100,
left: 200,
padding: 6,
fill: '#d6d6d6',
fontFamily: 'sans-serif',
fontSize: '24',
originY: 'center',
originX: 'center',
borderColor: '#d6d6d6',
cornerColor: '#d6d6d6',
cornerSize: 5,
cornerStyle: 'circle',
transparentCorners: false,
lockUniScaling: true
},
imgAttrs = {
left: 200,
top: 200,
originY: 'center',
originX: 'center',
borderColor: '#d6d6d6',
cornerColor: '#d6d6d6',
cornerSize: 5,
cornerStyle: 'circle',
transparentCorners: false,
lockUniScaling: true
},
introTxt = new fabric.Text('Paste images here', txtStyles),
pasteImage = function (e) {
var items=e.originalEvent.clipboardData.items;
e.preventDefault();
e.stopPropagation();
//Loop through files
for(var i=0;i<items.length;i++){
if (items[i].type.indexOf('image')== -1) continue;
var file = items[i],
type = items[i].type;
var imageData = file.getAsFile();
var URLobj = window.URL || window.webkitURL;
var img = new Image();
img.src = URLobj.createObjectURL(imageData);
fabric.Image.fromURL(img.src, function(img){
canvas.add(img);
});
}
},
//Canvas starter text
introCanvas = function() {
canvas.add(introTxt);
};
introCanvas();
$(window).on('paste', pasteImage);
})();
fiddlers: https://jsfiddle.net/c0kw5dbu/3/
Related
I am using fabric.js for image manipulation and it works great, but I need the controls to be always visible even when I click outside the object/image.
By default they are visible only when you click the objec/image, if you click outside of it the controls disappear.
Is it possibile to do so?
Thank you.
Unfortunately, there is no built-in method in FabricJS, to achieve this at the moment.
However, here is a workaround (function) , which will simulate this functionality ...
function showControls(...objs) {
objs.forEach(obj => {
obj.set('active', true);
canvas.renderAll();
canvas.on('mouse:down', function(e) {
obj.set('active', true);
});
})
}
after adding the image object on the canvas, call the above function along with passing the image object as a parameter, that you wish to show controls for.
ᴅᴇᴍᴏ
var canvas = new fabric.Canvas('c');
// add rectangle (for demo purposes only)
var rect = new fabric.Rect({
top: 100,
left: 290,
width: 100,
height: 100,
fill: '#07C',
originX: 'center',
originY: 'center',
transparentCorners: false
});
canvas.add(rect);
// add image (for demo purposes only)
fabric.Image.fromURL('https://i.imgur.com/Q6aZlme.jpg', function(img) {
img.set({
top: 100,
left: 110,
width: 100,
height: 100,
originX: 'center',
originY: 'center',
transparentCorners: false
})
canvas.add(img);
showControls(img); // pass an object that you wish to show controls for
});
// always show controls (multi-object support)
function showControls(...objs) {
objs.forEach(obj => {
obj.set('active', true);
canvas.renderAll();
canvas.on('mouse:down', function(e) {
obj.set('active', true);
});
})
}
canvas{border:1px solid #ccc}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.11/fabric.min.js"></script>
<canvas id="c" width="400" height="200"></canvas>
not perfect, but a start
// fabricjs object - always show controls
//fabric.Object.prototype.render = (function (render) {
fabric.Image.prototype.render = (function (render) {
return function (ctx) {
render.apply(this, arguments)
// show controls
// but most controls are not usable
// until you activate [click] the object
this._renderControls(ctx)
// activate this object
// to make all controls usable
// only one active object per canvas
// when another object is active
// the controls become not usable
if (!this.canvas._activeObject)
this.canvas._activeObject = this
}
//})(fabric.Object.prototype.render)
})(fabric.Image.prototype.render)
// fabricjs object - activate on mouseover
// this is just a quick hack
// to make controls always usable
//fabric.Object.prototype.initialize = (function (initialize) {
fabric.Image.prototype.initialize = (function (initialize) {
return function () {
initialize.apply(this, arguments)
this.on('mouseover',
function(event) {
this.canvas.setActiveObject(this)
this.canvas.renderAll() // TODO cheaper?
})
}
//})(fabric.Object.prototype.initialize)
})(fabric.Image.prototype.initialize)
edit 2016, Oct 24
I've an idea about this feature by using 'pattern' which seems to be a good idea.
I tried to prove my thoughts with this pen, its still buggy but at least we can set the image(pattern) position and keep the original image when double click.
I'am not very good at javascript, So if you are interest in this please help to make this more useable, any discussions/thoughts or code correction is welcome.
http://codepen.io/wushan/pen/LRrQEL?editors=1010
// Create Canvas
var canvas = this.__canvas = new fabric.CanvasEx('c', {
preserveObjectStacking: true
});
fabric.Object.prototype.transparentCorners = false;
// Global Settings
var url = "http://fabricjs.com/assets/pug.jpg";
//Make the Pattern by url
function createMaskedImage(url) {
//Load Image
fabric.Image.fromURL(url, function(img) {
img.scaleToWidth(300);
//Make a Pattern
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(img);
var pattern = new fabric.Pattern({
source: function() {
patternSourceCanvas.setDimensions({
width: img.getWidth(),
height: img.getHeight()
});
return patternSourceCanvas.getElement();
},
repeat: 'no-repeat'
});
console.log(pattern.offsetX)
console.log(pattern.offsetY)
console.log(img.getWidth()) // 縮小後 (*scale)
console.log(img.width) // 原尺寸
//Mask (can be any shape ex: Polygon, Circles....)
var rect = new fabric.Rect({
width: 200,
height: 200,
left: 150,
top: 100,
fill: pattern
})
//Bind Double Click Event from fabric.ext
//https://github.com/mazong1123/fabric.ext
rect.on('object:dblclick', function (options) {
//Pass pattern out
enterEditMode(rect, img);
});
canvas.add(rect);
canvas.setActiveObject(rect);
});
}
function enterEditMode(mask, image) {
image.left = mask.left;
image.top = mask.top;
// New Image
// Fake Crop Area (fixed)
var rect = new fabric.Rect({
width: mask.width,
height: mask.height,
left: mask.left,
top: mask.top,
fill: '#000000',
opacity: 0.8,
selectable: false
})
canvas.remove(mask);
canvas.add(image);
image.on('object:dblclick', function (options) {
//Flatten
flatten(rect, image);
});
canvas.add(rect);
// console.log(JSON.stringify(canvas));
}
function flatten(mask, image) {
//Make a Pattern
var patternSourceCanvas = new fabric.StaticCanvas();
patternSourceCanvas.add(image);
var pattern = new fabric.Pattern({
source: function() {
patternSourceCanvas.setDimensions({
width: image.getWidth(),
height: image.getHeight()
});
return patternSourceCanvas.getElement();
},
repeat: 'no-repeat'
});
//Offsets
pattern.offsetX = image.left - mask.left - image.left;
pattern.offsetY = image.top - mask.top - image.top;
var rect = new fabric.Rect({
width: mask.width,
height: mask.height,
left: mask.left,
top: mask.top,
fill: pattern
})
//Bind Double Click Event from fabric.ext
//https://github.com/mazong1123/fabric.ext
rect.on('object:dblclick', function (options) {
//Pass pattern out
enterEditMode(rect, image);
});
canvas.remove(mask);
canvas.remove(image);
canvas.add(rect);
canvas.setActiveObject(rect);
canvas.renderAll();
}
//Button Events
//Create
document.getElementById('createMaskedImage').addEventListener('click', function () {
createMaskedImage(url);
});
Test this :
click 'create'
double click on the image object
move/scale the image
double click the image to flatten the obejct.
Know issue:
when cutting the image without scale, the image position looks correct but there is a wired transparent space.
It generates a lot duplicate objects on the scene...
Original Post
I've been working with fabric.js for a few month, haven't seen any example or discussions about a mask/crop system which is behaves like canva.com ( which is way easy to understand for users. )
the object looks like this:
when double clicked on a group/mask, it shows the original image with an unselectable mask, you can move/scale the image whatever you need and click 'OK' to made the change without modifying the original image.
I'd like to know if there is any possible solutions about making this in fabricjs, or maybe some thoughts about this issue is welcome.
Thank you a lot !
I'm attempting to add a loaded image into a fabric Group object. Everything looks ok, but the selection controls aren't selectable and I can't drag the object around. The top left control works though and after clicking it everything is fine.
Here is a jsfiddle that demonstrates the behavior.
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
canvas.setActiveObject(group);
canvas.renderAll();
});
Is this a bug or am I doing something wrong?
For some performance reason, fabricjs does not call setCoords automatically after adding objects to a group ( in case of many object added you can call setCoords just once ).
So after doing addWithUpdate, just call group.setCoords();
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
group.setCoords();
canvas.setActiveObject(group);
canvas.renderAll();
});
I came across this post because I was having trouble getting the positioning of images to work properly. I ended up finding that it's easier to just create create an image element using document.createElement and set the src, then feed that into fabric.Image with all the options you need (instead of using fabric.Image.fromURL which was too much of a headache to use), before adding it to the group.
var oImg = document.createElement("img");
oImg.setAttribute('src', 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/Cog_font_awesome.svg/512px-Cog_font_awesome.svg.png');
var i = new fabric.Image(oImg, {
originX: 'center',
originY: 'center',
left: left+35,
top: top-30,
scaleX:.05,
scaleY:.05,
});
g = new fabric.Group([r, t, i]); // where r and t are other fabric objects
I am trying to set change the backround image for a canvas using fabric.js but running into some instability. The call to add the background works with no trouble but if I then click anywhere on the canvas it goes blank and seems to move to selection mode - although this just seems to be due to an untrapped exception.
I cannot figure out what is causing the behavior. It works fine on this page http://fabricjs.com/customization/ but the following jsfiddle is failing http://jsfiddle.net/YH9yD/20/
var canvas = window._canvas = new fabric.Canvas('c');
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.setBackgroundImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
backgroundImageOpacity: 0.5,
backgroundImageStretch: false
});
this.__canvases.push(canvas);
try loading the image before you add to the canvas. if you add an image that hasnt been loaded you will get an exception. This works for us:
var img = new Image();
img.onload = function(){
canvas.setBackgroundImage(img.src, canvas.renderAll.bind(canvas), {
originX: 'left',
originY: 'top',
left: 0,
top: 0
});
};
img.src = "your image source"
regards,
Benick
Try this
canvas.setBackgroundImage('add image source', canvas.renderAll.bind(canvas), {
scaleY: canvas.height ,
scaleX: canvas.width
});
canvas.renderAll();
Hello I'm new to titanium studio I'm reading the docs 2 days now and trying to make a simple slide animation or even any animation of any kind except opening a modal window. but I can't make it work.Here is what I was trying now but fails:
var slide_it_left = Titanium.UI.createAnimation();
slide_it_left.left = 500;
slide_it_left.duration = 500;
var mainWinOpts = {
backgroundColor:'#fff',
fullscreen:true,
navBarHidden: true
}
var animWinOpts = {
navBarHidden: true,
backgroundColor:'#000',
top:0,
left:0,
width: Ti.Platform.displayCaps.platformWidth,
height: Ti.Platform.displayCaps.platformHeight,
fullscreen:false,
animated:true
}
var mainWin = Ti.UI.createWindow(mainWinOpts);
var animWin = Ti.UI.createWindow(animWinOpts);
var labelOpts = {
text: 'click me!',
textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
font: {
fontFamily: 'monospace',
fontSize: 24
},
borderWidth: 1,
color: '#2e2e2e',
borderColor: '#2e2e2e',
backgroundColor: '#dedede',
top: 50,
left: 50,
width: Ti.Platform.displayCaps.platformWidth,
height: Ti.Platform.displayCaps.platformHeight,
opacity: 1.00,
width: Ti.UI.SIZE,
height: Ti.UI.SIZE
};
var label = Ti.UI.createLabel(labelOpts);
label.addEventListener('click',function(){
animWin.open(slide_it_left);
})
mainWin.add(label);
mainWin.open();
This among other snippets I tried from their docs - forums isn't working.
Could someone please provide me some working samples or references for android window or view animation. Or point out what I'm doing wrong. Thank you in advance.
Please try changing your code to the following:
label.addEventListener('click',function(){
animWin.open();
animWin.animate(slide_it_left);
});
You cannot use the animation object as a parameter for open().
Have a look at the valid params here.
Moreover, the docs give an example for sliding in a window on Android, which is very likely what you are trying to achieve:
var win2 = Ti.UI.createWindow({fullscreen:false});
win2.open({
activityEnterAnimation: Ti.Android.R.anim.slide_in_left,
activityExitAnimation: Ti.Android.R.anim.slide_out_right
});
You can find the animations for the Android platform here.