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/
Related
I am resizing an image in Javascript to have the image fill the parent container, with its correct aspect ratio. On top of that image i have items that i need to overlay on the image with specific coordinates.
Original image size is: 654px X 418px
On top of that image i overlay a few other canvas items at exact coordinates, 56px X 82px
I resize the image, keeping correct aspect ratio of 1.56
Image container to scale too: 1672px X 829px
New image size with correct aspect ratio: 1297px X 829px
How do i calculate where the new canvas overlay coordinates are??
You can use the properties of the image element to determine the scaling factor to apply to the overlay items.
The intrinsic (unscaled) dimensions of an image are:
const unscaledWidth = img.naturalWidth,
unscaledHeight = img.naturalHeight;
The dimensions of the image as drawn are:
const scaledWidth = img.offsetWidth,
scaledHeight = img.offsetHeight;
The scaling factors are thus:
const scaleX = scaledWidth / unscaledWidth,
scaleY = scaledHeight / unscaledHeight;
Ref:
HTMLImageElement.naturalWidth
HTMLImageElement.naturalHeight
HTMLElement.offsetWidth
HTMLElement.offsetHeight
I'm trying to made a collaboration tool based on fabricJs. Because I want canvas to be responsive I get some problems with pan/zoom functionality
Canvas view on large monitor
Problem is when someone on large monitor put some objects on corners for example and another user open this canvas on laptop or small monitor. In this case there is objects outside of current user viewport
Canvas view on small monitor
My idea is to add a button and functionality to fit all objects inside view with zoom and viewportTransform (I don't want to move objects in center of canvas because after save I will get different results from origin).
For this first I create a group with
let group = new fabric.Group(_this.canvas.getObjects()); and then I calculate zoom with
canvas.height / group.height and I look zoom to be calculated properly.
After that I don't know how to calculate where I need to zoom canvas. I try with getBoundingRect, calcViewportBoundaries and other function but can`t get correct zoom point.
JSFiddle - https://jsfiddle.net/znru0yx6/4/
I know this is 2.5 years after you asked. But this might help others who stumble across this (as i did) and give you some closure as well.
I've tried to include as many comments as possible as i always prefer to see the explanation alongside each line, rather than separately.
window.zoomToContents = function(canvas){
//first check if there are any elemnts to zoom to
if (canvas.getObjects().length < 1) {
return;
}
// reset zoom so pan actions work as expected
canvas.setZoom(1);
//group all the objects
const group = new fabric.Group(canvas.getObjects());
//find the centre of the group on the canvas
const x = (group.left + (group.width / 2)) - (canvas.width / 2);
const y = (group.top + (group.height / 2)) - (canvas.height / 2);
//and pan to it
canvas.absolutePan({x:x, y:y});
//now we need to decide whether width or height should determine the scaling
//e.g. a portrait box in a landscape canvas (height) needs scaling differently to a portrait box in a portrait canvas (could be height or width)
//or a landscape box in a portrait canvas (width)
//work out the distance between the edges of the group and the canvas
const heightDist = canvas.getHeight() - group.height;
const widthDist = canvas.getWidth() - group.width;
let groupDimension = 0;
let canvasDimension = 0;
//the smaller the number then that's the side we need to use as a reference to scale
//either group is inside the canvas (positive number) so the edge closest to the limits of the canvas will be used as the reference scale (smallest positive difference)
//or the group extends outside the canvas so the edge that extends further will be the reference (large negative number)
//either way, we want the smallest number
if (heightDist < widthDist) {
//height is the reference so need the height to scale to be nearly the height of the canvas
groupDimension = group.height;
canvasDimension = canvas.getHeight();
} else {
//width is the reference so need the width to scale to be nearly the width of the canvas
groupDimension = group.width;
canvasDimension = canvas.getWidth();
}
//work out how to scale the group to match the canvas size (then only make it zoom 80% of the way)
const zoom = (canvasDimension / groupDimension) * 0.8;
//we've already panned the canvas to the centre of the group, so now zomm using teh centre of teh canvas as teh reference point
canvas.zoomToPoint({ x: canvas.width / 2, y: canvas.height / 2 }, zoom);
}
I have an animation/canvas drawing where the pixels are hard coded and not relative. How do I make it so that the canvas and the drawing/animation scales with the browser size?
I thought of making the pixel values relative to the width and height of either the browser or the canvas but I don't know if it's a good idea.
Any tips?
P.S: I didn't post the code because it is almost 1000 lines long but if required I could post part of it.
Scale to fit, fill, or stretch
Almost every device has a different display aspect ratio, and then each setup will use only part, or all of the available screen to display the window.
The only real choice you have is how to adapt to fit the available space.
As the previous answer points out you can get the available size with innerWidth, innerHeight.
So lets assume you have that as width and height.
var width = innerWidth; // or canvas width
var height = innerHeight; //
Default resolution
I am assuming you app has a fixed aspect and an optimal resolution. To make it simple to adapt your app you need to keep the optimal resolution stored in code.
var defaultWidth = ?
var defaultHeight = ?
To adapt to displays you will need to get a scale that you set at the start of the app and adjust when the display size changes. You will also need the origin. the point where coordinate 0,0 is as for some solutions that will move.
These values are all relative to the current display that app is rendering on. They will take you native coordinates and make them conform to the device pixels.
var displaySetting = {
scaleX : ?,
scaleY : ?,
originX : ?,
originY : ?,
}
Fit, fill, or stretch
The 3 most basic options.
Stretch to fit. You stretch the rendering to fit the display but lose the aspect.
Scale to fit. You scale the rendering so that you maintain the aspect and all of the display is visible. Thought depending on the device you may have empty space on the sides or top and bottom.
Scale to fill. You scale the rendering so that it fills the device screen but you may end up clipping some or the rendering on the sides or top and bottom.
.
function getDisplayTransform(type, displaySetting){
displaySetting = displaySetting || {};
// first get scales
if(type === "stretch"){
displaySetting.scaleX = width / defaultWidth;
displaySetting.scaleY = height / defaultHeight;
} else if (type === "fit") {
displaySetting.scaleX = Math.min(width / defaultWidth, height / defaultHeight);
displaySetting.scaleY = displaySetting.scaleX;
} else { // type to fill
displaySetting.scaleX = Math.max(width / defaultWidth, height / defaultHeight);
displaySetting.scaleY = displaySetting.scaleX;
}
// now that the scale is set get the location of the origin. which is
displaySetting.originX = width / 2 - defaultWidth * 0.5 * displaySetting.scaleX;
displaySetting.originY = height / 2 - defaultHeight * 0.5 * displaySetting.scaleY;
// Note that for scale to fill the origin may be of the display or canvas
return displaySetting;
}
So now you just have to set the new coordinate system
ctx.setTransform(
displaySetting.scaleX,0,
0,displaySetting.scaleY,
displaySetting.originX,displaySetting.originY
);
So to fit with space to spare use getDisplayTransform("fit",displaySetting); or to fill use getDisplayTransform("fill",displaySetting); which will have some clipping top & bottom or left & right.
Once the transform is set you render as normal. You don't have to change the coordinates in your code, Line widths, font sizes, shadow offsets all remain the same as far as you code is concerned.
Set the canvas width and height to window.innerWidth and window.innerHeight respectively. So, the code would be like (in JS):
var canvas = document.getElementById('my_canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
Above code will only change the canvas size according to window width and height. To set the drawing/animation scale, you need to set its position/size dynamically (respect to the window width/height), not hard coded.
I am using cocos2d to develop a game and I came across below situation..Please some one guide me
this.scalesizex = this.image.height/winSize.height*0.7;
this.scalesizey = this.image.width/winSize.width*0.7;
myNode.scaleX = this.scalesizex;
myNode.scaleY = this.scalesizey;
In Above code I am trying to scale my image to 1/7 of my screen size , I know the original image width and height , Can any one tell me is it possible to get the image width and height after scaling ?
p.s: I am unable to set actual image height and width , So I am using scale to reduce my image width and height to fit my screen
If you want to scale the image to 1/7th of your screen size, the scale should be computed as follows:
this.scaleX = winSize.width / (this.image.width * 7);
this.scaleY = winSize.height / (this.image.height * 7);
Then, you can compute the new width and height by multiplying the image's original width and height with these scales.
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).