I try to crop image with this way
var crop = new Kinetic.Image({
image: img ,
x: 100, y: 100, width: 100, heigth: 100,
crop : {
x: 0, y: 0, width: 100, heigth: 100
}
});
and it doesn't work see in http://jsfiddle.net/cm5jj/
and then I try to fill rect with image
var rect = new Kinetic.Rect({
x: 100,
y: 100,
width: 100,
height: 100,
fill:{
image: img,
crop:{
x: 0,
y: 0,
width: 100,
height: 100
},
}
});
and still doesn't work http://jsfiddle.net/cm5jj/1/
please help.
thank so much.
I believe the problem is that you're cropping the image to the exact dimensions of the image size, which means that you aren't cropping at all. try setting width and height to values greater than the crop width and height values.
Also, if you're wanting to animate the sprite, you should be using Kinetic.Sprite() instead
In your first code snippet, you mistyped height with heigth. Maybe spell check will help with this.
As to your second code snippet, to fill a rect with an image, you need fillPatternImage property, which requires an image object, and fill is used to fill with color.
fill example:
var rect = new Kinetic.Rect({
x: 100,
y: 100,
width: 100,
height: 100,
fill: 'blue'
});
fillPatternImage example:
var img = new Image();
img.onload = function() {
var rect = new Kinetic.Rect({
x: 100,
y: 100,
width: 100,
height: 100,
fillPatternImage: img
});
// Add rect to your layer, and do drawing
};
img.src = 'http://cdn.sstatic.net/img/share-sprite-new.svg';
Related
I'm experiencing an issue when scaling an image inside a group the offset of the image changes. The group consists of a background (Rect) and an image. When scaling the group the offset if the image becomes different. Expected is that the offset remains the same as the background.
I'm using Fabric 4.0.0-beta.6
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
https://jsbin.com/migogekoxu/1/edit?html,css,js,output
Setting the strokeWidth property of the background element to 0 fixed the offset for me.
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
strokeWidth: 0, //<---
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<canvas id="canvas"></canvas>
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
Obviously that's a bug with fabricJS. If you don't want to fix it on your own in it's source code you can workaround it however.
You can manually correct the position by adding an offset of 0.5 to the image's top and left properties.
For example:
const canvas = new fabric.Canvas('canvas');
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red'
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width);
img.left += 0.5;
img.top += 0.5;
const post = new fabric.Group([bg, img], {
left: 100,
top: 100
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
<canvas id="canvas" width="600" height="400"></canvas>
I am new in fabric.js and want to achieve one functionality
I added image and one rectangle same size of image, now I want to scale image while scaling rectangle.
I don't want to group objects , image should scale by scaling rectangle with previous left, top position of image
below is my code
var imgURL = 'http://i.imgur.com/8rmMZI3.jpg';
var canvas = new fabric.Canvas('canvas');
var pug;
var pugImg = new Image();
pugImg.onload = function (img) {
pug = new fabric.Image(pugImg, {
angle: 0,
width: 500,
height: 500,
left: 50,
top: 70,
scaleX: .25,
scaleY: .25,
selectable: false
});
var rect = new fabric.Rect({
top: 65,
left: 200,
width: 150,
height: 150,
fill: 'red',
});
canvas.sendToBack(rect);
canvas.bringToFront(pug);
canvas.add(rect);
canvas.add(pug);
canvas.renderAll();
};
pugImg.src = imgURL;
canvas.on('object:scaling', function(e) {
var bounds = e.target.getBoundingRect();
pug.set({
width: bounds.width,
height: bounds.height,
top: bounds.top,
left: bounds.left
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.2.0/fabric.js"></script>
<canvas id="canvas" width=400 height=400></canvas>
I added image and one rectangle same size of image
In your example the rectangle is not the same size as the image
rect = {
width: 150,
height: 150,
scaleX: 1,
scaleY: 1,
}
pug = {
width: 500,
height: 500,
scaleX: .25,
scaleY: .25
});
To achieve the desired effect, you need to give theme the same (width, height, scaleX, scaleY), then just copy the rect (scaleX, scaleY) to the pug. otherwise
if they're not the same size you'll need to compute the scale ratio between the two.
var imgURL = 'http://i.imgur.com/8rmMZI3.jpg';
var canvas = new fabric.Canvas('canvas');
var pug, rect;
var pugImg = new Image();
pugImg.onload = function (img) {
pug = new fabric.Image(pugImg, {
angle: 0,
width: 500,
height: 500,
left: 50,
top: 70,
scaleX: .25,
scaleY: .25,
selectable: false
});
rect = new fabric.Rect({
top: 65,
left: 200,
width: 500,
height: 500,
scaleX: .25,
scaleY: .25,
fill: 'red',
});
canvas.sendToBack(rect);
canvas.bringToFront(pug);
canvas.add(rect);
canvas.add(pug);
canvas.renderAll();
};
pugImg.src = imgURL;
canvas.on('object:scaling', function(e) {
pug.set({
scaleX: rect.scaleX,
scaleY: rect.scaleY,
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.2.0/fabric.js"></script>
<canvas id="canvas" width=400 height=400></canvas>
So I want to save the canvas based on scale 1 and I want to include all existing/visible objects.
So right now, my canvas is 600x400 and if I were to save it, it would only save what's inside that 600x400, and it respects zoom level (a higher zoom will see less things, a lower zoom will see more things but smaller).
I've gotten around this by running this code:
let zoom = this.canvas.getZoom();
this.canvas.setZoom(1);
let url = this.canvas.toDataURL({width: 1000, height: 700, multiplier: 1});
this.canvas.setZoom(zoom);
window.open(
url,
'_blank' // <- This is what makes it open in a new window.
);
What this does is save the image as 1000x700, so stuff that were past 600px but under 1000 still gets saved.
However, this is hard coded. So I was wondering if there was an existing function or a clean/simple way to detect where all the objects are in and returns the full size (width and height).
fiddle: http://jsfiddle.net/1pxceaLj/3/
Update 1:
var gr = new this.fabric.Group([], {
left: 0,
top: 0
});
this.canvas.forEachObject( (o) => {
gr.add(o);
});
this.canvas.add(gr);
this.canvas.renderAll();
console.log(gr.height, gr.width); // 0 0
Solution
Using group was the best idea. Best example: http://jsfiddle.net/softvar/mRA8Z/
function selectAllCanvasObjects(){
var objs = canvas.getObjects().map(function(o) {
return o.set('active', true);
});
var group = new fabric.Group(objs, {
originX: 'center',
originY: 'center'
});
canvas._activeObject = null;
canvas.setActiveGroup(group.setCoords()).renderAll();
console.log(canvas.getActiveGroup().height);
}
One possibility would be to create a kind of Container using a fabricjs group and adding all created objects to this container.
I updated your fiddle here: http://jsfiddle.net/1pxceaLj/4/
Thus, you could just use group.width and group.height, perhaps adding a little offset or minimum values, and there you are having dynamical value to pass into toDataUrl even when having a smaller canvas.
code:
var canvas = new fabric.Canvas('c');
var shadow = {
color: 'rgba(0,0,0,0.6)',
blur: 20,
offsetX: 10,
offsetY: 10,
opacity: 0.6,
fillShadow: true,
strokeShadow: true
}
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
var rect2 = new fabric.Rect({
left: 800,
top: 100,
fill: "#FF0000",
stroke: "#000",
width: 100,
height: 100,
strokeWidth: 10,
opacity: .8
});
rect.setShadow(shadow);
//canvas.add(rect);
//canvas.add(rect2);
var gr = new fabric.Group([ rect, rect2 ], {
left: 0,
top: 0
});
canvas.add(gr);
function save()
{
alert(gr.width);
alert(gr.height);
let zoom = canvas.getZoom();
var minheight = 700;
canvas.setZoom(1);
let url = this.canvas.toDataURL({width: gr.width, height: gr.height > minheight ? gr.height : minheight, multiplier: 1});
canvas.setZoom(zoom);
window.open(
url,
'_blank'
);
}
I've looked through the collection of examples, that go with the library, but unfortunatelly, all examples, devoted to composite figures, are based on parsing json file.
reader.unmarshal(canvas, jsonDocument);
However, what I want to see is how to create composite figures from basic ones, something like:
var figure_1 = new draw2d.shape.basic.Rectangle(....);
var figure_1 = new draw2d.shape.basic.Diamond(....);
... then do something, so that figure_1 and figure_2 are now part of
... a composite figure
PS. I think what I need is StrongComposite, but I do not know how to use it. Hope someone can help!
EDIT
This is what I tried:
var baseRectangle = new draw2d.shape.composite.StrongComposite({width: 200, height: 200, x: conf.x, y: conf.y});
var top = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y});
var bottom = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y + 60});
baseRectangle.assignFigure(top);
baseRectangle.assignFigure(bottom);
canvas.add(baseRectangle);
But it does not work. I only see a gray box. I also tried this:
var baseRectangle = new draw2d.shape.composite.StrongComposite({width: 200, height: 200, x: conf.x, y: conf.y});
var top = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y});
var bottom = new draw2d.shape.basic.Oval({width: 120, height: 40, x: conf.x, y: conf.y + 60});
baseRectangle.assignFigure(top);
baseRectangle.assignFigure(bottom);
canvas.add(top);
canvas.add(bottom);
But as a result, I got absolutely independent ovals.
You also have to add baseRectangle to canvas.
var baseRectangle = new draw2d.shape.composite.StrongComposite({
width: 200,
height: 200,
x: conf.x,
y: conf.y
});
canvas.add(baseRectangle);
var top = new draw2d.shape.basic.Oval({
width: 120,
height: 40,
x: conf.x,
y: conf.y
});
var bottom = new draw2d.shape.basic.Oval({
width: 120,
height: 40,
x: conf.x,
y: conf.y + 60
});
baseRectangle.assignFigure(top);
baseRectangle.assignFigure(bottom);
canvas.add(top);
canvas.add(bottom);
In addition to needing to put the child elements on the canvas (sorry, can't comment on existing answer yet): I found I need to wait a frame (nextTick with Vue) before assignFigure can work. Otherwise draw2d throws exception:
TypeError: Cannot read properties of null (reading 'paper')
at Class.createShapeElement (null:23614:34)
at Class.getShapeElement (null:8492:33)
at Class.toFront (null:8351:22)
at Class.toFront
my function draw an image, and another image on another layer with Kinetic.js but i want to crop the second image which is named smsTopBg_image
window.onload = function() {
//INITIALISATION
var stage = new Kinetic.Stage({
container: "iPhone",
width: 480,
height: 720
});
//LAYERS
var background_layer = new Kinetic.Layer();
var sms_layer = new Kinetic.Layer();
var text_layer = new Kinetic.Layer();
//ELEMENTS
var iPhoneBg = new Image();
iPhoneBg.onload = function() {
var iPhoneBg_image = new Kinetic.Image({
image: iPhoneBg
});
background_layer.add(iPhoneBg_image);
stage.add(background_layer);
}
iPhoneBg.src = "iPhoneBg.jpg";
//--------------------------------------------------
var smsTopBg = new Image();
smsTopBg.onload = function() {
var smsTopBg_image = new Kinetic.Image({
image: smsTopBg,
x: 10,
y: 10,
});
sms_layer.add(smsTopBg_image);
stage.add(sms_layer);
}
smsTopBg.src = "iPhoneBg.jpg";
};
Thanks !
You can add an optional crop object to the main attributes object in your Image constructor.
It has an x, y, width and height properties.
var smsTopBg_image = new Kinetic.Image({
image: smsTopBg,
x: 10,
y: 10,
// random values, choose your own :
crop: {
x: 20,
y: 3,
width: 100,
height: 42
}
});
Ok ifound the complete solution with your help, it's necessary to add height and with to the image before crop like that :
var smsTopBg = new Image();
smsTopBg.onload = function() {
var smsTopBg_image = new Kinetic.Image({
image: smsTopBg,
x: 200,
y: 20,
width: 50,
height: 20,
crop: {
x: 20,
y: 10,
width: 50,
height: 50
}
});
sms_layer.add(smsTopBg_image);
stage.add(sms_layer);
}
Thanks !
Refer this url for Image Crop in Kinetic.js : http://jsfiddle.net/umhm7/