I am using Fabric.JS to create a layout builder. However once the object is displayed on canvas I cannot update the rx and ry property of the object like rectangle. I have a used a slider field where the user uses a slider for updating value of rx and ry on the object.
Further when I view the object it has been updated but the canvas is not updated with the new value of rx and ry.
Following is the code snippet I use :
$('#slider').slider()
.on('slideStop', function (ev) {
var val = $('#slider').slider('getValue').val();
var activeObject = canvas.getActiveObject();
if (activeObject) {
console.log('Inside active object');
activeObject.set({'rx': val, 'ry': val});
canvas.renderAll();
}
});
DEMO
var canvas = new fabric.Canvas('canvas');
$("#slider").slider({
orientation: "horizontal",
range: "min",
max: 50,
slide: refreshSwatch,
change: refreshSwatch
});
canvas.add(new fabric.Rect({
left: 25,
top: 50,
width: 150,
height: 150
}));
canvas.add(new fabric.Rect({
left: 225,
top: 50,
width: 150,
height: 150,
fill: 'rgba(100,100,200,0.5)'
}));
canvas.renderAll();
canvas.on('object:selected',function(){
$("#slider").slider('value',canvas.getActiveObject().rx);
})
function refreshSwatch() {
var val = $("#slider").slider("value");
var activeObject = canvas.getActiveObject();
if (activeObject) {
activeObject.set({
'rx': val,
'ry': val
});
canvas.renderAll();
}
}
canvas {
border: 2px dotted blue;
}
<link href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.16/fabric.min.js"></script>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div id="slider"></div><br>
<canvas id="canvas" width="400" height="400"></canvas>
Probably I am not getting what is your actual issue, but I added a demo and from jQuery slider value, its updating rect rx,ry value.
Related
I added a fabric Rect at (80, 80) and then called getImageData at that location. However, the data I get from that location doesn't match the Rect I placed there. Instead, I find the data around (60, 60).
var canvas = new fabric.Canvas('fabric-canvas');
canvas.setHeight(800);
canvas.setWidth(800);
var rect = new fabric.Rect({
left: 80,
top: 80,
fill: 'red',
width: 20,
height: 20
});
canvas.add(rect);
// This doesn't work
let data = canvas.getContext('2d').getImageData(80, 80, 20, 20);
canvas.getContext('2d').putImageData(data, 0, 0);
// This "works"
data = canvas.getContext('2d').getImageData(60, 60, 20, 20);
canvas.getContext('2d').putImageData(data, 100, 0);
Here's a fiddle demo: https://jsfiddle.net/2wxdb8ua/13/
How might this be possible?
The problem is certainly caused by some zooming, either due to your monitor being an high-res monitor (a.k.a retina), or browser/OS zoom being applied on the page.
You can either make the calculation yourself world_coord * window.devicePixelRatio:
var canvas = new fabric.Canvas('fabric-canvas');
canvas.setHeight(800);
canvas.setWidth(800);
var rect = new fabric.Rect({
left: 80,
top: 80,
fill: 'red',
width: 20,
height: 20
});
canvas.add(rect);
let ctx = canvas.getContext('2d').getImageData(...[80, 80, 20, 20].map(fixCoords));
canvas.getContext('2d').putImageData(ctx, ...[0, 0].map(fixCoords));
function fixCoords( coord ) {
return coord * window.devicePixelRatio;
}
canvas#fabric-canvas {
background: white;
border: 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.1/fabric.min.js"></script>
<canvas id='fabric-canvas'></canvas>
Or disable fabric's auto-scaling by using the enableRetinaScaling parameter of the initOptionObject:
var canvas = new fabric.Canvas('fabric-canvas', {
enableRetinaScaling: false
});
canvas.setHeight(800);
canvas.setWidth(800);
var rect = new fabric.Rect({
left: 80,
top: 80,
fill: 'red',
width: 20,
height: 20
});
canvas.add(rect);
let ctx = canvas.getContext('2d').getImageData(80, 80, 20, 20);
canvas.getContext('2d').putImageData(ctx, 0, 0);
canvas#fabric-canvas {
background: white;
border: 2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.1/fabric.min.js"></script>
<canvas id='fabric-canvas'></canvas>
But note that with the latter solution, your canvas will certainly become blurry on high-res monitors.
I created an arrow using Fabric.js group functionality by grouping Rectangle and Triangle.
Unfortunately I am not able to delete it as a group.
I figured out that getActiveObject() for a single object works fine. Instead, while debugging getActiveGroup() gives error.
var canvas = new fabric.Canvas('canvas');
canvas.setHeight(window.innerHeight * 0.75);
canvas.setWidth(window.innerWidth * 0.75);
//-------------------------Group - Rectangle and Triangle----------------------------------------------
window.addArrow = function() {
var rect = new fabric.Rect({
left: 0,
top: 0,
stroke: 'red',
fill: 'red',
width: 1,
height: 50,
});
rect.hasRotatingPoint = true;
canvas.add(rect);
var triangle = new fabric.Triangle({
width: 10,
height: 10,
fill: 'red',
left: -4,
top: -10
});
var group = new fabric.Group([rect, triangle], {
left: 150,
top: 100,
angle: 90
});
canvas.add(group);
}
//-------------------------Group Delete----------------------------------------------
window.deleteObject = function() {
return canvas.getActiveObject() == null ? canvas.getActiveGroup() : canvas.getActiveObject();
}
function getActiveGroup() {
canvas.getActiveGroup().forEachObject(function(o) {
canvas.remove(o)
});
}
function getActiveObject() {
canvas.remove(canvas.getActiveObject());
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.min.js"></script>
<canvas id="canvas" width="800" height="600" style="border:1px solid red;"></canvas>
<button onClick="addArrow()">Arrow</button>
<button onClick="deleteObject()">Delete</button>
Because you didn't set any active object you cannot use getActiveObject.
In this case you may use getObjects:
canvas.getObjects('group') // in order to get the group
canvas.getObjects('rect') // in order to get the rectangle
var canvas = new fabric.Canvas('canvas');
canvas.setHeight(window.innerHeight * 0.75);
canvas.setWidth(window.innerWidth * 0.75);
//-------------------------Group - Rectangle and Triangle----------------------------------------------
window.addArrow = function() {
var rect = new fabric.Rect({
left: 0,
top: 0,
stroke: 'red',
fill: 'red',
width: 1,
height: 50,
});
rect.hasRotatingPoint = true;
canvas.add(rect);
var triangle = new fabric.Triangle({
width: 10,
height: 10,
fill: 'red',
left: -4,
top: -10
});
var group = new fabric.Group([rect, triangle], {
left: 150,
top: 100,
angle: 90
});
canvas.add(group);
}
//-------------------------Group Delete----------------------------------------------
window.deleteObject = function() {
// remove group....
canvas.getObjects('group').forEach(function(ele,idx) {
canvas.remove(ele);
});
canvas.getObjects('rect').forEach(function(ele,idx) {
canvas.remove(ele);
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.min.js"></script>
<canvas id="canvas" width="800" height="600" style="border:1px solid red;"></canvas>
<button onClick="addArrow()">Arrow</button>
<button onClick="deleteObject()">Delete</button>
use sub classing in fabric.js for creating arrow object so that single object is only needed. So handling will be easier.
[http://fabricjs.com/fabric-intro-part-3#subclassing][1]
I am having trouble flipping or mirroring the object horizontally when the object itself is clicked on the FabricJS canvas.
I came close but it was mirroring the object when it was being resized too, which I didn't want.
I would guess I need to add the 'flipX: true' attribute to object on the first click and on the next click remove that attribute and so on with each click. Or maybe that is over complicating it and it can be done much easier with a flipX function I do not know.
I did find a Fiddle that flipped the object, but it was onclick of a button not the object itself.
I am struggling to solve this :\
My Fiddle
HTML:
<canvas id="canvas" width="400" height="300"></canvas>
JS:
var canvas = this.__canvas = new fabric.Canvas('canvas');
canvas.on('object:selected', function() {
toggle('flipX');
});
// create a rectangle
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 100,
height: 50,
angle: 20,
fill: 'red'
});
canvas.add(rect);
canvas.renderAll();
You could accomplish that in the following way ...
var canvas = this.__canvas = new fabric.Canvas('canvas');
// mouse event
canvas.on('mouse:down', function(e) {
if (e.target) {
if (!e.target.__corner) {
e.target.toggle('flipX');
canvas.renderAll();
}
e.target.__corner = null;
}
});
// create a rectangle
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 100,
height: 50,
angle: 20,
});
// set gradient (for demonstration)
rect.setGradient('fill', {
type: 'linear',
x1: -rect.width / 2,
y1: 0,
x2: rect.width / 2,
y2: 0,
colorStops: {
0: '#ffe47b',
1: 'rgb(111,154,211)'
}
});
canvas.add(rect);
rect.set('flipX', true);
canvas.renderAll();
body{margin:10px 0 0 0;overflow:hidden}canvas{border:1px solid #ccc}
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
<canvas id="canvas" width="208" height="208"></canvas>
Same from above, different case.
Apply flip to background image
fabric.Image.fromURL('../' +ImageUrl, function (img02) {
Backcanvas.setBackgroundImage(img02, Backcanvas.renderAll.bind(Backcanvas), {
backgroundImageStretch: false,
top: 0,
left: 0,
originX: 'left',
originY: 'top',
flipY:'true'
});
Backcanvas.renderAll();
Backcanvas.backgroundImage.setCoords();
canvas.renderAll();
Backcanvas.renderAll();
}, { crossOrigin: 'anonymous' });
I can't find a way to use the Resize filter (slideHack) without getting a pixelated image sometimes.
I use fabric.js 1.7.2
I just added the image in the canvas
fabric.Image.fromURL(url, function(oImg)
{
var scaling = 0.2;
var rFilter = new fabric.Image.filters.Resize({
resizeType: 'sliceHack'
});
oImg.resizeFilters.push(rFilter);
oImg.applyFilters();
oImg.set({
left: 300,
top: 300,
scaleX: scaling,
scaleY: scaling
});
canvas.add(oImg);
canvas.renderAll();
});
When I click on the image or resize it manually, the edges get smooth.
When I apply a Tint Filter, it is pixelated again
I can't find the function triggered to smooth the edges...
Thanks for your help.
The document Introduction to Fabric.js, Part 2 suggests the following syntax, which seems to solve the issue:
fabric.Image.fromURL('pug.jpg', function(img) {
// add filter
img.filters.push(filter);
// apply filters and re-render canvas when done
img.applyFilters(canvas.renderAll.bind(canvas));
// add image onto canvas
canvas.add(img);
});
The result is compared to your original method in the code snippet below. In order to make it work, I also added { crossOrigin: 'Anonymous' } (see the comments to this answer by AndreaBogazzi).
var canvas = new fabric.Canvas('c');
var ctx = canvas.getContext("2d");
var url = 'http://i.imgur.com/a47Yxsb.png';
var imgWidth = 770;
function performScaling() {
// Get scaling factor
var scaling = parseFloat(document.getElementById("txtScaling").value);
canvas.clear();
// With original method
fabric.Image.fromURL(url, function(oImg)
{
var rFilter = new fabric.Image.filters.Resize({
resizeType: 'sliceHack'
});
oImg.resizeFilters.push(rFilter);
oImg.applyFilters();
oImg.set({
left: 0,
top: 16,
scaleX: scaling,
scaleY: scaling
});
canvas.add(oImg);
canvas.renderAll();
});
// As suggested in Fabric.js introduction
fabric.Image.fromURL(url, function (oImg) {
oImg.filters.push(new fabric.Image.filters.Resize({
resizeType: 'sliceHack', scaleX: scaling , scaleY: scaling
}));
oImg.set({
left: imgWidth * 1.1 * scaling,
top: 16
});
oImg.applyFilters(canvas.renderAll.bind(canvas));
canvas.add(oImg);
},{ crossOrigin: 'Anonymous' });
// Add labels
canvas.add(new fabric.Text('Pixelated', {
fontFamily: 'Arial',
fontSize: 12,
left: 0.48 * imgWidth * scaling,
top: 0,
fill: 'black',
originX: 'center'
}));
canvas.add(new fabric.Text('Not pixelated', {
fontFamily: "Arial",
fontSize: 12,
left: 1.58 * imgWidth * scaling,
top: 0,
fill: 'black',
originX: 'center'
}));
}
#divScaling
{
display: inline-block;
vertical-align: middle;
margin: 6px 32px 16px 0px;
vertical-align: top;
}
#txtScaling
{
width: 70px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.2/fabric.min.js"></script>
<div id="divScaling">
<label for="txtScaling">Scaling factor: </label>
<input id="txtScaling" type="text" value="0.2" />
<button onclick="performScaling()">
Scale
</button>
</div>
<img src="http://i.imgur.com/a47Yxsb.png" style="width: 30px;height: 30px;"/>
<canvas id="c" width="600" height="400"></canvas>
I think that pixelated problem was fixed in version 2. Edge was smoother than version 1.72
Notice breaking changes in v2:
Another breaking change is that the .resizeFilters is no more an array of resize filter. Is a single resizeFilter that you can use when the object is scaled on the canvas.
image.resizeFilter = new fabric.Image.filters.ResizeFilter({type: 'hermite'});
var canvas = new fabric.Canvas('c');
var ctx = canvas.getContext("2d");
var url = 'http://i.imgur.com/a47Yxsb.png';
var imgWidth = 1024;
function performScaling() {
// Get scaling factor
var scaling = parseFloat(document.getElementById("txtScaling").value);
canvas.clear();
// As suggested in Fabric.js introduction
fabric.Image.fromURL(url, function (oImg) {
oImg.set({
left: imgWidth * 1.1 * scaling,
top: 16
});
oImg.scale(scaling);
oImg.resizeFilter = new fabric.Image.filters.Resize({
resizeType: 'sliceHack'
});
oImg.applyResizeFilters();
canvas.add(oImg);
canvas.renderAll();
// canvas.setBackgroundImage(
// oImg,
// () => {
// canvas.renderAll();
// },
// );
},{ crossOrigin: 'Anonymous' });
// Add labels
canvas.add(new fabric.Text('Not pixelated', {
fontFamily: "Arial",
fontSize: 12,
left: 1.58 * imgWidth * scaling,
top: 0,
fill: 'black',
originX: 'center'
}));
}
#divScaling
{
display: inline-block;
vertical-align: middle;
margin: 6px 32px 16px 0px;
vertical-align: top;
}
#txtScaling
{
width: 70px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.4/fabric.min.js"></script>
<div id="divScaling">
<label for="txtScaling">Scaling factor: </label>
<input id="txtScaling" type="text" value="0.2" />
<button onclick="performScaling()">
Scale
</button>
</div>
<img src="http://i.imgur.com/a47Yxsb.png" style="width: 30px;height: 30px;"/>
<canvas id="c" width="600" height="400"></canvas>
When I remove the object and click on canvas element , the removed shape re-appears and I am not able to select it also.
var delete = function(){
var canvas = document.getElementById("canvas").fabric;
canvas.remove(canvas.getActiveObject());
}
You have to register an handler to object selection on canvas, then remove the object.
Check the runnable snippet below if could work for your needs:
$(function() {
var canvas = new fabric.Canvas('c')
var operation = '';
var circle = new fabric.Circle({
radius: 20,
fill: 'green',
left: 100,
top: 100
});
var triangle = new fabric.Triangle({
width: 20,
height: 30,
fill: 'blue',
left: 50,
top: 50
});
canvas.add(circle, triangle);
canvas.on('object:selected', doOperationHandler);
function doOperationHandler() {
if (operation == 'remove') {
remove();
}
}
function remove() {
canvas.remove(canvas.getActiveObject());
}
$('#btn_select').on('click', function() {
operation = '';
});
$('#btn_delete').on('click', function() {
operation = 'remove';
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='c'>
</canvas>
<button id='btn_select'>Select</button>
<button id='btn_delete'>Delete</button>