Re-scale svg element - javascript

I have different rect elements that are drawn inside an svg layer which is overlapped over a png image.
In another view I can see the elements drawn but the image below is bigger and it hasn't same scale factor, if I divide the width for height for both the images, the results aren't the same.
How can I re-scale an svg element inside an image with two different scale factors?
These images could explain what I mean to do
Image with rect drawn:
Image that I should re-scale:

I have solved the problem with a simple function.
Ours unknowns are the rect's coordinates that we want to re-scale and its size, so if we denote with oldRectX and oldRectY, newRectX and newRectY rispectively as the old and the new rect's coordinates and with oldSVGWidth and oldSVGHeight, newSVGWidth and newSVGHeight rispectively as the old and the new SVG's dimensions that contains the rects, applying this ratio we can calculate the new positionament and the new size:
oldRectX : oldSVGWidth = newRectX : newSVGWidth
So I can calculate newRectX:
newRectX = (oldRectX * newSVGWidth)/oldSVGWith
The same reasoning it's apply for calculate newRectY with the difference that I have to replace the width with the height and X with Y:
newRectY = (oldRectY * newSVGHeight)/oldSVGHeight
Finally the new sizes:
newRectWidth = oldRectWidth * (newSVGWidth/oldSVGWidth)
newRectHeight = oldRectWidth * (newSVGHeight/oldSVGHeight)

Related

How can i get high resolution image from smaller canvas?

I am using one canvas in my web app and it's actual height and width are 500px. I am showing this canvas on screen as 500px square but i want image exported from this canvas as 1600px square. I have tried below code with no luck.
canvas.width = 1600;
canvas.style.width = 500;
Any help will be appreciated.
You can have the canvas display at 500px while still having a resolution of 1600px. Display size and resolution are independent. For resolution you set the canvas width and height properties. For display size you set the canvas style width and height properties.
// create a canvas or get it from the page
var canvas = document.createElement("canvas");
// set the resolution (number of pixels)
canvas.width = canvas.height = 1600;
// set the display size
canvas.style.width = canvas.style.height = "500px";
// get the rendering context
var ctx = canvas.getContext("2d");
To get the rendering to match the display size you need to scale up all rendering. You can do this by setting the transform scale to the canvas resolution divided by the display size
var scale = 1600 / 500; // get the scale that matches display size
ctx.setTransform(scale,0,0,scale,0,0);
Now when you render to the canvas you use the screen size coordinates.
ctx.fillRect(0,0,500,500); // fill all of the canvas.
ctx.fillStyle = "red"; // draw a red circle 100 display pixels in size.
ctx.beginPath();
ctx.arc(250,250,100,0,Math.PI * 2);
ctx.fill();
When you then save the canvas, what ever method you use as long as it is not screen capture the saved canvas will be 1600 by 1600 and all the rendering will be correctly positions and proportional
HTML
<canvas width="1600px" height="1600px" > </canvas>
CSS
canvas{
position :absolute;
transform:scale(0.3125);
left:-500px; //adjust
top:-350px; //adjust
}
Use transform:scale() to adjust size of your canvas
Now 1600 * 1600 will be the actual size of your canvas, so you can directly export images from your canvas
But in view it show as 500px * 500px beacuse it's scaled down, it dose not affect the image quality while exporting
Honest answer: you can't.
If you did, then you'd have found a way to losslessly compress data with less than 1/9th of the original size, and without any encoding, which is unarguably impossible.
What you can do is scale it up in a way that it at least doesn't get blurry. To do that, you need the final image to be an integer multiple of the previous canvas, so the browser won't apply anti-aliasing. Or if you want to use your own copying formula with putImageData that would get rid of anti-aliasing, you'll still get various incongruences and it would be very slow
In your case, the closest you could get is 1500x1500 ( 3*500x3*500 ). If your point was to process an image, you're not in luck, but if you just want to display something good enough, you can resort to various other tricks such as centering the canvas and using properties like box-shadow to make it clear that it's separate from the rest of the screen

enlarging polygon without scaling in fabricjs

I have a page where initially is loaded an SVG and then for each path of the SVG is applyed a background image in a way similar to the one showed in the dynamic pattern demo.
At the same time, I want to enlarge the loaded SVG to screen size. I have used the setScaleX() method on all the SVG's elements but the background image appears distorted, probably because of the scaling ratio.
So I've tried to use the setWidth() method, calling it on the original value of the element multiplied by my ratio. This solution works with an SVG composed of only "Rect", but when there are some polygons inside they're not enlarged, only the containing box is updated.
I can understand this, setting the width of a polygon necessarily means updating the coordinates of its points, but this is the effect I want to obtain.
So, there's a way to enlarge a polygon in fabricjs without using setScaleX() method?
After long time I've found a solution for this problem. You can find and explanation of the FabricJS behaviour in this github issue I made.
The way I've found to enlarge a polygon without scaling it in FabricJS is to move every single point of the polygon, multiplying it by the scale ratio needed.
There's the code I'm using.
var paths = canvas.getObjects();
var ratio = 2;
for (var c=0; c<paths.length; c++) {
var p = paths[c];
p.setWidth(p.getWidth() * ratio);
p.setHeight(p.getHeight() * ratio);
if (p.points) {
for(var j=0;j< p.points.length; j++) {
p.points[j].x *= ratio;
p.points[j].y *= ratio;
}
}
p.setLeft(p.getLeft() * ratio);
p.setTop(p.getTop() * ratio);
p.setCoords();
}
canvas.renderAll();

Get pixel color from resized canvas, on mouseover

I have checked this question which provides the perfect answer. But my problem is slightly different. I have a canvas of 300 x 300 and i am re-sizing the canvas using css to 200 x 60. If i re-size the canvas using css i am not able to get the color value onmouseover.
In the re-sized fiddle if you mouse over right below the red or blue rectangles you will notice it still says #FF0000 & #0000FF respectively while it should be #000000. So how to make it work even with re-sized canvas?
Fiddle: Re-sized with css.
Fiddle: Non re-sized.
You need to apply a scale factor inside the mouse handler method. The scale factor is the relationship between your canvas's bitmap (actual size) and the element size (CSS size).
For example:
// find scale:
var sx = example.width / parseInt(example.style.width, 10);
var sy = example.height / parseInt(example.style.height, 10);
// apply to x/y
x = (x * sx)|0; // scale and cut any fraction to get integer value
y = (y * sy)|0;
Updated fiddle
In addition the code need to have some boundary check of the coordinates so getImageData() won't fail (not shown here).

Scale div into div like background image

I'm working on a web app and now facing a rather tricky task.
I have an image that I have to add overlay for.
Let's say I have an image of a car. I need to add values, like outside and inside temperature, on top of the car image.
It would be easy to add fixed pixel offsets for those temperature labels, but the image need to be scalable to every screen height and width-wise. I can't think of easy way to scale div into div exactly as "background-size:contain;" does for images.
Could someone point me to right tracks before I write complex javascript scaling logic?
ww = window width
wh = window height
ow = Original Width of your image
oh = Original Height of your image
nw = New Width of your image
nh = New Height of your image
mt = Margin top to adjust image to the screen vertically
ox = overlay location x coordinate according to original size of the image
oy = overlay location y coordinate according to original size of the image
nox = New ox
noy = new oy
sf = scaling factor
based on screen size you'll have a scaling factor for your image.
sf = ww / ow -> we find our scaling factor
nh = oh * sf -> then calculate our new image height
mt = (oh - nh) / 2 -> amount we need to shift image up to center screen
nox = ox * sf -> new overlay x coordinate
noy = (oy * sf) - mt -> new overlay y coordinate
edit
if image is too wide then this logic needs to be adjusted to shift image horizontally not vertically
edit 2
If you're targeting modern browsers only, then you can use CSS transform: scale(sx[, sy]); . This will re-size your DIV and all it's content proportionally Use transform: scale() with transform-origin: 0 0; to re-size from top left corner.
If you are using a SVG image this would be easy:
http://css-tricks.com/svg-text-typographic-designs/
Here is two examples with CSS:
http://codepen.io/chriscoyier/pen/zxqdev
http://maketea.co.uk/2013/12/16/smooth-text-overlays-with-css-transforms.html
I'm never tested CSS3 object-fit , but I think it's very useful for your solution:
https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
https://dev.opera.com/articles/css3-object-fit-object-position/

Resizing images (in SVG format) after applying "rotate" transformation

I've been working on an application that allows users to add images to a viewport, then resize them, crop them and rotate them. For the rotation to work, the images are added as SVG's, using the Raphael.js library.
All images have an outline (called wrapper). When an image is resized, so is its wrapper. When the image is rotated, the wrapper expands too, so that it will contain the rotated image (see image bellow).
So far, so good. Except that when I attempt to resize an image object AFTER it has been rotated, it won't be contained within its outline anymore (see image bellow).
When resizing an image, the svg oject's "x" and "y" attributes also need to be adjusted. Both the new size (width + height) of the image and the "x" and "y" attributes are computed as seen bellow:
var ratio_width = w / init.wrapper.width, // w = the new width of the wrapper (computed at each resize step)
ratio_height = h / init.wrapper.height, // h = the new height of the wrapper
svg_width = init.svg.width * ratio_width, // svg_width = the new width of the image
svg_height = init.svg.height * ratio_height, // svg_height = the new height of the image
x = init.svg.x*ratio_width,
y = init.svg.y*ratio_height;
this.svg.attr({
"x": x,
"y": y,
"width": svg_width,
"height": svg_height
});
The "init" JavaScript object contains the size (width & height) of the wrapper AND the size of the image (after the image is rotated, their dimensions will differ), but also the values of the x and y attributes BEFORE the resize operation.
The thing is that all the values computed above are correct. I know because I've also tried destroying the svg at each resize step and re-appending it (but it's not an option, there's too much of a flicker), with those same values for size, x and y, like so:
// "this.angle" on the second line is the current rotation angle
this.svg.remove();
this.svg = this.canvas.image(this.src,x,y,svg_width,svg_height).transform("r"+this.angle);
So if I simply remove the object and then create it again with the values above, why won't simply changing its attributes do the job as it should? I've been pulling my hair out for 3 days now, so I'd appreciate some help!

Categories