I am currently having an issue when setting width and height of a svg object in fabricjs. I tried setting it like this
svg.width = 100;
svg.height = 100;
But I am not getting the result I expected. It just crops the svg instead of scaling it. So, how can I set its width and height properly?
Any help will be appreciated. Thanks!
Use scaleToWidth() and scaleToHeight() method to properly set SVG object*'s* width and height respectively.
:-: working example :-:
var canvas = new fabric.Canvas('fabric-canvas');
fabric.loadSVGFromURL('http://cdn.shopify.com/s/files/1/0496/1029/files/Freesample.svg?5153', function(objects, options) {
var svg = fabric.util.groupSVGElements(objects, options);
svg.left = 50;
svg.top = 50;
svg.scaleToWidth(100);
svg.scaleToHeight(100);
canvas.add(svg);
canvas.renderAll();
});
canvas{border:1px solid #ccc}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.13/fabric.min.js"></script>
<canvas id="fabric-canvas" width="200" height="200"></canvas>
Related
I am new to fabric.js, I want to create a circle using fabric and render it on canvas I have done the following code. But this code generates an Oval shaped figure which is not a perfect circle, Please let me know why is this happening.
this.canvasIns = new fabric.Canvas('mySampleCanvas');
var myCircle = new fabric.Circle({
radius:50,
borderColor: 'black',
fill:'#fff'
});
this.canvasIns.add(myCircle);
this.canvasIns.renderAll();
I found the problem due to which I was facing the issue. It was happening because I was dynamically changing the height, width of the canvas using the following code.
Due to which the issue was occurring.
var canvasElement = this.myCanvas.nativeElement;
canvasElement.style.height = canvasElement.nextElementSibling.style.height = this.canvas_css.height;
canvasElement.style.width = canvasElement.nextElementSibling.style.width = this.canvas_css.width;
canvasElement.style.left = canvasElement.nextElementSibling.style.left = this.canvas_css.left;
canvasElement.style.top = canvasElement.nextElementSibling.style.top = this.canvas_css.top;
I removed this code and gave static style to the canvas and its working fine.
Check that the height and width off your html attributes and css are the same. If you use CSS to set the size of the canvas the canvas will be stretched not resized to the CSS Values.
Example:
- Canvas Size: 500:500, your circle: 100:100
- CSS Size: 1000:500
- Canvas size on screen: 1000:500, your circle: 200:100
In my case I solved the problem by adding a <div> around the canvas generated by fabric.js to get the desired width and height values and then setting the canvas size with javascript on pageload:
Html:
<div id="drawingArea">
<canvas id="canvas" class="canvas-layer" height="500" width="500"></canvas>
</div>
Javascript:
var canvas = new fabric.Canvas('canvas');
$(window).on('load', function (e) {
canvas.setHeight($('#drawingArea').height());
canvas.setWidth($('#drawingArea').width());
});
When programming in HTML canvas, the js dimensions of the canvas don't always match the css dimensions.
Why is this happening and how can I fix this?
I've found the problem. I was setting the dimensions of the using CSS, when you actually have to set the width and height attributes. This was causing it to be stretched/skewed.
var canvas = $('<canvas/>').attr({width: cw, height: ch}).appendTo('body');
http://jsfiddle.net/h2yJn/66/
You can try to set the width and height of the canvas to be equal to the dimensions of the window object:
function createCanvas() {
var ctx = //the canvas context;
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
//your code
}
In addition, it is a good idea to set the body and html tags to the full width of the window:
body, html{
height: 100%;
width: 100%;
}
You can also do this by using pure javascript and set the dimensions of the canvas in javascript depending on your CSS values:
//Get Canvas
var canvas = document.getElementById("canv");
// Get computed style of the canvas element.
var cstyle = window.getComputedStyle(canvas);
// Returns the width as str in px: e.g. 600px.
// Parse resolves that issue.
canv.width = parseInt(cstyle.width);
canv.height = parseInt(cstyle.height);
Demo:
https://jsfiddle.net/ofaghxfq/2/
I am trying to write code that resizes an SVG overlay on top of an image. The server I am working with returns both the image and via an API, a list of polygon points I need to overlay on top of the image.
This is what it should look like (the image below is a correctly aligned SVG layer). The image size is 1280x720 (I've scaled it down)
What I need to do in my app (ionic v1 app) is to make sure the SVG overlay resizes as the browser window resizes and it seems to be very hard. Here is my approach:
I am trapping a window resize event and when the image is resized, I scale the SVG polygon points relative to the size of the drawn window as it seems there is really no way to "automatically" scale the SVG by the browser like it does with an image.
Here is my code pen as you see it doesn't work as intended when I rescale (and for that matter in when its full size the overlays are not accurate). The overlays don't look accurate and when I resize it all messed up. Can someone help?
Given SO needs a code block for codepen links here it is, but its just easier to look at the codepen if you want to run it
CSS:
.imagecontainer{position:relative; margin:0 auto;}
.zonelayer
{
position:absolute;
top:0;
left:0;
background:none;
}
.zonelayer polygon {
fill-opacity: 0.25;
stroke-width: 2px;
}
.Active {
stroke: #ff0000;
fill: #ff0000;
}
HTML code:
<ion-content>
image:{{disp}}<br/>
<small>points: <span ng-repeat="item in zoneArray">{{item}}</span></small>
<div class="imagecontainer">
<img id="singlemonitor" style="width:100vw; height:100vh;object-fit:contain" ng-src="http://m9.i.pbase.com/o9/63/103963/1/164771719.2SfdldRy.nphzms.jpeg" />
<div class="zonelayer">
<svg ng-attr-width="{{cw}}" ng-attr-height="{{ch}}" class="zonelayer" ng-attr-viewBox="0 0 {{cw}} {{ch}}">
<polygon ng-repeat="item in zoneArray" ng-attr-points="{{item}}" class="Active"/> </polygon>
</svg>
</div>
</div>
</ion-content>
JS controller:
window.addEventListener('resize', liveloaded);
liveloaded();
// credit: http://stackoverflow.com/questions/41411891/most-elegant-way-to-parse-scale-and-re-string-a-string-of-number-co-ordinates?noredirect=1#41411927
function scaleCoords(string, sx, sy) {
var f = [sx, sy];
return string.split(' ').map(function (a) {
return a.split(',').map(function (b, i) {
return Math.round(b * f[i]);
}).join(',');
}).join(' ');
}
function liveloaded()
{
$timeout (function () {
console.log ("IMAGE LOADED");
var img =document.getElementById("singlemonitor");
//var offset = img.getBoundingClientRect();
$scope.cw = img.clientWidth;
$scope.ch = img.clientHeight;
$scope.vx = img.offsetWidth;
$scope.vy = img.offsetHeight;
var rect = img.getBoundingClientRect();
//console.log(rect.top, rect.right, rect.bottom, rect.left);
$scope.disp = img.clientWidth+ "x"+img.clientHeight + " with offsets:"+$scope.vx+"/"+$scope.vy;
$scope.zoneArray = [
"598,70 700,101 658,531 516,436",
"531,243 687,316 663,593 360,717 191,520",
"929,180 1108,248 985,707 847,676",
"275,17 422,45 412,312 271,235",
];
var ow = 1280;
var oh = 720;
for (var i=0; i < $scope.zoneArray.length; i++)
{
var sx = $scope.cw/ow;
var sy = $scope.ch/oh;
$scope.zoneArray[i] = scaleCoords($scope.zoneArray[i],sx,sy);
console.log ("SCALED:"+$scope.zoneArray[i]);
}
});
}
There are a couple of issues with your code.
The main problem is you can't use ng-attr-viewBox, because angular will "normalise" the attribute to lower case. It turns the attribute into viewbox (lower case B) which is (currently) invalid. viewBox is case sensitive.
The solution is to use a special trick of Angular to preserve camel-case. If you use ng-attr-view_box, it will generate the correctly camel-cased attribute name of viewBox.
<svg width="100vw" height="100vh" class="zonelayer" ng-attr-view_box="0 0 {{cw}} {{ch}}">
The other thing is that you are using the wrong width and height values for the viewBox. You need to use the natural/intrinsic image dimensions in your viewBox.
$scope.cw = img.naturalWidth;
$scope.ch = img.naturalHeight;
Link to updated code pen
No matter what I try, I can't figure out why does drawImage vertically stretch a picture. Here is the code:
<canvas id="canvas" style="padding:0;border:0;margin:0 auto;
width:320px;height:456px;z-index:0;"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var d = new Image
d.src = './images/portadilla.jpg';
d.onload = function a(){context.drawImage(d, 0, 0);}
</script>
My image is 456px high and 320px wide, same as the canvas. I've tried using different images and formats, but the problem remains. How would you solve this?
Because you're not setting the size of the canvas correctly. Don't use CSS/style to set the size but use its attributes:
<canvas id="canvas" width=320 height=456 style="..."></canvas>
Your image is stretched because the default size of the bitmap for a canvas element is 300x150 pixels so if you don't set the size properly the canvas bitmap will be stretched to fit the element size set with CSS.
You can also set the size using JavaScript:
var canvas = document.getElementById('canvas');
canvas.width = 320;
canvas.height = 456;
I have a canvas element defined statically in the html with a width and height. If I attempt to use JavaScript to resize it dynamically (setting a new width and height - either on the attributes of the canvas or via the style properties) I get the following error in Firefox:
uncaught exception: [Exception... "Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)" location: "JS frame :: file:///home/russh/Desktop/test.html :: onclick :: line 1" data: no]
Is it possible to resize this element or do I have to destroy it and create a new element on the fly?
You didn't publish your code, and I suspect you do something wrong. it is possible to change the size by assigning width and height attributes using numbers:
canvasNode.width = 200; // in pixels
canvasNode.height = 100; // in pixels
At least it works for me. Make sure you don't assign strings (e.g., "2cm", "3in", or "2.5px"), and don't mess with styles.
Actually this is a publicly available knowledge — you can read all about it in the HTML canvas spec — it is very small and unusually informative. This is the whole DOM interface:
interface HTMLCanvasElement : HTMLElement {
attribute unsigned long width;
attribute unsigned long height;
DOMString toDataURL();
DOMString toDataURL(in DOMString type, [Variadic] in any args);
DOMObject getContext(in DOMString contextId);
};
As you can see it defines 2 attributes width and height, and both of them are unsigned long.
Note that if your canvas is statically declared you should use the width and height attributes, not the style, eg. this will work:
<canvas id="c" height="100" width="100" style="border:1px"></canvas>
<script>
document.getElementById('c').width = 200;
</script>
But this will not work:
<canvas id="c" style="width: 100px; height: 100px; border:1px"></canvas>
<script>
document.getElementById('c').width = 200;
</script>
I just had the same problem as you, but found out about the toDataURL method, which proved useful.
The gist of it is to turn your current canvas into a dataURL, change your canvas size, and then draw what you had back into your canvas from the dataURL you saved.
So here's my code:
var oldCanvas = canvas.toDataURL("image/png");
var img = new Image();
img.src = oldCanvas;
img.onload = function (){
canvas.height += 100;
ctx.drawImage(img, 0, 0);
}
<div id="canvasdiv" style="margin: 5px; height: 100%; width: 100%;">
<canvas id="mycanvas" style="border: 1px solid red;"></canvas>
</div>
<script>
$(function(){
InitContext();
});
function InitContext()
{
var $canvasDiv = $('#canvasdiv');
var canvas = document.getElementById("mycanvas");
canvas.height = $canvasDiv.innerHeight();
canvas.width = $canvasDiv.innerWidth();
}
</script>
This worked for me just now:
<canvas id="c" height="100" width="100" style="border:1px solid red"></canvas>
<script>
var c = document.getElementById('c');
alert(c.height + ' ' + c.width);
c.height = 200;
c.width = 200;
alert(c.height + ' ' + c.width);
</script>
Here's my effort to give a more complete answer (building on #john's answer).
The initial issue I encountered was changing the width and height of a canvas node (using styles), resulted in the contents just being "zoomed" or "shrunk." This was not the desired effect.
So, say you want to draw two rectangles of arbitrary size in a canvas that is 100px by 100px.
<canvas width="100" height="100"></canvas>
To ensure that the rectangles will not exceed the size of the canvas and therefore not be visible, you need to ensure that the canvas is big enough.
var $canvas = $('canvas'),
oldCanvas,
context = $canvas[0].getContext('2d');
function drawRects(x, y, width, height)
{
if (($canvas.width() < x+width) || $canvas.height() < y+height)
{
oldCanvas = $canvas[0].toDataURL("image/png")
$canvas[0].width = x+width;
$canvas[0].height = y+height;
var img = new Image();
img.src = oldCanvas;
img.onload = function (){
context.drawImage(img, 0, 0);
};
}
context.strokeRect(x, y, width, height);
}
drawRects(5,5, 10, 10);
drawRects(15,15, 20, 20);
drawRects(35,35, 40, 40);
drawRects(75, 75, 80, 80);
Finally, here's the jsfiddle for this: http://jsfiddle.net/Rka6D/4/ .
Prototypes can be a hassle to work with, and from the _PROTO part of the error it appears your error is caused by, say, HTMLCanvasElement.prototype.width, possibly as an attempt to resize all the canvases at once.
As a suggestion, if you are trying to resize a number of canvases at once, you could try:
<canvas></canvas>
<canvas></canvas>
<canvas></canvas>
<script type="text/javascript">
...
</script>
In the JavaScript, instead of invoking a prototype, try this:
$$ = function(){
return document.querySelectorAll.apply(document,arguments);
}
for(var i in $$('canvas')){
canvas = $$('canvas')[i];
canvas.width = canvas.width+100;
canvas.height = canvas.height+100;
}
This would resize all the canvases by adding 100 px to their size, as is demonstrated in this example
Hope this helped.