I have checked all the answers to clone existing canvas to another one. But I could not get it done.
Please check my current progress.
http://jsfiddle.net/37n8rtdf/5/
First canvas will be clipped to that square you see initially and content of that canvas will be added in another canvas. But I don't know it always throw TYPE_MISMATCH_ERR: DOM Exception 171 in chrome. I am using fabricjs to clipping content.
Bit of help is appreciated.
Thanks
Here is code of my script:
HTML
<textarea id="line_1"></textarea>
<input type="button" id="render" value="Apply" />
<input type="button" id="preview" value="preview" />
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
<canvas id="c_new" width="500" height="500" style="border:1px solid red; margin: 30px;"></canvas>
`
Javascript
var canvas = this.__canvas = new fabric.Canvas('c');
var canvas_new = this.__canvas = new fabric.Canvas('c_new');
var product_image = 'http://www.jail.se/hardware/digital_camera/canon/ixus_800is-powershot_sd700/images/sample_photos/sample3.jpg';
//var ctx = canvas.getContext("2d");
var polygon;
$(document).ready(function(){
fabric.Object.prototype.transparentCorners = false;
//canvas.setDimensions({width:w,height:h});
var center = canvas.getCenter();
canvas.setBackgroundImage(product_image,
canvas.renderAll.bind(canvas), {
scaleX:1,
scaleY:1,
top: center.top,
left: center.left,
originX: 'center',
originY: 'center',
backgroundImageOpacity: 0,
backgroundImageStretch: false
});
canvas_new.setBackgroundImage(product_image,
canvas_new.renderAll.bind(canvas_new), {
scaleX:1,
scaleY:1,
top: center.top,
left: center.left,
originX: 'center',
originY: 'center'
});
polygon = new fabric.Polygon([
{x: 0, y: 0},
{x: 220, y: 0},
{x: 220, y: 180},
{x: 0, y: 180} ], {
left: 140,
top: 150,
angle: 0,
fill: 'transparent',
stroke: '#000', strokeWidth: 1,
lockMovementX: true,
lockMovementY: true,
lockScalingX: true,
lockScalingY: true,
lockRotation: true,
hasControls: false,
hasBorders: false,
hoverCursor: 'default',
overflow: 'hidden'
});
canvas.add(polygon);
$('#render').click(function(){ return render(); });
$('#preview').click(function(){ return rasterize(); });
});
function render()
{
var text_val = $('#line_1').val();
var comicSansText = new fabric.Text(text_val, {
fontWeight: 'normal'
});
canvas.add(comicSansText.set({ left: 200, top: 150, angle: 0 }));
}
function rasterize()
{
var shape = canvas.item(0);
polygon.strokeWidth=0;
canvas.renderAll();
//canvas.remove(shape);
canvas.clipTo = function(ctx) {
shape.render(ctx);
};
var ctx2 = canvas_new.getContext('2d');
ctx2.drawImage(canvas, 0, 0);
}
`
#WinterMute is right, you are trying to draw the Fabric object instead of the canvas element.
You fabric object seems to have two canvases embedded (lowerCanvasEl and upperCanvasEl).
So you can modify just a little bit your code to make it look like :
function rasterize() {
var shape = canvas.item(0);
polygon.strokeWidth = 0;
canvas.renderAll();
canvas.clipTo = function(ctx) {
shape.render(ctx);
};
var ctx2 = canvas_new.getContext('2d');
ctx2.drawImage(canvas.lowerCanvasEl, 0, 0);
ctx2.drawImage(canvas.upperCanvasEl, 0, 0);
}
Updated fiddle
Related
My aim is to get three buttons in fabric.js:
"Copy",
"Paste",
"Delete".
Button with name "Copy" should copy selected objects.
Button with name "Paste" should paste copied objects.
Button with name "Delete" should delete selected objects from canvas.
"Copy" and "paste" buttons helps to work with selected objectives.
Buttons with copy and paste works well at that time remove button shows error:
How should I solve my problem?
function Copy()
{
canvas.getActiveObject().clone(function(cloned)
{
_clipboard = cloned;
});
}
function Delete()
{
canvas.getActiveObject().remove();
}
function Paste() {
_clipboard.clone(function(clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
clonedObj.canvas = canvas;
clonedObj.forEachObject(function(obj) {
canvas.add(obj);
});
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
_clipboard.top += 10;
_clipboard.left += 10;
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
}
var canvas = this.__canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 100,
top: 50,
fill: '#D81B60',
width: 100,
height: 100,
strokeWidth: 2,
stroke: "#880E4F",
rx: 10,
ry: 10,
angle: 45,
hasControls: true
});
canvas.add(rect);
<div>
<button type="button" onclick="Copy()">copy</button>
<button type="button" onclick="Paste()">paste</button>
<button type="button" onclick="Delete()">delete</button>
</div>
<div style="display:flex;flex-direction:row;">
<div>
<canvas id="c" width="1300" height="1300"></canvas>
</div>
</div>
<script src="https://unpkg.com/fabric#4.6.0/dist/fabric.js"></script>
The error is clear you are getting:
remove is not a function
You should be using canvas.remove(object) here is a working sample:
function Delete() {
var active = canvas.getActiveObject()
if (active) {
canvas.remove(active)
if (active.type == "activeSelection") {
active.getObjects().forEach(x => canvas.remove(x))
canvas.discardActiveObject().renderAll()
}
}
}
var canvas = this.__canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 100,
top: 50,
fill: '#D81B60',
width: 100,
height: 100,
strokeWidth: 2,
stroke: "#880E4F",
rx: 10,
ry: 10,
angle: 45,
hasControls: true
});
canvas.add(rect);
var rect2 = new fabric.Rect({
left: 160,
top: 10,
fill: '#D81B60',
width: 60,
height: 60,
strokeWidth: 2,
stroke: "#880E4F",
hasControls: true
});
canvas.add(rect2);
<div>
<button type="button" onclick="Delete()">delete</button>
</div>
<div style="display:flex;flex-direction:row;">
<div>
<canvas id="c" width="1300" height="1300"></canvas>
</div>
</div>
<script src="https://unpkg.com/fabric#4.6.0/dist/fabric.js"></script>
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>
I have an issue on mobile orientation change. When the orientation changes the canvas size adjust himself accordingly. And objects in the canvas exist in the same position after orientation where they were before the orientation relatively the canvas left and top.
It will be more clear in the image.
I want to change the positions of the objects on window:resize relatively to the center of canvas instead of left and top. Please help!
You can center the object on canvas with canvas.centerObject(object);.
var canvas = new fabric.Canvas('c');
var dia1 = new fabric.Circle({
radius: 12,
left: 0,
top: 0,
originX: 'center',
originY: 'center',
fill: 'transparent',
strokeWidth: 5,
stroke: "red",
width: 50,
height: 50,
});
var dia2 = new fabric.Circle({
radius: 5,
left: 0,
top: 0,
originX: 'center',
originY: 'center',
fill: 'red',
width: 50,
height: 50,
});
var targetEl = new fabric.Group([dia1, dia2], {
originX: 'center',
originY: 'center',
});
//center object on canvas
canvas.centerObject(targetEl);
canvas.add(targetEl);
canvas.renderAll();
//center on window resize
$(window).resize(function() {
canvas.centerObject(targetEl);
canvas.renderAll();
});
canvas {
border: 1px solid #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.js"></script>
<canvas id="c" width="600" height="300"></canvas>
you may need to set the object dimensions as follwing
const canvas = new fabric.Canvas('canvas');
// you can simply use
canvas.viewportCenterObject(obj);
// or if you want to center manually
const centerObject = obj => {
const center = canvas.getCenter();
const zoom = canvas.getZoom();
canvas.centerObject(obj);
obj.set({
left: center.left / zoom - obj.width / 2,
top: center.top / zoom - obj.height / 2,
});
obj.setCoords();
canvas.calcOffset();
canvas.renderAll()
};
See Fiddle: I am trying to render a background image on a FabricJS polygon, but for some reason the scale of the pattern is different on different devices / browsers for no obvious reason.
Here is the code:
HTML:
<canvas id="tcanvas" width="200" height="200" />
JavaScript:
function testPattern(id) {
var url = 'https://dummyimage.com/200x200/aff/000',
tCanvas = new fabric.Canvas(id);
new fabric.Image.fromURL(url, function(img) {
var tPoints = [{
x: 0,
y: 0
},
{
x: 0,
y: 200
},
{
x: 200,
y: 200
},
{
x: 200,
y: 0
}
];
var tPatternWidth = 200;
var tPatternHeight = 200;
var tImgXScale = tPatternWidth / img.width;
var tImgYScale = tPatternHeight / img.height;
img.set({
left: 0,
top: 0,
scaleX: tImgXScale,
scaleY: tImgYScale
});
var tPatternSourceCanvas = new fabric.StaticCanvas();
tPatternSourceCanvas.add(img);
tPatternSourceCanvas.renderAll();
var tPattern = new fabric.Pattern({
offsetX: 0,
offsetY: 0,
source: function() {
tPatternSourceCanvas.setDimensions({
width: tPatternWidth,
height: tPatternHeight
});
tPatternSourceCanvas.renderAll();
return tPatternSourceCanvas.getElement();
},
repeat: 'no-repeat'
});
var tImgPoly = new fabric.Polygon(tPoints, {
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fill: tPattern,
objectCaching: false,
selectable: false,
stroke: 'red',
strokeWidth: 1
});
tCanvas.add(tImgPoly);
tCanvas.renderAll();
});
}
testPattern('tcanvas');
This is what I see on my desktop Chrome:
... and this what I see on a (Samsung) tablet Chrome:
How can I fix the code so that the both patterns look the same, i.e., like the first one?
It s a retina support side effect.
When initializing the canvas for the Pattern, please pass as option:
enableRetinaScaling: false
var tPatternSourceCanvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false});
This will avoid fabric trying to enhance the canvas twice.
I'm creating a shape with certain width and height (this shape is a clipping rect) inside canvas. Then inside this shape I'm loading an object which can be moved and rotated.
I wrote a custom function for get center point inside this shape, and it works good if object are not rotated.
If I rotate this object over 90deg and click "center" button - the object is moved up outside the clipping rect.
I cannot use a native FabricJs "centerV" function, because I would like that object will be centered inside clipping rect - not the canvas container, so that is why I made a variable "objectVerticalCenter".
My code is presented here:
var canvas = new fabric.Canvas('canvas', {
'selection': false
});
var clippingRect = new fabric.Rect({
left: 170,
top: 90,
width: 185,
height: 400,
fill: 'transparent',
stroke: 1,
opacity: 1,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
selectable: false,
preserveObjectStacking: true,
objectCaching: false
});
var retinaScalling = canvas.getRetinaScaling();
var pol = new fabric.Polygon([{
x: 200,
y: 0
}, {
x: 250,
y: 50
}, {
x: 250,
y: 100
}, {
x: 150,
y: 100
}, {
x: 150,
y: 50
}], {
left: 250,
top: 150,
angle: 0,
fill: 'green'
});
pol.scaleX = 0.5;
pol.scaleY = 0.5;
pol.set('id', 'shape');
pol.scaleToWidth(clippingRect.getWidth());
pol.setCoords();
pol.clipTo = function(ctx) {
ctx.save();
ctx.setTransform(retinaScalling, 0, 0, retinaScalling, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
canvas.centerObjectH(pol);
canvas.add(pol);
canvas.renderAll();
canvas.setActiveObject(pol);
canvas.add(pol);
canvas.renderAll();
document.getElementById('rotate').onclick = function() {
var activeObject = canvas.getActiveObject();
activeObject.setAngle(200);
activeObject.setCoords();
canvas.renderAll();
};
document.getElementById('center').onclick = function() {
var activeObject = canvas.getActiveObject();
var rectHeight = activeObject.getBoundingRectHeight();
var objectVerticalCenter = (clippingRect.getHeight() / 2) - (rectHeight / 2) + clippingRect.top;
activeObject.set('top', objectVerticalCenter);
activeObject.setCoords();
canvas.renderAll();
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<button id="rotate">Rotate</button>
<button id="center">Center</button>
<canvas id="canvas" width="530" height="600"></canvas>
JSFiddle
To reproduce my problem you have to, click "rotate", then "center" button and you will see that shape is moved up outside the clipping rect.
If object is not rotated centering works fine.
Is there any way to fix my centering function or way to use "centerV" function inside the clipping rect?
Regards
If you want to center an object inside another, the safest thing to do is:
get the center point of the container
set the center point of the object in that point
in code this could equal to:
var clipCenter = clippingRect.getCenterPoint();
activeObject.setPositionByOrigin(clipCenter,'center','center');
var canvas = new fabric.Canvas('canvas', {
'selection': false
});
var clippingRect = new fabric.Rect({
left: 170,
top: 90,
width: 185,
height: 400,
fill: 'transparent',
stroke: 1,
opacity: 1,
hasBorders: false,
hasControls: false,
hasRotatingPoint: false,
selectable: false,
preserveObjectStacking: true,
objectCaching: false
});
var retinaScalling = canvas.getRetinaScaling();
var pol = new fabric.Polygon([{
x: 200,
y: 0
}, {
x: 250,
y: 50
}, {
x: 250,
y: 100
}, {
x: 150,
y: 100
}, {
x: 150,
y: 50
}], {
left: 250,
top: 150,
angle: 0,
fill: 'green'
});
pol.scaleX = 0.5;
pol.scaleY = 0.5;
pol.set('id', 'shape');
pol.scaleToWidth(clippingRect.getWidth());
pol.setCoords();
pol.clipTo = function(ctx) {
ctx.save();
ctx.setTransform(retinaScalling, 0, 0, retinaScalling, 0, 0);
clippingRect.render(ctx);
ctx.restore();
};
canvas.centerObjectH(pol);
canvas.setActiveObject(pol);
canvas.add(pol);
canvas.renderAll();
document.getElementById('rotate').onclick = function() {
var activeObject = canvas.getActiveObject();
activeObject.setAngle(200);
activeObject.setCoords();
canvas.renderAll();
};
document.getElementById('center').onclick = function() {
var activeObject = canvas.getActiveObject();
var clipCenter = clippingRect.getCenterPoint();
activeObject.setPositionByOrigin(clipCenter,'center','center');
canvas.renderAll();
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.21/fabric.min.js"></script>
<button id="rotate">Rotate</button>
<button id="center">Center</button>
<canvas id="canvas" width="530" height="600"></canvas>