I have a div element within my webpage, with the id "glCanvas". I want to be able to match it's aspect ratio to that of a photo that a user uploads:
$("#imageInput").change(function() {
readBackgroundFile(this);
readExif(this, function(data) {
aspectRatio = data.width/data.height;
height = $("#glCanvas").height();
width = $("#glCanvas").width();
newWidth = height*aspectRatio;
console.log(data.focalLength);
halfWidth = newWidth/2;
FOV = 2 * Math.atan(halfWidth*0.5, data.focalLength);
camera.aspect(aspectRatio);
$("#glCanvas").width(newWidth);
renderer.setSize(newWidth, height);
viewerPosition = new THREE.Vector3().fromArray(llhxyz(data.latitude, data.longitude, data.altitude));
updateScenePlacement();
});
I think maybe the style.CSS may be the issue:
#glCanvas{
background-color: black;
width: 650px;
margin: 0 auto 0 auto;
height: 500px;
So i tried deleting it, but unfortunately that removed it all together. What do i do?
Related
I have 2 boxes / divs. An outer box and an inner box. The inner box is always contained within the outer box. Given the following info:
outer box: width=200 height=100
inner box: aspect ratio=16/9
In JavaScript, how do I calculate the maximum size of the inner box such that its aspect ratio is preserved?
I know you're asking for JavaScript specifically, but this is pretty simple to do in CSS with aspect-ratio, and if you need the dimensions in JS you could just grab the rendered dimensions.
const pre = document.querySelector('pre');
const aspect = document.querySelector('.ratio');
// getboundingClientRect also works.
const dimensions = window.getComputedStyle(aspect);
pre.textContent = `width: ${dimensions.width}, height: ${dimensions.height}`;
.container {
width: 200px;
height: 100px;
}
.ratio {
aspect-ratio: 16 / 9;
/* Just keeping it within the constaints */
max-height: 100px;
border: 1px solid red;
}
.no-ratio {
border: 1px solid red;
}
<div class="container">
<div class="ratio">
16:9
</div>
</div>
<pre></pre>
<div class="container">
<div class="no-ratio">
Not 16:9.
</div>
</div>
<pre></pre>
Get the outer aspect ratio and use that to determine if the inner box needs to be letter-boxed (landscape, shorter) or pillar-boxed (portrait, narrower) relative to the outer box. Calculate the inner dimensions based on that. You can also calculate the offsets needed to center it.
const outerWidth = 200;
const outerHeight = 100;
const aspectRatio = 16/9;
let innerWidth, innerHeight, innerTop, innerLeft;
if (outerWidth / outerHeight > aspectRatio) {
innerWidth = outerHeight * aspectRatio;
innerHeight = outerHeight;
innerLeft = (outerWidth - innerWidth) / 2;
innerTop = 0;
} else {
innerWidth = outerWidth;
innerHeight = outerWidth / aspectRatio;
innerLeft = 0;
innerTop = (outerHeight - innerHeight) / 2;
}
let outerBoxWidth = 200;
let outerBoxHeight = 100;
let maxInnerBoxWidth = ((outerBoxWidth / 16) | 0);
let maxInnerBoxHeight = ((outerBoxHeight / 9) | 0);
let widthLower = maxInnerBoxHeight > maxInnerBoxWidth;
if(widthLower){
let innerBoxHeight = 9 * maxInnerBoxWidth;
let innerBoxWidth = 17 * maxInnerBoxWidth;
}else{
let innerBoxHeight = 9 * maxInnerBoxHeight;
let innerBoxWidth = 17 * maxInnerBoxHeight;
}
Based on the answer from Ouroborus I came up with the following which proves it works.
const getBoxes = (outerWidth, outerHeight, innerAspectRatio) => {
const outer = { width: outerWidth, height: outerHeight, aspectRatio: outerWidth / outerHeight }
const inner = { width: null, height: null, aspectRatio: innerAspectRatio }
const pillarBoxed = outer.aspectRatio > inner.aspectRatio
inner.width = !pillarBoxed ? outer.width : outer.height * inner.aspectRatio
inner.height = pillarBoxed ? outer.height : outer.width / inner.aspectRatio
return { outer, inner }
}
const adjust = 40
const boxes1 = getBoxes(160 + adjust, 90, 16/9)
const boxes2 = getBoxes(160, 90 + adjust, 16/9)
// display purposes only
const displayBoxes = (boxes, outerId, innerId) => {
const outerEl = document.getElementById(outerId)
outerEl.style.width = boxes.outer.width + 'px'
outerEl.style.height = boxes.outer.height + 'px'
const innerEl = document.getElementById(innerId)
innerEl.style.width = boxes.inner.width + 'px'
innerEl.style.height = boxes.inner.height + 'px'
}
displayBoxes(boxes1, 'outer1', 'inner1')
displayBoxes(boxes2, 'outer2', 'inner2')
// console.log('boxes1', boxes1)
// console.log('boxes2', boxes2)
#outer1, #outer2 {
background-color: black;
display: inline-flex;
justify-content: center;
align-items: center;
margin: 10px;
}
#inner1, #inner2 {
background-color: red;
}
<div id="outer1">
<div id="inner1"></div>
</div>
<div id="outer2">
<div id="inner2"></div>
</div>
To make this easy to test, I made the outer box 160x90 BUT 40px wider (first example) and 40px taller (second example). Given that this works nicely with a 16/9 aspect ratio, there should be 20px left and right in the first example and 20px above and below in the second example. Which there is!
Also, the inner box should be 160x90 in both examples, which it is!
Proof that this code works as expected.
For my React Native app, I must import an image of an unknown width from an API. I already set the height of the image retrieved (30px). However, the image can be of any width.
Is there a way to set the width automatically based on the height I set? Is there a way to do that?
Here is my code:
<Image style={styles.logo} resizeMode="contain" source={{url: `${Config.S3_COMPARATORS}/${this.props.account.institutionId}`}} />
The styles.logo only contains: height: 30;
I don't know the width or the aspect ratio. I only know the image is 100px of height and that the width is between 0 and 300px.
Can you help me?
This should maintain the aspect ratio:
height: 100px;
width: auto;
img{
height: 100px;
width: auto;
}
In your componentDidMount() paste below method
let c = await this._getAspectRatio(IMAGE_URL)
if(c!=null){
c.h
c.w
}
This is a helper function
async _getAspectRatio(url){
var aspectRatio = 0
var c = 0;
var h = 0 , w = 0;
try{
await Image.getSize(url, (width, height) => {
console.log('>>url',url)
console.log('width ',width,'==height ',height)
console.log('ratio ',width/height)
h = height
w= width
});
}catch(e){
console.log(JSON.stringify(e))
}
return {c , h, w};
}
Using the above methods you can have the dynamic height width of the image, the hold this dynamic height width using setState
Then apply it to the image
height:this.props.aspect[this.props.index].height*.06,width:this.props.aspect[this.props.index].width * .06,
Here .6 is just a multiplier you can switch between values to fit your image needs,
I have 2 JavaScript pages. the first one has an image control and the second one has a function that returns a callback as an image.
page 1:
//Calls the function from page 2 and the callback image is set as the source of the control.
previewImage(current, function(img) {
jQuery(".mapItem").attr("src",img.src);
});
page 2:
//The functions callback returns an image which we use in page 1 (above)
var canvas = document.createElement('canvas');
context = canvas.getContext('2d');
context.drawImage(this.m_Images[i],0,0,canvas.width,canvas.height);
var t = new Image();
t.src = canvas.toDataURL();
callback(t);
The issue:
my control in page 1 (.mapItem) has a height and width of 75.2px (fixed)
The image that is coming from the callback however will have a different size each time e.g one day it can be 200px * 300px and one day it can be 150px * 200px etc
How can i clip the image of the callback? I want the image (t) to zero (0) as x and y starting points and then clip the image where ever the .mapItem control height and width is.
I need this to be proportional ratio. So i cant just add the following code:
context.drawImage(this.m_Images[i],0,0,72.5,72.5); because this will ruin the image as we dont even know if it is square shaped.
What should i do?
Thanks in advance :)
I tried to simulate your environment so here you have a possible solution.
loadImage is like your image loader that you have in your Page 2.
The load callback calls the previewImage() method.
This method receive the img extract the width and height calculate the aspect ratio with the aspectRatioFit() method.
And finally draw the image into the mapItem element in Page 1.
Note:
This is just a simulation based on your description try to adapt it your real project. If you have a question or you need an update of my solution. Please let me know.
Update:
If you want to Crop the image then just need to add min-width and in-height into the CSS style:
#mapItem {
display: block;
overflow: hidden;
height: 75px;
width: 75px;
}
#mapItem canvas {
display: block; // Otherwise it keeps some space around baseline
min-width: 100%; // Scale up to fill container width
min-height: 100%; // Scale up to fill container height
-ms-interpolation-mode: bicubic; // Scaled images look a bit better in IE now
}
This will crop your image that you receive from the page 1.
function loadImage() {
var preview = document.getElementById('source_image');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener('load', function(e) {
preview.src = e.target.result;
preview.onload = function() {
previewImage(this);
};
}, false);
if (file) {
reader.readAsDataURL(file);
}
};
function previewImage(img) {
console.log(img.width + ' ' + img.height);
var maxSize = {
width: 75,
height: 75
};
var croppedImage = document.getElementById('mapItem');
var canvas = document.createElement('canvas');
var ctx;
//var newSize = aspectRatioFit(img.width, img.height, maxSize.width, maxSize.height);
canvas.width = img.width;
canvas.height = img.height;
croppedImage.appendChild(canvas);
ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
function aspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
return {
width: srcWidth * ratio,
height: srcHeight * ratio
};
}
.clear {
padding-bottom: 20%;
}
#mapItem {
display: block;
overflow: hidden;
height: 75px;
width: 75px;
}
#mapItem canvas {
display: block; /* Otherwise it keeps some space around baseline */
min-width: 100%; /* Scale up to fill container width */
min-height: 100%; /* Scale up to fill container height */
-ms-interpolation-mode: bicubic; /* Scaled images look a bit better in IE now */
}
<input type="file" onchange="loadImage()"><br>
<div>
<h3>Image in Page 2</h3>
<img id="source_image" alt="Image preview..." />
</div>
<div>
<h3>Image In page 1</h3>
<div id="mapItem">
</div>
</div>
<div class="clear">
</div>
I'm using a framework that requires explicit pixels for a modal window that will be used to display messages to the end user at various points in the system. The messages will always be plain text so I want to base the width on the number of chars in the message. A message could be 2 words or 2 paragraphs.
At first I tried straight multiplication, which works great for content.length of 150-300 but anything <150 is too small and >300 gets way too large.
I need help with an algorithm that would effect a smaller number greater than it would effect a large number. I would like it to have the rough effect of:
getWidthForContent("small message") = 120; //13 chars
getWidthForContent("another reasonable length for a modal message") = 300; //45 chars
getWidthForContent("lorem ipsum...") = 900; //600+ chars
Another way you could look at this is trying to find a multiplier value from a curve. Here is a really sloppy approach:
determineBestWidth: function (contentLength) {
var width = 900; //max
if (contentLength < 200) {
width = contentLength * 2;
if (contentLength < 125) {
width = contentLength * 3;
if (contentLength < 50) {
width = contentLength * 5;
if (contentLength < 20) {
width = contentLength * 10;
if (contentLength < 10) {
width = contentLength * 20;
}
}
}
}
}
return width;
},
forgive me for my lack of math skills
You need to create a hidden div with the same content of your modal.
Then with some CSS tricks you will be able to do what you want (if I understood what you wanted) :
var modal = document.getElementById("modal");
var modalHidden = document.getElementById("modalHidden");
modalHidden.innerHTML = modal.innerHTML;
var height = (modalHidden.clientHeight + 1) + "px";
var width = (modalHidden.clientWidth + 1) + "px";
var result = document.getElementById("result");
result.innerHTML = "Height : " + height + "<br/>Width : " + width;
if (parseInt(width.substring(0, width.length - 2)) < 500)
{
modal.style.width = width;
}
#modal {
border: 1px solid darkblue;
border-radius: 4px;
text-align: justify;
background-color: #a4b8ff;
padding: 10px;
width: 500px;
word-wrap :break-word;
margin-bottom: 40px;
}
#modalHidden
{
visibility: hidden;
white-space: nowrap;
width: auto;
height: auto;
position: absolute;
}
#info {
color: grey;
margin-bottom: 40px;
}
<div id="info">Add some text and see that the width changes. Just use the variable in the JS and you're good.</div>
<div id="modal">
Some short text
</div>
<div id="modalHidden"></div>
<div id="result"></div>
I made a jsFiddle, if you wan to "play" with it.
Credits go to Bacon Ipsum.
I have a iframe that I create and add to the page as an overlay. I want it to be in the center of the page. However, when I am on mobile on a non mobile optimized page, and zoomed in, the iframe usually shows up off the screen.
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
width = w.innerWidth || e.clientWidth || g.clientWidth;
display_width = 450;
margin = (width-450) / 2;
frame.setAttribute('style','z-index: 2147483647;position:fixed;top:20px;left:'+margin+'px;width:'+display_width+'px;margin:0px;border: 1px solid; border-color:#ddd;max-width:none;overflow:visible;');
function resizeFrameWidth(){
var frame = document.getElementById('PennyPledge54DT');
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
width = w.innerWidth || e.clientWidth || g.clientWidth;
var margin = 20;
var display_width = width - 40;
if(!mobile){
display_width = 450;
margin = (width-450) / 2;
}
frame.style.width = display_width+"px";
frame.style.left = margin+"px";
}
if(window.attachEvent) {
window.attachEvent('onresize', resizeFrameWidth);
}
else if(window.addEventListener) {
window.addEventListener('resize', resizeFrameWidth, true);
It works when I resize the window, but not when I zoom.
edit:
here is a jsfiddle as requested. However, the problem manifests itself when you zoom in on the content. I only know how to do that via a mobile device. So I don't know how useful a fiddle will be.
https://jsfiddle.net/41y62su7/
edit
Here's a screenshot of what my iframe looks like on mobile when I zoom in on the page a little bit.
Here is a simpler method of achieving the same effect without grabbing the window.onresize event (which fires repeatedly throughout the resize event not just at the end). Using the heredoc function defined below allows for better maintainabilty and readability in your code. In this example I include the overlay which is not included in your javascript code.
This method uses margins to center the iframe within the overlay which is fixed in one position and set to the width and height of the viewport.
function heredoc(f) {
return f.toString().match(/\/\*\s*([\s\S]*?)\s*\*\//m)[1];
};
var overlay = document.createElement("div");
var iframe = document.createElement("iframe");
iframe.setAttribute("src", "https://www.google.ca");
overlay.style.cssText = heredoc(function() {/*
z-index: 2147483647;
position: fixed;
top: 0;
width: 100vw;
height: 100vw;
background: rgba(0,0,0,0.8);
*/});
iframe.style.cssText = heredoc(function() {/*
display: block;
margin-top:20px;
width: 450px;
max-width: 100vw;
height: 150px;
border: 1px solid #ddd;
overflow:visible;
margin: 0 auto;
margin-top: 20px;
*/});
overlay.appendChild(iframe);
document.getElementById('main').appendChild(overlay);
// you can also use
// document.body.appendChild(overlay);
// if you don't want to require that the user have an element with the id main
/* for example only, not required */
body {
width: 1000px;
height: 1000px;
}
<body>
<p id="main">
This is a bunch of text. Yadda yadda.
</p>
</body>