Preview and zoom image like Firefox - javascript

When you open an image in Firefox, it has the following characteristics:
Displayed centered in the browser.
Will have a height/width contained (does not exceed browser dimensions).
Does not resize larger than its max size.
Does not resize at all if its smaller than the browser window.
Retains aspect ratio.
Has a zoom tool which resizes the image to its max size and allows it to exceed browser dimensions.
How would I replicate this using CSS/JS? I've tried several different methods using CSS but I'm assuming it requires JS and I can't find any examples.
The best results I've achieved using contain:
height: 100%;
width: 100%;
background-url: {location of image}
background-position: center center;
background-repeat: no-repeat;
background-size: contain;
But this only produces nice results with larger images, as it stretches smaller images to the match the browser height or width whereby instead I want images smaller than the browser to just be centered and retain their max height/width.

jsBin demo
(Disclaimer: Modern browsers only: (IE9+))
all you need is:
<div id="parent">
<img src="image.jpg">
</div>
CSS:
html,
body{
margin:0;
height:100%;
background:url(http://i.imgur.com/aSqDLP0.png);
}
body{ /* or use a wrapper element instead */
display: table;
width: 100%;
text-align: center;
}
#parent{
display: table-cell;
vertical-align: middle;
}
#parent img{
vertical-align: middle;
max-height: 100vh;
max-width: 100vw;
box-shadow: 0 4px 15px #111;
}
The above is enough for the centering and resize stuff.
If you want to additionally do exactly like Firefox does:
zoom-in cursor on hover - if image is resized by the browser (small)
scroll the window to the clicked coordinates - if image is small
zoom-out cursor - if image is zoomed (clicked) - show the -
than a bit of jQuery might come handy:
var $parent = $("#parent"),
isParentSmaller = false,
zoom = false,
parW, parH,
imgW, imgH;
$parent.find("img").on("mouseenter click", function( e ){
imgW = this.naturalWidth;
imgH = this.naturalHeight;
parW = $parent.width();
parH = $parent.height();
isParentSmaller = parW-1 < imgW || parH-1 < imgH;
$(this).css({
cursor: isParentSmaller ? (zoom?"zoom-out":"zoom-in") : "auto"
});
if(e.type=="click" && isParentSmaller){
zoom = !zoom; // Toggle zoom boolean
$(this).css({ // Apply cursor styles
maxWidth : zoom ? "none" : "100vw",
maxHeight : zoom ? "none" : "100vh",
cursor : zoom ? "zoom-out":"zoom-in"
});
// Scroll window where we want to zoom:
window.scrollTo(((imgW/parW)-1)*e.clientX, ((imgH/parH)-1)*e.clientY);
}
});
Modesty time, the above performs even better than Firefox, since Firefox looses the Magnifying glass cursor if you resize the window :)

Note: I re-wrote my answer because my previous solution wouldn't work in Firefox (oh, the irony). Also it caused strange behaviours in other browsers. Reason was flexbox to center the image both vertically and horizontally.
Let's do this step by step.
For keeping the aspect ratio of an image while setting maximum dimensions can be achieved with this:
.img {
display: block; // could also be inline-block or other block-like types
max-height: 100%;
max-width: 100%;
height: auto;
width: auto;
}
Now, centering an element both vertically and horizontally are technically 3 lines of code with flexbox. As mentioned above, this caused strange behaviours when scaling the images in some browsers. Instead we use text-align: center to center the image along the x-axis and a method with something called a "Ghost Element" to center the image along the y-axis. You can learn more about it in this article from CSS Tricks. In summary, we have this to center the element:
.parent {
text-align: center;
white-space: nowrap;
}
.parent:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
margin-right: -0.25em;
}
.centered-child {
display: inline-block;
vertical-align: middle;
}
At last, we combine the scaling and centering. I assume the HTML only exists of a single <img class="img" ...> in the body.
html {
width: 100%;
height: 100%;
}
body {
margin: 0;
width: 100%;
height: 100%;
background-color: #333;
text-align: center;
}
body:before {
content: '';
width: 0;
height: 100%;
display: inline-block;
vertical-align: middle;
white-space: nowrap;
margin-left: -0.25em;
}
.img {
display: inline-block;
vertical-align: middle;
max-height: 100%;
max-width: 100%;
width: auto;
height: auto;
}
Now we implement the zooming
In order to zoom the image we need JavaScript. Let's use jQuery.
It's not nice to change css attributes in JavaScript, so we prepare two extra classes.
.img.is-zoomable {
cursor: zoom-in;
}
.img.is-zoomed {
cursor: zoom-out;
max-height: none;
max-width: none;
}
On click JavaScript will toggle the class is-zoomed and on mouseenter we decide if the image can be zoomed. If it can be zoomed, we add the class is-zoomable.
$('.img').on('click', function() {
$(this).toggleClass('is-zoomed');
});
$('.img').on('mouseenter', function() {
var origWidth = this.naturalWidth;
var origHeight = this.naturalHeight;
var currWidth = $(this).width();
var currHeight = $(this).height();
if (origWidth !== currWidth || origHeight !== currHeight) {
$(this).addClass('is-zoomable');
}
});
Et voilà, we're done. See my codepen for a working example.

Related

How to combine dynamic Javascript div height resizing with in-page anchor hyperlinks?

Ok, this is an unusual question, but I've been playing around with it for hours and haven't had any progress - hoping the SO community can help! 🤓
Here's the webpage in question: https://pifornerds.io/
We're using this code to dynamically set the background (the digits of pi that you see in grey) height based on the foreground content length:
<script>
// Dynamically set base height based on container contents
var containerHeight = document.getElementById("container").offsetHeight;
var baseHeight = document.getElementById("base").offsetHeight;
if (baseHeight > containerHeight) {
document.getElementById("base").style.height = containerHeight + "px";
}
</script>
CSS snippet:
.layered {
display: grid;
justify-items: left;
background-color: #222222;
max-width: 1000px;
margin: auto;
height: auto;
min-height: 100px;
overflow: hidden;
}
.layered > * {
grid-column-start: 1;
grid-row-start: 1;
}
.base {
font-family: "DejaVuSansMonoBold", courier;
color: white;
opacity: 0.1;
max-width: 100%;
height: 100%;
word-break: break-all;
margin-top: -3px;
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
position: static;
z-index: 1;
overflow: hidden;
}
.container {
display: block;
height: max-content;
overflow: hidden;
}
We wanted to do this so there wouldn't be a bunch of pi digits in the footer area. We think it looks "cleaner" this way.
However, the menu at the top uses anchor links to jump to content lower on the page, for example:
Roadmap
jumps to:
<a id="roadmap"></a>
Now here's the problem ...
After you click a menu anchor link, and then try to scroll back up the page to the top, you can't 🧐 ... because somehow the previous Javascript height update won't allow that.
This is as far up as I can scroll up now, after clicking "Roadmap":
Any ideas? Thanks in advance! 😀
EDIT: Here's a Fiddle: https://jsfiddle.net/imkane/kof83msy/4/
Of course it works properly there 😖
You need to remove the overflow: hidden; from .layered div.
It will probably be better to add overflow: hidden; to .base div, which is the one you set the height on.

Add scrolling to Lightbox2

Heres an image of the issue I'm trying to resolve. I am working on my portfolio site; and I have images of some of my personal projects, all of them are the same width but some have different heights. Due to getting full page screenshots of my work, some of the images have a much greater height than others. Instead of allowing displaying all the images the same size and allowing scrolling in the modal window, it scales the images down to fit within the same height as all the others. This gives it an odd look cause some of the images get scaled down a lot. I would like to get all the images to display in the same width, and those that need it to allow scrolling to see the rest of the image. I tried to use overflow: scroll; on the .lightbox but that didn't help. I've also tried overflow-y. I would also like to disable the page in the background from being able to scroll, to allow the scrolling to be focused on the images that it is necessary on.
.lightbox {
position: absolute;
left: 0;
width: 100%;
z-index: 10000;
text-align: center;
line-height: 0;
font-weight: normal;
}
.lightbox .lb-image {
display: block;
min-width: 100%;
height: auto;
border-radius: 3px;
/* Image border */
border: 4px solid white;
}
.lightbox a img {
border: none;
}
.lb-outerContainer {
position: relative;
*zoom: 1;
width: 250px;
min-height: 250px;
margin: 0 auto;
border-radius: 4px;
/* Background color behind image.
This is visible during transitions. */
background-color: white;
}
Lightbox2 by default appends calculated width & height to the image and .lb-outerContainer. But you can override this by doing the following -
.lb-outerContainer {
width: 100% !important;
height: auto !important;
}
.lightbox .lb-image {
width: 100% !important;
height: auto !important;
}
I don't recommend this because this breaks the intended use of this plugin. I'm sure you'll find an alternative to lightbox2 that achieves what you're looking for. So you can consider this as a temporary fix.
EDIT: Here's a jsfiddle to see it work. https://jsfiddle.net/hsugx6wm/43/

object-fit alternative for ie 11

I have a image in a div. The image is set to fill the screen and scale from center with the following css
.backdiv img{
height:100%;
width: 100%;
display: block;
object-fit:cover;
}
Everything works fine on chrome/safari/firefox. On IE 11 the aspect ratio of the image is not maintained as the image is being forced to be 100% width and 100% and the object-fit is ignored since ie 11 does not support it.
How can i achieve the same on ie 11
you can do away with the img element and style the background style of the containing div
<div id="divbackground" style="position:absolute;top:0;left:0;bottom:0;right:0;background-image:url(myimage.png);background-size:fill">
To make an img element render with its 'natural' aspect ratio, just specify its width or height style to 100%, not both... eg.
This is old question, but I have had the exact same annoying issue where everything worked fine for Chrome/Edge but some css properties did not work in IE11,
I ended up using HTML "figure" element which solved all my problems.
The below code forces the image to reduce nicely(without changing the original aspect ratio).
<figure class="figure-class">
<img class="image-class" src="{{photoURL}}" />
</figure>
and css classes:
.image-class {
border: 6px solid #E8E8E8;
max-width: 189px;
max-height: 189px;
}
.figure-class {
width: 189px;
height: 189px;
}
Recently got the same issue and here is my solution:
.backdiv {
position: relative;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.backdiv img {
position: absolute;
width: auto;
height: auto;
min-width: 100%;
min-height: 100%;
}
It looks exactly like object-fit: cover;. Image is centered by using flex layout, you can change image position by changing justify-content and align-items
if ('objectFit' in document.documentElement.style === false) {
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll("img").forEach(function (image) {
if (image.currentStyle['object-position'] || image.currentStyle['object-fit']) {
(image.runtimeStyle || image.style).background = "url(\"".concat(image.src, "\") no-repeat ");
if (image.currentStyle['object-fit'])
(image.runtimeStyle || image.style).backgroundSize = image.currentStyle['object-fit'];
if (image.currentStyle['object-position'])
(image.runtimeStyle || image.style).backgroundPosition = image.currentStyle['object-position'];
image.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='".concat(image.width, "' height='").concat(image.height, "'%3E%3C/svg%3E");
}
});
});
}
here is a work around to find all the images that have object-fit and object-position for ie

Resize child div element to fit in parent div on window resize

I have a div element (shown with red border in the image below), which I want to be able to fit in its parent div when the window is resized and not fall into the next line (the parent is the one with the green border).
I want the red div to have a starting width: 949px (in my current screen) in order to fit the entire space available as shown in the image, but be resizable, so that it doesn't fall into the next line if width: 949px is to much to fit.
In essence, I want it at all costs to cover the area it covers in the image even if in a narrower screen that means it will be like 10px wide.
How can I achieve this? Any solution using CSS, JavaScript or jQuery will be gladly accepted.
The image:
CSS:
#parent {
text-align: center;
width: 90%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: inline-block;
}
#child1-row2 {
text-align: left;
vertical-align: middle;
width: 288px;
display: inline-block;
}
#child2-row2 {
text-align: left;
vertical-align: middle;
width: 288px;
position: relative;
margin: 0 25px 0 25px;
display: inline-block;
}
#child3-row2 {/* The one with the red border */
vertical-align: middle;
height: 452px;
width: 949px;
overflow: hidden;
position: relative;
display: inline-block;
}
You can use flexbox to do this by using the flex-grow property.
HTML :
<div id="main">
<div id="box1">1</div>
<div id="box2">2</div>
<div id="box3">3</div>
</div>
CSS :
#main {
display: flex;
flex-flow: row;
width:100%;
min-height:50px;
}
#box1{
background-color:red;
width:100px;
}
#box2{
background-color:blue;
width:100px;
}
#box3{
background-color:green;
flex-grow: 1;
}
Here is a working JSFiddle
You can use css calc function for this. Support for calc seems to be quite good now.
As you have mentioned, the left side divs are of fixed width, say 120px each. Also suppose the margin between them is 30px. So, the total width left for your red div is 100% - (2*120 + 2*30)px i.e. (100% - 300px ).
#red-div
{
width: calc(100% - 300px);
}
Add % width or you can do following :
$(window).resize(function() {
var window_width = $(window).width();
var w1_width = $('.div1').width(); // The first element width
var w2_width = $('.div2').width(); // The second element width
var main_div_width = window_width - (w1_width+w2_width+gutter[i.e margin between all 3 elements]);
$('.main_div_width').css('width', main_div_width);
});

Fullscreen video without black borders

The problem I have is that the video always gets black bars on the sides or on the top/bottom depending on the screen size.
Any idea how to get it full screen always without showing that annoying black bars? and without using a plugin.
This is my markup:
<div id="full-bg">
<div class="box iframe-box" width="1280" height="800">
<iframe src="http://player.vimeo.com/video/67794477?title=0&byline=0&portrait=0&color=0fb0d4" width="1280" height="720" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</div>
</div>
#full-bg{
width: 100%;
height: 100%;
img{
display: none;
}
.iframe-box{
width: 100%;
height: 100%;
position: absolute;
background: url(../img/fittobox.png);
left: 0 !important;
top: 0 !important;
iframe{
width: 100%;
height: 100%;
}
}
}
Try adding to your CSS
.iframe-box {
max-width: 1280px; /* video width */
max-height: 720px; /* video height */
}
This means that width: 100%; height: 100% will let the element will expand as much as it can, until it hits a maximum height or width of 720px or 1280px, respectively.
If the screen you're viewing it on has a greater resolution, the node will stop expanding and you'll not have black borders.
Further, afaik the following is not valid CSS, are you using a library or something?
#full-bg {
.iframe-box {
foo: bar;
}
}
Edit after answer accepted: I just thought of a completely different way to achieve this, but it would require you to change a lot of your CSS
.fittobox { /* give fit to box an aspect ratio */
display: inline-block; /* let it be styled thusly */
padding: 0; /* get rid of pre-styling */
margin: 0;
width: 100%; /* take up full width available */
padding-top: 56.25%; /* give aspect ratio of 16:9; "720 / 1280 = 0.5625" */
height: 0px; /* don't want it to expand beyond padding */
position: relative; /* allow for absolute positioning of child elements */
}
.fittobox > iframe {
position: absolute; /* expand to fill */
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
}
If you know the aspect ratio of your video, you shouldn't even need Javascript. You can use a percentage-based padding-top.
I could post code, but I'd recommend you read this entire article anyway.
#Paul S. 's answer works for me for the .fittobox container for 16:9 video aspect ratios but the .fittobox > iframe embed still has black bars with his CSS. Removing the right and bottom positioning fixes it for me (no need for "px" in those 0 values, of course):
.fittobox > iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

Categories

Resources