I am trying to display images in the browser. I want them to display proportional to each other, so I am scaling them down by the size of the actual image. They have all been scanned at the same resolution, so it will make their sizes proportional. Here is the function to set their size:
let imageSize = (image)=>{
let multiplier = screen.width > 1200 ? 0.35 : 0.15;
let width = `${image.clientWidth * multiplier}px`;
image.style.height = `${image.clientHeight * multiplier}px`;
image.style.width = width;
}
This works. However, I have to create a variable for width. If I try to set the width the same way I am setting height, it doesn't work. For example:
let imageSize = (image)=>{
let multiplier = screen.width > 1200 ? 0.35 : 0.15;
image.style.height = `${image.clientHeight * multiplier}px`;
image.style.width = `${image.clientWidth * multiplier}px`;
}
This does not work. The width of the image ends up being WAY too small and distorting the image. I checked and the same thing is happening in both Firefox and Chrome.
This only applies to the width. the height seems to work normally. What the hell is going on here? This doesn't make any sense to me. Can anybody shed any light on why this might be happening?
Example:
let imageSize = (image)=>{
let multiplier = screen.width > 1200 ? 0.35 : 0.15;
console.log(image.clientWidth * multiplier); // 475.65
image.style.height = `${image.clientHeight * multiplier}px`;
image.style.width = `${image.clientWidth * multiplier}px`;
console.log(image.style.width); // "166.6px"
}
Related
I have a code responsible for cropping image and saving its cropped areas in a list of divs. Each of these divs represents each of the cropped images. But the problem is - I dont want them to be so huge, I want them to have fixed height and width, e.g. max 100x100px.
Codesandbox with working image cropping: https://codesandbox.io/s/fast-frost-0b5tt
Code revelant to cropping logic:
const width = pos.w + "px";
const height = pos.h + "px";
const marginLeft = - pos.x + "px";
const marginTop = - pos.y + "px";
return (
<div
key={i}
style={{
width: width,
height: height,
backgroundSize: "400px 300px",
backgroundPosition: `top ${marginTop} left ${marginLeft}`,
backgroundImage: "url('https://boygeniusreport.files.wordpress.com/2016/11/puppy-dog.jpg?quality=98&strip=all&w=400')"
}}
/>
);
You will see that image cropping works great, however newly created images have dynamic height and width.
Question: How to make these newly created divs to have fixed width and height, but without destroying it? Thanks!
Edit: New link added (the old one was missing styles)
The goal is to make the children (cropped images) to have static height and width, but keep the background image in correct position. But seems like im too stopid to do it by myself
The following change within your render function will create a 100px square centered on the selection's center point, or as close as possible keeping within the image limits (I'm not sure how to reference the original image's width & height from here).
...
const previews = crops.map(({ pos }, i) => {
const width = 100 + "px";
const height = 100 + "px";
let margx = (pos.w / 2) - 50 + pos.x;
let margy = (pos.h / 2) - 50 + pos.y;
if(margx < 0) margx = 0;
if(margy < 0) margy = 0;
// this needs origional image width & height (400,300) to get max values
const maxx = 400-100;
const maxy = 300-100;
if(margx > maxx) margx = maxx;
if(margy > maxy) margy = maxy;
const marginLeft = - margx + "px";
const marginTop = - margy + "px";
return (
<div
...
If you fix both height and width, the previews will look distorted. So I would recommend
only fixing the height.
const fixedHeight = 100;
const zoom = fixedHeight / pos.h;
const backgroundWidth = 400 * zoom;
const backgroundHeight = 300 * zoom;
const width = pos.w * zoom;
const height = fixedHeight;
const marginLeft = -pos.x * zoom;
const marginTop = -pos.y * zoom;
See results in this codesandbox demo.
I can propose something in between solutions proposed by #MunimMunna and #ArleighHix improving both solutions.
See result
// setup base image size
const imageBaseWidth = 400;
const imageBaseHeight = 300;
// choose thumbnail size and aspect ratio
const thumbHeight = 100;
const thumbWidth = 200;
// we check which axis needs to be filled to border
const zoomX = thumbWidth / pos.w;
const zoomY = thumbHeight / pos.h;
// you can improve it further by defining max zoom in level so that thumbnails don't show ugly pixels
// just use additional zoom = Math.min(zoom, maxZoom) and some more logic for handling min max margin offset so it wont go outside image bounds
const zoom = Math.max(zoomX, zoomY);
// scaling base image to best fit available space
const backgroundWidth = imageBaseWidth * zoom;
const backgroundHeight = imageBaseHeight * zoom;
// calculate offset to top left corner of biggest rect in selected region that keeps target aspect ratio
const marginLeft = thumbWidth / 2 - (pos.w / 2 + pos.x) * zoom;
const marginTop = thumbHeight / 2 - (pos.h / 2 + pos.y) * zoom;
return (
<div
className="preview"
key={i}
style={{
width: thumbWidth + "px",
height: thumbHeight + "px",
backgroundSize: `${backgroundWidth}px ${backgroundHeight}px`,
backgroundPosition: `top ${marginTop}px left ${marginLeft}px`,
backgroundImage: "url('https://boygeniusreport.files.wordpress.com/2016/11/puppy-dog.jpg?quality=98&strip=all&w=400')"
}}
/>
);
It chooses bigest posible region inside selection that keeps aspect ratio of targeted thumbnails and zooms in on it if required. You can setup target thumbnails size with fixedHeight, fixedWidth
I'd try a rewrite without the jcrop library, to have better access to all the props, size, positioning etc.
https://stackblitz.com/edit/react-7ubxaa
I've seen a few posts here trying to answer this question and I tried using the codes given as answers but haven't been able to get it to work, so I must be doing something wrong. Basically I have a div with a background-image with the CSS property "background-size: contain". I want to get the dimensions of the scaled background. Here is my code, mostly copied from another post here, with some things changed to match my div's names:
var elem = document.querySelector("#enlarged-inner .image-bg");
function getBackgroundSize(elem) {
elem = document.querySelector("#enlarged-inner .image-bg");
//get original background size
var computedStyle = getComputedStyle(elem);
var img = new Image;
img.src = computedStyle.backgroundImage.replace(/url\((['"])?(.*?)\1\)/gi, '$2');
var imgW = parseInt(img.width, 10);
var imgH = parseInt(img.height, 10);
//get scaled size
var newW = parseInt(computedStyle.width, 10);
var newH = parseInt(computedStyle.height, 10);
var scaledW = 0;
var scaledH = 0;
scaledW = imgW / imgH * newH;
scaledH = imgH / imgW * newW;
}
window.onresize = function(){ getBackgroundSize(elem); }
Its able to get the original (unscaled) size of the background image just fine, so I atleast know that the first half of the code is working. But the second part, the important part, doesn't seem to do anything. I've been testing it by changing a test divs innerHTML to the new variables:
document.getElementById("test").innerHTML = newW + " , " + newH;
I'm new to javascript and not great at math so I'm sure theres probably something I'm just not understanding correctly here.
EDIT: the code above has been updated a little and heres some more explanation.
So in this picture the blue box is a scalable div that contains the div with the background. Its height and width are set with vh and vw.
The gray box represents the background image itself, with its size set to contain. In the updated code above, newW and newH give me the dimensions of the blue box, no matter how its scaled. imgW and imgH give me the original unscaled dimensions of the background. I want scaledW and scaledH to return the scaled size of the gray box, but my math doesnt seem to work out.
I can't test it right now but I think that the variables you're modifying (newW and newH) are passed by value. So, you actually don't update your image's size. You'll probably need to use these variables to update your image's size, maybe with:
img.width = newW etc
I figured out a simpler way to achieve what I wanted. Basically what I was trying to do was have a div with an image in it, that I could scale to any size, and the entire image would always fit inside the div (similar to the background-size contain property), and the images would always maintain their original aspect ratio. In my particular case instead of a div, its the size of the full window, and I found it to be easier to work with images in img tags instead of backgrounds. Heres the code:
function getBackgroundSize(elem) {
elem = document.querySelector("#enlarged-inner img");
var imgW = elem.naturalWidth;
var imgH = elem.naturalHeight;
var newW = window.innerWidth;
var newH = window.innerHeight;
var imgRatio = imgW / imgH;
var newRatio = newW / newH;
var scaledW = 0;
var scaledH = 0;
if (imgRatio > newRatio) {
scaledW = newW;
scaledH = imgH * newW / imgW; }
else {
scaledW = imgW * newH / imgH;
scaledH = newH; }
document.querySelector("#enlarged-inner img").style.width = scaledW;
document.querySelector("#enlarged-inner img").style.height = scaledH;
}
I basically just found the unscaled height and width of the image, and the dimensions of the containing div (the window in my case), and then found their ratios of width over height. I'm not so good at math but atleast in the examples I tried to work out, if the W/H of the image was larger than W/H of the window, it would mean the image's width would be defined by the width of the window. And then the correct height of the image could be found with some simple math.
When i run my application it gives me several errors:
the first one is:
SyntaxError: class is a reserved identifier in the class thumbnail
code:
const MAXHEIGHT = 170;
const MAXWIDTH = 320;
import {Subcategory} from './subcategory'
//import {Category} from './category'
import {bootstrap} from 'angular2/platform/browser'
class Thumbnail {
width: number;
height: number;
constructor(element: HTMLImageElement) {
this.height = element.height;
this.width = element.width;
}
resize(oImage: HTMLImageElement) {
var maxWidth = 100; // Max width for the image
var maxHeight = 100; // Max height for the image
var ratio = 0; // Used for aspect ratio
var width = oImage.width; // Current image width
var height = oImage.height; // Current image height
// Check if the current width is larger than the max
if (oImage.width > MAXWIDTH) {
ratio = MAXWIDTH / width; // get ratio for scaling image
oImage.width=MAXWIDTH; // Set new width
oImage.height=height * ratio; // Scale height based on ratio
height = height * ratio; // Reset height to match scaled image
width = width * ratio; // Reset width to match scaled image
}
// Check if current height is larger than max
if (height > MAXHEIGHT) {
ratio = MAXHEIGHT / height; // get ratio for scaling image
oImage.height= MAXHEIGHT; // Set new height
oImage.width= width * ratio; // Scale width based on ratio
width = width * ratio; // Reset width to match scaled image
height = height * ratio; // Reset height to match scaled image
}
// Check if current height is smaller than max
if (height < MAXHEIGHT) {
ratio = MAXHEIGHT / height; // get ratio for scaling image
oImage.height = MAXHEIGHT; // Set new height
oImage.width = width * ratio; // Scale width based on ratio
width = width * ratio; // Reset width to match scaled image
height = height * ratio; // Reset height to match scaled image
}
// Check if the current width is smaller than the max
if (oImage.width < MAXWIDTH) {
ratio = MAXWIDTH / width; // get ratio for scaling image
oImage.width = MAXWIDTH; // Set new width
oImage.height = height * ratio; // Scale height based on ratio
height = height * ratio; // Reset height to match scaled image
width = width * ratio; // Reset width to match scaled image
}
document.body.appendChild(oImage);
console.log(oImage.height);
console.log(oImage.width);
}
};
window.onload = () => {
var oImage = new Image();
var oImage2 = new Image();
// var category = new Category(1, "string");
var subcategory = new Subcategory("teste",1,2);
oImage.src = 'AngularJS.png';
oImage.setAttribute('class','img-responsive');
oImage2.src = 'ts.jpg';
var thumbnail = new Thumbnail(oImage);
var thumbnail2 = new Thumbnail(oImage2);
thumbnail.resize(oImage);
thumbnail2.resize(oImage2);
console.log(oImage.height);
console.log(oImage.width);
};
The second error is:
GET http://localhost:51580/app
301 Moved Permanently
The configuration of angular is:
<script>
System.config({
packages: {
TypeScriptHTMLApp1: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app')
.then(null, console.error.bind(console));
</script>
I already tried to create a folder named app and moved the file for that folder but the error persists. I need help with this, I lost several hours and can't resolve anything. I attach one image with the file structure and another one with the firebug errors. Thanks in advance.
as suggested by #Eric in comment
Classes aren't supported in Firefox version < 45 according to this article
. instead try same use case in the chrome.
posted as answer may helpful to someone.
I have a class named Engine.Renderer. It just creates a new canvas and give me the possibility to easily update and render the active canvas' scene. When a new canvas is created, I apply those settings to its context:
this.context.imageSmoothingEnabled = false;
this.context.mozImageSmoothingEnabled = false;
this.context.webkitImageSmoothingEnabled = false;
In CSS, I've added those lines:
main canvas {
position: absolute;
top: 0;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
-ms-interpolation-mode: nearest-neighbor
}
I have also write a function that adjusts the canvas to the window:
[...]
resize: function () {
var width = window.innerWidth;
var height = width / 16 * 9;
if ( height > window.innerHeight ) {
height = window.innerHeight;
width = height * 16 / 9;
}
if ( width > window.innerWidth ) {
width = window.innerWidth;
height = width / 16 * 9;
}
width = Number( width ).toFixed();
height = Number( height ).toFixed();
this.canvas.style.width = width + "px";
this.canvas.style.height = height + "px";
this.container.style.width = width + "px";
this.container.style.height = height + "px";
this.container.style.left = ( ( window.innerWidth - width ) / 2 ) + "px";
// the new scale
this.scale = ( width / this.canvas.width ).toFixed( 2 );
}
[...]
Now, I have a class named Character. This class is able to create and render a new character on the given canvas. The render part looks like this:
context.drawImage(
this.outfit,
this.sprite * this.dimension.width,
this.direction * this.dimension.height,
this.dimension.width,
this.dimension.height,
this.position.x,
this.position.y,
// set the character sizes to normal size * scale
this.dimension.width * this.renderer.scale,
this.dimension.height * this.renderer.scale
);
I have two problems with it:
the game performance is even worse than before (~9 FPS when rendering a single character on ~1400x800px canvas);
character' image is not so much blurred as before, but I still can see a little blur;
How can I solve those problems?
Try using integer values for positions and sizes:
context.drawImage(
this.outfit,
this.sprite * this.dimension.width,
this.direction * this.dimension.height,
this.dimension.width,
this.dimension.height,
this.position.x|0, // make these integer
this.position.y|0,
// set the character sizes to normal size * scale
(this.dimension.width * this.renderer.scale)|0,
(this.dimension.height * this.renderer.scale)|0
);
Also, setting canvas size with CSS/style will affect interpolation. From my own tests the CSS settings for interpolation does not seem to affect canvas content any longer.
It's better, if you need a fixed small size scaled up, to set the canvas size properly and instead use scale transform (or scaled values) to draw the content:
this.canvas.width = width;
this.canvas.height = height;
Update: Based on the comments -
When changing the size of the canvas element the state is reset as well meaning the image smoothing settings need to be reapplied.
When image smoothing is disabled the browser will use nearest-neighbor which means best result is obtained when scaling 2^n (2x, 4x, 8x or 0.5x, 0.25x etc.) or otherwise "clunkyness" may show.
A modified fiddle here.
I'm writing a little script that allows the user to move and resize a div. I need to keep the aspect ratio and my logic doesn't work.
function resizing() {
var currentHeight = elmnt.offsetHeight;
var currentWidth = elmnt.offsetWidth;
var newHeight = currentHeight + (event.pageY - currentY);
var newWidth = currentWidth + (event.pageX - currentX);
var ratio = currentWidth / currentHeight;
if(ratio < 1) {
newwidth = parseInt(newHeight * ratio);
}
else {
newheight = parseInt(newWidth / ratio);
}
elmnt.style.height = newHeight + "px";
elmnt.style.width = newWidth + "px";
currentY = event.pageY;
currentX = event.pageX;
}
The script kind of works. But unfortunately it doesn't keep the aspect ratio completely correct. Sometimes, when I resize horizontyl only, the old height remains the same, sometimes it works, but one length gets resized with a little offset.
When I resize up and down and up and down again, the lengths gets more and more equal and when it is a proper square, everything is right.
Hwo can I fix my problems? Where is my fallacy?!
Your ratio is wrong I think.
You need to calculate this by taking the old width and dividing by the new width, or old height / new height.
e.g.
var ratio = newWidth / currentWidth;
newHeight = currentHeight * ratio;
Change it about if it is the height that is changing.
I could fiy it.
THANK YOU VERY MUCH!
My problem was that I first, had to track of which axis has more change. The second problem, which I didn't recognized was, that I had BIG problems with rounding issues.
When setting the css size using jQuery, it rounds. And I took the height for ratio calculations every single event.
That means that the inaccuracy was getting more and more bad.
Now I took this into account and figured out a way to get this working very good.
I now do this directly onclick and just update them instead of getting from the element:
currentHeight = $("#dragger").height();
currentWidth = $("#dragger").width();
So thanks again for your help! Here is my final result:
http://jsfiddle.net/julian_weinert/xUAZ5/30/
You have to do this, get the min scale (ratio). The code below is a part of my PHP script, but easily translated to JS. $iSrc = Source and $iDest is destination MAX width/height.
Your problem is you don't get the right ratio. The first line to define the ratio is where your problem will be solved. It gets the lowest ratio of the width or height. You just do width/height and forget height/width. That's why vertical scaling is not correct.
$scale = min($iDestWidth/$iSrcWidth, $iDestHeight/$iSrcHeight);
if($scale >= 1){
$iDestHeight = $iSrcHeight;
$iDestWidth = $iSrcWidth;
}else{
$iDestWidth = floor($scale*$iSrcWidth);
$iDestHeight = floor($scale*$iSrcHeight);
}
replace your if(ratio < 1) block with the following. offsetx and offsety relate to your (event.pageX - currentX) and (event.pageY - currentY):
if (Math.abs(offsetx) > Math.abs(offsety)) {
ratio = currentHeight/currentWidth;
newHeight = currentHeight + (offsetx * ratio);
newWidth = currentWidth + offsetx;
} else {
ratio = currentWidth/currentHeight;
newHeight = currentHeight + offsety;
newWidth = currentWidth + (offsety * ratio);
}
here is a quick jsfiddle of the whole thing in action: http://jsfiddle.net/8TWRV/