Most elegant image to image frame comparison algorithm - javascript

I'm interested in finding the most elegant and 'correct' solution to this problem. I'm new to JS/PHP and I'm really excited to be working with it. The goal is to compare the size of a given image to the size of a 'frame' for the image (going to be a <div> or <li> eventually) and resize the image accordingly. Users will be uploading images of many sizes and shapes and there are going to be many sizes and shapes of frames. The goal is to make sure that any given image will be correctly resized to fit any given frame. Final implementation will be in PHP, but I'm working out the logic in JS. Here's what I have:
//predefined variables generated dynamically from other parts of script, these are example values showing the 'landscape in landscape' ratio issue
var $imageHeight = 150, $imageWidth = 310, $height = 240, $width = 300;
//check image orientation
if ( $imageHeight == $imageWidth ) {
var $imageType = 1; // square image
} else if ( $imageHeight > $imageWidth ) {
var $imageType = 2; // portrait image
} else {
var $imageType = 3; // landscape image
};
//check frame orientation and compare to image orientation
if ( $height == $width) { //square frame
if ( ( $imageType === 1 ) || ( $imageType === 3 ) ) {
$deferToHeight = true;
} else {
$deferToHeight = false;
};
} else if ( $height > $width ) { //portrait frame
if ( ( $imageType === 1 ) || ( $imageType === 3 ) ) {
$deferToHeight = true;
} else {
if (($imageHeight / $height) < 1) {
$deferToHeight = true;
} else {
$deferToHeight = false;
};
};
} else { //landscape frame
if ( ( $imageType === 1 ) || ( $imageType === 2 ) ) {
$deferToHeight = false;
} else {
if (($imageWidth / $width) > 1) {
$deferToHeight = true;
} else {
$deferToHeight = false;
};
};
};
//set values to match (null value scales proportionately)
if ($deferToHeight == true) { //defer image size to height of frame
$imageWidth = null;
$imageHeight = $height;
} else { //defer image size to width of frame
$imageWidth = $width;
$imageHeight = null;
};
I've tested it with a bunch of different values and it works, but I have a suspicion that I could have implemented a much more elegant solution. What could I have done better?

Here's how I dealt with this exact problem recently, in a JS implementation:
// determine aspect ratios
var a1 = imageWidth / imageHeight,
a2 = frameWidth / frameHeight,
widthDetermined = a1 > a2;
// set scale
var scale = widthDetermined ?
// scale determined by width
frameWidth / imageWidth :
// scale determined by height
frameHeight / imageHeight;
// set size (both dimensions) based on scale
imageWidth = scale * imageWidth;
imageHeight = scale * imageHeight;
Much less code than your version. Note that the square case is dealt with either way, which might be true in your version as well.

if the image is suppose a square image and you want to convert it into portrait, dont you think the image would look as if it was elongated? i feel the proportion would be changed and the image might look bad. what do u think about the same?

Related

iPhoneX and Notch detection

Using Javascript; how can I check if the users device is an iPhoneX?
Also, how can I determine what side the iPhones' 'notch' is positioned when in the landscape orientation?
There are some great articles out there: https://webkit.org/blog/7929/designing-websites-for-iphone-x/
... but these tend to take advantage of cutting-edge features that aren't natively supported in many mobile browsers at the time of writing this.
So I've come up with a method of detecting the iPhoneX with Javascript. My process also checks for the position of the Notch depending on the users device orientation:
https://codepen.io/marknotton/pen/NwpgBK
(function(window){
// Really basic check for the ios platform
// https://stackoverflow.com/questions/9038625/detect-if-device-is-ios
var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
// Get the device pixel ratio
var ratio = window.devicePixelRatio || 1;
// Define the users device screen dimensions
var screen = {
width : window.screen.width * ratio,
height : window.screen.height * ratio
};
// iPhone X Detection
if (iOS && screen.width == 1125 && screen.height === 2436) {
// Set a global variable now we've determined the iPhoneX is true
window.iphoneX = true;
// Adds a listener for ios devices that checks for orientation changes.
window.addEventListener('orientationchange', update);
update();
}
// Each time the device orientation changes, run this update function
function update() {
notch();
iphoneXChecker();
}
// Notch position checker
function notch() {
var _notch = '';
if( 'orientation' in window ) {
// Mobile
if (window.orientation == 90) {
_notch = 'left';
} else if (window.orientation == -90) {
_notch = 'right';
}
} else if ( 'orientation' in window.screen ) {
// Webkit
if( screen.orientation.type === 'landscape-primary') {
_notch = 'left';
} else if( screen.orientation.type === 'landscape-secondary') {
_notch = 'right';
}
} else if( 'mozOrientation' in window.screen ) {
// Firefox
if( screen.mozOrientation === 'landscape-primary') {
_notch = 'left';
} else if( screen.mozOrientation === 'landscape-secondary') {
_notch = 'right';
}
}
window.notch = _notch;
}
})(window);
// Bespoke functions:
// The above functions have no jQuery Dependencies.
// The below code uses jQuery solely for this quick demo.
if ( window.iphoneX === true ) {
$('body').addClass('iphoneX');
}
function iphoneXChecker() {
if (window.notch == 'left') {
$('body').removeClass('notch-right').addClass('notch-left');
} else if (window.notch == 'right') {
$('body').removeClass('notch-left').addClass('notch-right');
} else {
$('body').removeClass('notch-right notch-left');
}
}
I can't help but feel like this is just a combination of little hacks. As you'll probably notice; my Javascript isn't exactly to a high standard and I'm sure there are better/cleaner ways to do this.
I'd be very happy to receive feedback and solutions to issues I've not considered.
If you just want to check for the iPhoneX (ignoring the Notch), this should do the job:
https://codepen.io/marknotton/pen/MOpodJ
(function(){
// Really basic check for the ios platform
// https://stackoverflow.com/questions/9038625/detect-if-device-is-ios
var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
// Get the device pixel ratio
var ratio = window.devicePixelRatio || 1;
// Define the users device screen dimensions
var screen = {
width : window.screen.width * ratio,
height : window.screen.height * ratio
};
// iPhone X Detection
if (iOS && screen.width == 1125 && screen.height === 2436) {
alert('iPhoneX Detected!');
}
})();
iPhone X and 11 have 9:19.5 aspect ratio:
9 / 19.5 = 0.4615384615.toFixed(3) = "0.462"
Let's try this on all iPhone X and 11 using window.screen.
X, Xs, Xs Max (Display Zoom: Zoomed), 11 Pro, 11 Pro Max (Display Zoom: Zoomed):
375 / 812 = 0.4618226601.toFixed(3) = "0.462"
Xs Max (Display Zoom: Standard), XR, 11, 11 Pro Max (Display Zoom: Standard):
414 / 896 = 0.4620535714.toFixed(3) = "0.462"
So...
let iPhone = /iPhone/.test(navigator.userAgent) && !window.MSStream
let aspect = window.screen.width / window.screen.height
if (iPhone && aspect.toFixed(3) === "0.462") {
// I'm an iPhone X or 11...
}
Please keep in mind, window.screen always return sizes in portrait, regardless active device orientation.

Rerun javascript function when screen orientation changes

I'm using a code that scale images to fit the parent div, it's called "aspectcorrect".
The problem happens on the mobile version: my parent div has 100% width, and when the user changes the orientation of the screen to landscape, the image doesn't resize to fit the new div's width.
There is a way to rerun the onload event (which scales the image), when the user changes the orientation of the screen?
Here is my website: www.mcsoftware.com.br/sitemc
I'm still working on it.
(To understand what I'm saying, open it on your cellphone, and when you change the screen orientation just click on "Continuar mesmo assim" to navigate)
Thanks!
aspectcorrect.js
function ScaleImage(srcwidth, srcheight, targetwidth, targetheight, fLetterBox) {
var result = { width: 0, height: 0, fScaleToTargetWidth: true };
if ((srcwidth <= 0) || (srcheight <= 0) || (targetwidth <= 0) || (targetheight <= 0)) {
return result;
}
// scale to the target width
var scaleX1 = targetwidth;
var scaleY1 = (srcheight * targetwidth) / srcwidth;
// scale to the target height
var scaleX2 = (srcwidth * targetheight) / srcheight;
var scaleY2 = targetheight;
// now figure out which one we should use
var fScaleOnWidth = (scaleX2 > targetwidth);
if (fScaleOnWidth) {
fScaleOnWidth = fLetterBox;
}
else {
fScaleOnWidth = !fLetterBox;
}
if (fScaleOnWidth) {
result.width = Math.floor(scaleX1);
result.height = Math.floor(scaleY1);
result.fScaleToTargetWidth = true;
}
else {
result.width = Math.floor(scaleX2);
result.height = Math.floor(scaleY2);
result.fScaleToTargetWidth = false;
}
result.targetleft = Math.floor((targetwidth - result.width) / 2);
result.targettop = Math.floor((targetheight - result.height) / 2);
return result;
}
onimageload.js
function OnImageLoad(evt) {
var img = evt.currentTarget;
// what's the size of this image and it's parent
var w = $(img).width();
var h = $(img).height();
var tw = $(img).parent().width();
var th = $(img).parent().height();
// compute the new size and offsets
var result = ScaleImage(w, h, tw, th, false);
// adjust the image coordinates and size
img.width = result.width;
img.height = result.height;
$(img).css("left", result.targetleft);
$(img).css("top", result.targettop);
}
Where onload function goes
<img onload="OnImageLoad(event);" />
https://jsfiddle.net/ffxeqq21/
You can try some javascript library like jQuery mobile and use the orientationchange event This way you could just do
$( window ).on( "orientationchange", function( event ) {
//Some code
});

Dynamically Resizing Image Maps?

Hope you can help with this problem I've been trying to nut out.
I've found the examples on http://home.comcast.net/~urbanjost/semaphore.html very awesome and work perfectly for what I need.
Only problem is that I'd like the coordinates to dynamically change based on the window size first. The way it works at the moment is that it loads the default coords (works great for resolutions of 1920x1080 but is hugely unaligned on 1024x768) and will then resize on window resize.. I'd like it to detect the size of the browser window for smaller screens first, then display the code accordingly.
Here's my javascript:
<script type="text/javascript" >
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| |||||||||||||||||||||||||||||||||||||||||
GLOBAL_AREAS= new Array();
GLOBAL_SUFFIX= "g";
GLOBAL_WIDTH=-1;
GLOBAL_HEIGHT=-1;
GLOBAL_NEW_AREAS= new Array();
//---------------------------------------------------------------------------
function setglobal(){
// place original AREA coordinate strings into a global array, called first time setXY is called
var arrayAreas = document.body.getElementsByTagName("AREA" );
GLOBAL_WIDTH= document.getElementById("tclteam_s1" ).width; // get original width
GLOBAL_HEIGHT= document.getElementById("tclteam_s1" ).height; // get original height
for(var i = 0; i < arrayAreas.length; i++) {
GLOBAL_AREAS[i]= arrayAreas[i].coords;
}
document.body.onresize=setXY('tclteam_s1',XSIZE(),YSIZE());
// alert("GLOBAL_AREAS" + GLOBAL_AREAS );
}
//---------------------------------------------------------------------------
function setXY(elementid,newwidth,newheight){
if (GLOBAL_WIDTH == -1 ){
setglobal();
}
document.getElementById(elementid).width=newwidth;
document.getElementById(elementid).height=newheight;
scaleArea();
}
//---------------------------------------------------------------------------
function XSIZE(){ // get browser window.innerWidth , dealing with ie
var myWidth = 1;
if( typeof( window.innerWidth ) == 'number' ) {
//Non-IE
myWidth = window.innerWidth;
} else if( document.documentElement && ( document.documentElement.clientWidth ) ) {
//IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
} else if( document.body && ( document.body.clientWidth ) ) {
//IE 4 compatible
myWidth = document.body.clientWidth;
}
return myWidth;
}
//---------------------------------------------------------------------------
function YSIZE(){ // get browser window.innerHeight, dealing with ie
var myHeight = 1;
if( typeof( window.innerHeight ) == 'number' ) {
//Non-IE
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myHeight = document.documentElement.clientHeight;
} else if( document.body && ( document.body.clientHeight ) ) {
//IE 4 compatible
myHeight = document.body.clientHeight;
}
return myHeight;
}
//---------------------------------------------------------------------------
function scaleArea() { // using values stored at load, recalculate new values for the current size
var arrayAreas = document.body.getElementsByTagName("AREA" );
message = " "
for(var i = 0; i < arrayAreas.length; i++) {
ii=i+1;
rescaleX= document.getElementById("tclteam_s1" ).width/GLOBAL_WIDTH ;
rescaleY= document.getElementById("tclteam_s1" ).height/GLOBAL_HEIGHT ;
sarray=GLOBAL_AREAS[i].split("," ); // convert coordinates to a numeric array assuming comma-delimited values
var rarray =new Array();
for(var j = 0; j < sarray.length; j += 2) {
rarray[j]=parseInt(sarray[j])*rescaleX; // rescale the values
rarray[j]=Math.round(rarray[j]);
rarray[j+1]=parseInt(sarray[j+1])*rescaleY; // rescale the values
rarray[j+1]=Math.round(rarray[j+1]);
}
message = message + rarray.join("," ) + '\n';
arrayAreas[i].coords=rarray.join("," ); // put the values back into a string
GLOBAL_NEW_AREAS[i]= arrayAreas[i].coords;
}
// alert(rescaleX + " " + rescaleY + "\n" + GLOBAL_WIDTH + " " + GLOBAL_HEIGHT + "\n" + " GLOBAL_AREAS" + GLOBAL_AREAS + "\nSCALED AREAS" + message);
}
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</script>
The following script here detects the browser window size. So I'm hoping to include this in the above so the image map will dynamically resize from the browser window size first:
<script type="text/javascript">
var winW = 630, winH = 460;
if (document.body && document.body.offsetWidth) {
winW = document.body.offsetWidth;
winH = document.body.offsetHeight;
}
if (document.compatMode=='CSS1Compat' &&
document.documentElement &&
document.documentElement.offsetWidth ) {
winW = document.documentElement.offsetWidth;
winH = document.documentElement.offsetHeight;
}
if (window.innerWidth && window.innerHeight) {
winW = window.innerWidth;
winH = window.innerHeight;
}
</script>
Is there a way to code this so it will read the browser window size first (using the code directly above), then load the image map accordingly?

Detect Phone orientation on load

Is this possible to check with Javascript on page load. I know you can detect a change in orientation but is it possible to check what state the phone is in straight away?
Thanks
You could check the viewport width and height and see which one is larger:
var isLandscapeMode = window.innerWidth > window.innerHeight;
if (isLandscapeMode) {
// fit page to wide orientation here...
}
For a more in-depth discussion of viewport sizes on mobile devices, see
http://www.quirksmode.org/mobile/viewports2.html
(Updated code)
You can do this...
var currOrientation= '';
/************************
* Changes the (obj) element's class, to
* -vrt (vertical)
* -hrz (horizontal)
* depending on its width
*************************/
var orientation = function( obj ){
var w = getWidth(),
h = getHeight();
if( w <= h && currOrientation !== 'vrt' ){
obj.className = 'vrt';
currOrientation = 'vrt';
}
else if( currOrientation !== 'hrz' ) {
obj.className = 'hrz';
currOrientation = 'hrz';
}
}
/************************
* Binding a repeating timer
************************/
var orientationINIT = function() {
var content;
if( (content = document.getElementsByTagName('body')[0]) != undefined ){
orientation( content );
var Orient = setInterval( function() {
orientation( content );
}, 500 ); //each 500ms a call for orientation change
}
}
orientationINIT should be called on page load or DOM complete (or at the bottom of the page )
where getHeight() and getWidth() should be simple functions that return the window's width / height in number form.

Check if element is visible on screen [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
jQuery - Check if element is visible after scroling
I'm trying to determine if an element is visible on screen. In order to to this, I'm trying to find the element's vertical position using offsetTop, but the value returned is not correct. In this case, the element is not visible unless you scroll down. But despite of this, offsetTop returns a value of 618 when my screen height is 703, so according to offsetTop the element should be visible.
The code I'm using looks like this:
function posY(obj)
{
var curtop = 0;
if( obj.offsetParent )
{
while(1)
{
curtop += obj.offsetTop;
if( !obj.offsetParent )
{
break;
}
obj = obj.offsetParent;
}
} else if( obj.y )
{
curtop += obj.y;
}
return curtop;
}
Thank you in advance!
--- Shameless plug ---
I have added this function to a library I created
vanillajs-browser-helpers: https://github.com/Tokimon/vanillajs-browser-helpers/blob/master/inView.js
-------------------------------
Intersection Observer
In modern browsers you can use the IntersectionObserver which detects where an element is on the screen or compared to a parent.
The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.
Today I would probably lean toward this API if I need to detect and react to when an element has entered or exited the screen.
But for a quick test/lookup when you just want to verify if an emelemt is currently on screen I would go with the version just below using the getBoundingClientRect.
Using getBoundingClientRect
Short version
This is a lot shorter and should do it as well:
function checkVisible(elm) {
var rect = elm.getBoundingClientRect();
var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
}
with a fiddle to prove it: http://jsfiddle.net/t2L274ty/1/
Longer version
And a version with threshold and mode included:
function checkVisible(elm, threshold, mode) {
threshold = threshold || 0;
mode = mode || 'visible';
var rect = elm.getBoundingClientRect();
var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
var above = rect.bottom - threshold < 0;
var below = rect.top - viewHeight + threshold >= 0;
return mode === 'above' ? above : (mode === 'below' ? below : !above && !below);
}
and with a fiddle to prove it: http://jsfiddle.net/t2L274ty/2/
A more traditional way to do it
As BenM stated, you need to detect the height of the viewport + the scroll position to match up with your top position. The function you are using is ok and does the job, though its a bit more complex than it needs to be.
If you don't use jQuery then the script would be something like this:
function posY(elm) {
var test = elm, top = 0;
while(!!test && test.tagName.toLowerCase() !== "body") {
top += test.offsetTop;
test = test.offsetParent;
}
return top;
}
function viewPortHeight() {
var de = document.documentElement;
if(!!window.innerWidth)
{ return window.innerHeight; }
else if( de && !isNaN(de.clientHeight) )
{ return de.clientHeight; }
return 0;
}
function scrollY() {
if( window.pageYOffset ) { return window.pageYOffset; }
return Math.max(document.documentElement.scrollTop, document.body.scrollTop);
}
function checkvisible( elm ) {
var vpH = viewPortHeight(), // Viewport Height
st = scrollY(), // Scroll Top
y = posY(elm);
return (y > (vpH + st));
}
Using jQuery is a lot easier:
function checkVisible( elm, evalType ) {
evalType = evalType || "visible";
var vpH = $(window).height(), // Viewport Height
st = $(window).scrollTop(), // Scroll Top
y = $(elm).offset().top,
elementHeight = $(elm).height();
if (evalType === "visible") return ((y < (vpH + st)) && (y > (st - elementHeight)));
if (evalType === "above") return ((y < (vpH + st)));
}
This even offers a second parameter. With "visible" (or no second parameter) it strictly checks whether an element is on screen. If it is set to "above" it will return true when the element in question is on or above the screen.
See in action: http://jsfiddle.net/RJX5N/2/
I hope this answers your question.
Could you use jQuery, since it's cross-browser compatible?
function isOnScreen(element)
{
var curPos = element.offset();
var curTop = curPos.top;
var screenHeight = $(window).height();
return (curTop > screenHeight) ? false : true;
}
And then call the function using something like:
if(isOnScreen($('#myDivId'))) { /* Code here... */ };

Categories