ScaleY a svg while followed by the below text divs - css

I need to change the height of a svg on mousewheel/scroll, but while this svg resizes, I need to make the text below follow this scaleY. I've tried implementing a position: relative but this does not work. How can I do that? Here's a PS of what I try to achieve.
Here's a PS of what I have now - images and text are just placeholder.
This is my relevant HTML code:
<div className="main_container">
<div className="main_container_inner">
<div className="img_container">
<img src={img_link} />
</div>
<div className="text_container">
<h1>fvdfv</h1>
<p>fvdfv</p>
</div>
</div>
</div>
This is my relevant CSS code:
.main_container{
display: block;
position: fixed;
width: 100vw;
}
body{
height: 400vh;
}
.main_container .main_container_inner{
display: inline-flex;
width: 33.33%;
flex-direction: column;
}
.main_container img{
width: 100%;
transform-origin: top;
}
This is what I have for now:
As you can see, when I change the scaleY value of the image, the text does not adjust to the new height.

The issue here is the flex containers do not have a setted height property.
So when you scale an image, it is quite hard to set its container height accordingly, so it "pushes" the other divs below. The image simply overflow its container.
I found a way anyway! Because I sticked (maybe for too long) on that weird challenge.
It requires a couple calculations.
I assumed you wish to scale up for the original height of the image to 100% of the viewport height. This assumption may be wrong... I this case, just play with those calculations. You have the example well commented below.
Another thing I noticed is you seem to use React. If so, I guess you should store the img height and its container height in the component state.
   I'm leaving that challenge to you ;)
// Get the elements
let main_container = document.querySelector(".main_container")
let imgContainer = document.querySelector(".img_container")
let img = document.querySelector(".img_container img")
// Some global variables to be filled when the image has fully loaded or on window resize
let viewportHeight, scrollHeight, scrollable, currentImgHeight, maxScale
// When the img has loaded...
function imgLoaded(){
// Get some dimentions
viewportHeight = window.innerHeight
scrollHeight = document.documentElement.scrollHeight
scrollable = scrollHeight-viewportHeight
// Calculate the maxScale for an img height == 100% of the viewport
currentImgHeight = img.getBoundingClientRect().height
maxScale = viewportHeight/currentImgHeight
// Set an initial height to the .img_container - Same as the img.
imgContainer.style.height = currentImgHeight+"px"
}
// If the image has already fully loaded (cache), else wait for the load event
if(img.complete){
imgLoaded()
}else{
img.addEventListener("load", imgLoaded)
}
// On window resize
window.onresize = function(){
img.style.transform = "scale(1)"
window.scrollTo(0,0)
imgLoaded()
}
// Scroll handler
function scrollHandler(){
// update the current img height value
currentImgHeight = img.getBoundingClientRect().height
// Calculate a new scale for the img -- The +1 is to avoid a negative value at the bottom of the page
let newScale = scrollable/(scrollable + 1 - document.documentElement.scrollTop)
// Apply the newScale if less or equal to maxScale
let scale = (newScale>maxScale)?maxScale:newScale
img.style.transform = `scaleY(${scale.toFixed(3)})`
// Adjust the img_container, so it pushes the div below
imgContainer.style.height = currentImgHeight+"px"
// Just for this demo...
console.log(imgContainer.style.height,scale.toFixed(3)+"% ")
}
// Add the event listener
document.addEventListener("scroll", scrollHandler)
/* Just for this demo... Styling the SO console which is in the way! */
.as-console{
background: transparent !important;
text-align: right;
}
.as-console-wrapper{
max-height: 1em !important;
border-top: 0 !important;
}
.as-console-row-code{
padding: 0 !important;
border: 0 !important;
}
/* added to your CSS */
*{
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* Your css unchanged */
.main_container {
display: block;
position: fixed;
width: 100vw;
}
body {
height: 400vh;
}
.main_container .main_container_inner {
display: inline-flex;
width: 33.33%;
flex-direction: column;
}
.main_container img {
width: 100%;
transform-origin: top;
}
<div class="main_container">
<div class="main_container_inner">
<div class="img_container">
<img src="https://via.placeholder.com/400" />
</div>
<div class="text_container">
<h1>fvdfv</h1>
<p>fvdfv</p>
</div>
</div>
</div>
Run in full screen mode! or CodePen

Related

How to scale a centered image in order to fill its parent? [duplicate]

I need to make this image stretch to the maximum size possible without overflowing it's <div> or skewing the image.
I can't predict the aspect-ratio of the image, so there's no way to know whether to use:
<img src="url" style="width: 100%;">
or
<img src="url" style="height: 100%;">
I can't use both (i.e. style="width: 100%; height: 100%;") because that will stretch the image to fit the <div>.
The <div> has a size set by percentage of the screen, which is also unpredictable.
Update 2016:
Modern browser behave much better. All you should need to do is to set the image width to 100% (demo)
.container img {
width: 100%;
}
Since you don't know the aspect ratio, you'll have to use some scripting. Here is how I would do it with jQuery (demo):
CSS
.container {
width: 40%;
height: 40%;
background: #444;
margin: 0 auto;
}
.container img.wide {
max-width: 100%;
max-height: 100%;
height: auto;
}
.container img.tall {
max-height: 100%;
max-width: 100%;
width: auto;
}​
HTML
<div class="container">
<img src="http://i48.tinypic.com/wrltuc.jpg" />
</div>
<br />
<br />
<div class="container">
<img src="http://i47.tinypic.com/i1bek8.jpg" />
</div>
Script
$(window).load(function(){
$('.container').find('img').each(function(){
var imgClass = (this.width/this.height > 1) ? 'wide' : 'tall';
$(this).addClass(imgClass);
})
})
There is a much easier way to do this using only CSS and HTML:
HTML:
<div
class="fill"
style="background-image: url('path/to/image.jpg');">
</div>
CSS:
.fill {
background-size: cover;
background-position: center;
width: 100%;
height: 100%;
}
This will place your image as the background, and stretch it to fit the div size without distortion.
Not a perfect solution, but this CSS might help. The zoom is what makes this code work, and the factor should theoretically be infinite to work ideally for small images - but 2, 4, or 8 works fine in most cases.
#myImage {
zoom: 2; //increase if you have very small images
display: block;
margin: auto;
height: auto;
max-height: 100%;
width: auto;
max-width: 100%;
}
If you're able to set the image as a background-image then you can do something like this, which will crop the image without stretching it:
<div style="background-image: url(...); background-size: cover; width: 100%; height: 100%;"></div>
If you need to stick with an <img> tag, then as of 2019, you can now use the object-fit css property that accepts the following values:
fill | contain | cover | none | scale-down
See https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
As an example, you could have a container that holds an image:
<div class="container">
<img src="" class="container_img" />
</div>
.container {
height: 50px;
width: 50%;
}
.container_img {
height: 100%;
width: 100%;
object-fit: cover;
}
If you can, use background images and set background-size: cover. This will make the background cover the whole element.
CSS
div {
background-image: url(path/to/your/image.png);
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: cover;
}
If you're stuck with using inline images there are a few options. First, there is
object-fit
This property acts on images, videos and other objects similar to background-size: cover.
CSS
img {
object-fit: cover;
}
Sadly, browser support is not that great with IE up to version 11 not supporting it at all. The next option uses jQuery
CSS + jQuery
HTML
<div>
<img src="image.png" class="cover-image">
</div>
CSS
div {
height: 8em;
width: 15em;
}
Custom jQuery plugin
(function ($) {
$.fn.coverImage = function(contain) {
this.each(function() {
var $this = $(this),
src = $this.get(0).src,
$wrapper = $this.parent();
if (contain) {
$wrapper.css({
'background': 'url(' + src + ') 50% 50%/contain no-repeat'
});
} else {
$wrapper.css({
'background': 'url(' + src + ') 50% 50%/cover no-repeat'
});
}
$this.remove();
});
return this;
};
})(jQuery);
Use the plugin like this
jQuery('.cover-image').coverImage();
It will take an image, set it as a background image on the image's wrapper element and remove the img tag from the document. Lastly you could use
Pure CSS
You might use this as a fallback. The image will scale up to cover it's container but it won't scale down.
CSS
div {
height: 8em;
width: 15em;
overflow: hidden;
}
div img {
min-height: 100%;
min-width: 100%;
width: auto;
height: auto;
max-width: none;
max-height: none;
display: block;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
Hope this might help somebody, happy coding!
Thanks to CSS3
img
{
object-fit: contain;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
IE and EDGE as always outsiders:
http://caniuse.com/#feat=object-fit
That's impossible with just HTML and CSS, or at least wildly exotic and complicated. If you're willing to throw some javascript in, here's a solution using jQuery:
$(function() {
$(window).resize(function() {
var $i = $('img#image_to_resize');
var $c = $img.parent();
var i_ar = $i.width() / $i.height(), c_ar = $c.width() / $c.height();
$i.width(i_ar > c_ar ? $c.width() : $c.height() * (i_ar));
});
$(window).resize();
});
That will resize the image so that it will always fit inside the parent element, regardless of it's size. And as it's binded to the $(window).resize() event, when user resizes the window, the image will adjust.
This does not try to center the image in the container, that would be possible but I guess that's not what you're after.
You can use object-fit: cover; on the parent div.
https://css-tricks.com/almanac/properties/o/object-fit/
Set width and height of the outer container div. Then use below styling on img:
.container img{
width:100%;
height:auto;
max-height:100%;
}
This will help you to keep an aspect ratio of your img
If you want to set a max width or height (so that it will not be very large) while keeping the images aspect-ratio, you can do this:
img{
object-fit: contain;
max-height: 70px;
}
I came across this question searching for a simular problem. I'm making a webpage with responsive design and the width of elements placed on the page is set to a percent of the screen width. The height is set with a vw value.
Since I'm adding posts with PHP and a database backend, pure CSS was out of the question. I did however find the jQuery/javascript solution a bit troblesome, so I came up with a neat (so I think myself at least) solution.
HTML (or php)
div.imgfill {
float: left;
position: relative;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: cover;
width: 33.333%;
height: 18vw;
border: 1px solid black; /*frame of the image*/
margin: -1px;
}
<div class="imgfill" style="background-image:url(source/image.jpg);">
This might be some info
</div>
<div class="imgfill" style="background-image:url(source/image2.jpg);">
This might be some info
</div>
<div class="imgfill" style="background-image:url(source/image3.jpg);">
This might be some info
</div>
By using style="" it's posible to have PHP update my page dynamically and the CSS-styling together with style="" will end up in a perfectly covered image, scaled to cover the dynamic div-tag.
To make this image stretch to the maximum size possible without overflowing it's or skewing the image.
Apply...
img {
object-fit: cover;
height: -webkit-fill-available;
}
styles to the image.
Using this method you can fill in your div with the image varying ratio of divs and images.
jQuery:
$(window).load(function(){
$('body').find(.fillme).each(function(){
var fillmeval = $(this).width()/$(this).height();
var imgval = $this.children('img').width()/$this.children('img').height();
var imgClass;
if(imgval > fillmeval){
imgClass = "stretchy";
}else{
imgClass = "stretchx";
}
$(this).children('img').addClass(imgClass);
});
});
HTML:
<div class="fillme">
<img src="../images/myimg.jpg" />
</div>
CSS:
.fillme{
overflow:hidden;
}
.fillme img.stretchx{
height:auto;
width:100%;
}
.fillme img.stretchy{
height:100%;
width:auto;
}
This did the trick for me
div img {
width: 100%;
min-height: 500px;
width: 100vw;
height: 100vh;
object-fit: cover;
}
if you working with IMG tag, it's easy.
I made this:
<style>
#pic{
height: 400px;
width: 400px;
}
#pic img{
height: 225px;
position: relative;
margin: 0 auto;
}
</style>
<div id="pic"><img src="images/menu.png"></div>
$(document).ready(function(){
$('#pic img').attr({ 'style':'height:25%; display:none; left:100px; top:100px;' })
)}
but i didn't find how to make it work with #pic { background:url(img/menu.png)}
Enyone?
Thanks
I had similar issue. I resolved it with just CSS.
Basically Object-fit: cover helps you achieve the task of maintaining the aspect ratio while positioning an image inside a div.
But the problem was Object-fit: cover was not working in IE and it was taking 100% width and 100% height and aspect ratio was distorted. In other words image zooming effect wasn't there which I was seeing in chrome.
The approach I took was to position the image inside the container with absolute and then place it right at the centre using the combination:
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
Once it is in the centre, I give to the image,
// For vertical blocks (i.e., where height is greater than width)
height: 100%;
width: auto;
// For Horizontal blocks (i.e., where width is greater than height)
height: auto;
width: 100%;
This makes the image get the effect of Object-fit:cover.
Here is a demonstration of the above logic.
https://jsfiddle.net/furqan_694/s3xLe1gp/
This logic works in all browsers.
HTML:
<style>
#foo, #bar{
width: 50px; /* use any width or height */
height: 50px;
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
}
</style>
<div id="foo" style="background-image: url('path/to/image1.png');">
<div id="bar" style="background-image: url('path/to/image2.png');">
JSFiddle
...And if you want to set or change the image (using #foo as an example):
jQuery:
$("#foo").css("background-image", "url('path/to/image.png')");
JavaScript:
document.getElementById("foo").style.backgroundImage = "url('path/to/image.png')";
Many of the solutions found here have some limitation: some not working in IE ( object-fit) or older browsers, other solutions do not scale up the images (only shrink it), many solution do not support resize of the window and many are not generic, either expect fix resolution or layout(portrait or landscape)
If using javascript and jquery is not a problem I have this solution based on the code of #Tatu Ulmanen. I fixed some issues, and added some code in case the image is loaded dinamically and not available at begining. Basically the idea is to have two different css rules and apply them when required: one when the limitation is the height, so we need to show black bars at the sides, and othe css rule when the limitation is the width, so we need to show black bars at the top/bottom.
function applyResizeCSS(){
var $i = $('img#imageToResize');
var $c = $i.parent();
var i_ar = Oriwidth / Oriheight, c_ar = $c.width() / $c.height();
if(i_ar > c_ar){
$i.css( "width","100%");
$i.css( "height","auto");
}else{
$i.css( "height","100%");
$i.css( "width","auto");
}
}
var Oriwidth,Oriheight;
$(function() {
$(window).resize(function() {
applyResizeCSS();
});
$("#slide").load(function(){
Oriwidth = this.width,
Oriheight = this.height;
applyResizeCSS();
});
$(window).resize();
});
For an HTML element like:
<img src="images/loading.gif" name="imageToResize" id="imageToResize"/>
try this
HTML:
<div class="container"></div>
CSS:
.container{
background-image: url("...");
background-size: 100%;
background-position: center;
}

How can I assign the value of a css calc() to a css-variable, and not have it delay the calculation until the css-variable is used

I have been unable to determine from any explanation I could find, about when the value of a css variable has been set., so I conducted a test
The html
<div class="flex">
<div class="item1">
<div class="included"></div>
</div>
<div class="item2">
<div class="included"></div>
</div>
</div>
the css
:host {
display: grid;
place-content: center;
}
.flex {
display: flex;
flex-direction: column;
width:800px;
height: 300px;
background-color: blue;
--div-width: calc(100% - 10px);
}
.item1, .item2 {
background-color: green;
height: 100px;
}
.item1 {
width: 80%;
}
.item2 {
width: 40%;
}
.included {
width: var(--div-width);
background-color:yellow;
height:50px;
}
Note I did the test inside a custom element, hence the :host descriptor, however that is not important.
What is important is that I set --div-width as calc(100% - 10px) inside a div that is 800px wide - so would expect it to have a value of 790px.
However , from this screenshot
It is obvious that the calculation is being delayed until the place where the variable is used. as the yellow boxes are just 10px short of the surrounding element.
Is there a way of telling css to set the value of the property as the element where it is declared, rather than where it is used.
I did try and proxy the variable - like this ...
--proxy-width: calc(100% - 10px);
--div-width: var(--proxy-width);
but it made no difference.
PS This is a trivial example, how I actually want to use it is to control the width of an item (a textarea) inside a custom element, dependent on the context, so I can make the element responsive to changes of the width of some outer container.
If I understand correctly, you want to make an element responsive to changes in size relative to a parent container. The way I've done this (but relative to the viewport) is by using relative length units like vmin along with calc() and CSS variables. This way, we can create a "responsive unit" whether its relative to the initial containing block or some other positioned ancestor.
The example below shows a <div> with nested <textarea> that is powered by responsive units. Since this width and height calculation is being done relative to the viewport even though the <textarea> is a child of the div. You could possibly swap vmin for a relative length unit which isn't relative to the viewport but relative to some ancestor like the custom element.
.wrapper {
border: 1px solid;
padding: 1rem;
}
textarea {
--w: 150;
--h: 80;
width: 200px; /* fallback width */
height: 200px; /* fallback height */
width: calc(var(--w) * 1vmin);
height: calc(var(--h) * 1vmin);
max-width: 100%;
/* extra styles for demo */
border: 1px solid red;
margin: 0 auto;
}
<div class="wrapper">
<textarea id="demo" name="demo"
rows="5" cols="30">It was a dark and stormy night...
</textarea>
</div>
I found a "perfect" work around!.
The solution to this is to use the 'resize' event to read the size of the element you wanted use 100% on using getBoundingClientRect(); and using the width returned to set the "calc" as width px - whatever
So for the example in the question I would use this code
_resize() {
if (this.resizeInProgress) return;
this.resizeInProgress = true;
requestAnimationFrame(() => {
const container = this.shadowRoot.querySelector('.flex');
const bound = container.getBoundingClientRect();
container.style.setProperty('--div-width', `calc(${bound.width}px - 10px)`);
this.resizeInProgress = false;
});
}
I bind that function to this in my custom element constructor
this.resizeInProgress = true;
this._resize = this._resize.bind(this);
and this in the connectedCallback do
window.addEventListener('resize', this._resize);
this.resizeInProgress = false;
and in my disconnectedCallback remove it again with
this.resizeInProgress = true;
window.removeEventListener('resize', this._resize);
I retain the calc() function because in my real life cases as the amount subtracted is in "em" units

How to insert an image in a DIV keeping the aspect ratio of the tags?

I made this code:
<div id="divImage" style="background-color: skyblue;height: 200px;">
<div style="background-color: red;width: 8%;height: 60px;margin-left: 20px; position: relative;top:50%;margin-right: auto;transform: translateY(-50%);"></div>
<div style="background-color: red;width: 8%;height: 60px;margin-right: 20px; position: relative;top:50%;margin-left: auto;transform: translateY(-150%);"></div>
</div>
I need to insert an image in the divImage with a width of 100%. For this, it is necessary that the divImage height is not fixed (height: 200px;). My intention is to make this divImage adapt to any screen size (in%).
You can force your box to keep an aspect ratio using its pseudo elements. Here the before element will always have a certain padding top which makes the box take up its height in relation to its width because the 100% in the equation always refers to the container's width.
.divImage {
&:before {
// Change this line to adjust the aspect ratio
// 1px / 1px will for example give you a square box.
// The first value refers to the height-part
padding-top: 5px / 10px * 100%;
content: "";
width: 1px;
margin-left: -1px;
float: left;
height: 0;
}
&:after { /* to clear float */
content: "";
display: table;
clear: both;
}
}
If you want to use an image in the div that covers the box add also this:
.divImage {
position: relative;
img {
position:absolute;
width: 100%;//might not be necessary, just check if it works without
height: 100%;//might not be necessary, just check if it works without
object-fit:cover; //or contain if you dont want it to fill the container
}
}
Using JavaScript, you will need to adjust the ratio on a per image basis and when the page resizes.
let ratio = 1.4;
const width = containerWidth.getBoundingClientRect().width;
const imageHeight = `${width * ratio}px`;

Resize <img> to fit available screen space in smallest dimension with pure CSS [duplicate]

I have made a div (div1), which is equal to the browser window size. Then I have made another div (div2) inside the parent div (div1). Then I have placed an image inside the second div (div2). My browser window size is 1360X638 and my image size is 1600*1200.
I want the image to fit itself according to the parent div's (div1) size. So the image (which is larger than the window size) have to fit itself to the second div (div2), which is equal to window size), and this image will fit exactly to the div's size (so, there isn't any scrolling or cropping of image in the display).
I have searched for some time. The solution I found is to set the maximum height and width to 100%. I did this.
I wrote this part:
<div style="max-width: 100%; max-height: 100%; background-color: red; margin-right: 0px; padding: 2 2 2 2; overflow:visible;">
<div style="max-height: 100%; max-width: 100%;">
<img style="max-width: 100%; max-height: 100%; overflow:visible;" src="1.jpg" />
</div>
</div>
The output is like this:
You can see there is a scroll in the right side. I don't want that there.
jQuery Solution - Proof of Concept
Suppose you have the following HTML:
<div class="container">
<img src="http://placehold.it/1600x1200" />
</div>
You can apply the following CSS rules to size an image to fit the view port (100% of browser width or height):
html, body {
height: 100%;
margin: 0;
}
.container {
height: 100%;
width: 100%;
background-color: red;
text-align: center; /* optional */
}
.container img {
vertical-align: top;
}
.portrait img {
width: 100%;
}
.landscape img {
height: 100%;
}
Use the following jQuery method to pick the correct CSS rule depending on the aspect ratio of the view port:
function resizeImg() {
var thisImg= $('.container');
var refH = thisImg.height();
var refW = thisImg.width();
var refRatio = refW/refH;
var imgH = thisImg.children("img").height();
var imgW = thisImg.children("img").width();
if ( (imgW/imgH) > refRatio ) {
thisImg.addClass("portrait");
thisImg.removeClass("landscape");
} else {
thisImg.addClass("landscape");
thisImg.removeClass("portrait");
}
}
$(document).ready(resizeImg())
$(window).resize(function(){
resizeImg();
});
Demo fiddle: http://jsfiddle.net/audetwebdesign/y2L3Q/
This may not be the entire answer but it may be a place to start.
Reference
I worked on a related problem earlier, which may be of interest:
Make image fill div completely without stretching

Keeping aspect ratio of image but cropping when hitting max/min height

I'm trying to make this image responsive that it will resize and keep it aspect ratio at all sizes. However above 650px, its height won't increase but will just crop the top and bottom to keep the ratio and not stretch. Also I want the image height won't go below 200px and just the left and right. I want image to always be center too. Here is what I have so far: https://jsfiddle.net/4q83mh41/
<div class="hero">
<img src="http://wpdevserver.x10host.com/wp-content/themes/wp/img/hero1.jpg">
</div>
.hero {
width: 100%;
min-height:200px;
max-height: 650px;
position: relative;
}
.hero img {
width: 100%;
height:auto;
min-height:200px;
overflow: hidden;
max-height: 650px;
position:aboslute;
}
Many thanks
By adding some javascript, the position of the image can be adjusted dynamically inside the .hero div. Additionally, CSS media queries can be used to keep the width of the image correct.
.hero {
width: 100%;
overflow: hidden;
max-height: 650px;
}
.hero img {
position: relative;
height: 200px;
width: auto;
}
#media (min-width:420px) {
.hero img {
width: 100%;
height: auto;
}
}
The javascript simply listens for resize events and adjusts the top and right properties of the relatively positioned image to keep it centered. Note that I'm using JQuery to manipulate the properties.
var heroresize = function() {
var aspect = 1800./858,
maxheight = 650,
minheight = 200,
width = $(".hero").width();
if(width < minheight*aspect) {
$(".hero img").css("right",(minheight*aspect - width)/2 + "px");
} else {
$(".hero img").css("right","0px");
}
if(width > maxheight*aspect) {
$(".hero img").css("top",(maxheight - width/aspect)/2 + "px");
} else {
$(".hero img").css("top","0px");
}
}
$(function(){
heroresize();
// remove if you don't need dynamic resizing
$(".hero").on("resize",heroresize);
});
The Javascript may cause some lag/stutter because resize events can be performance-intensive. If you don't need to worry about dynamic resizes, you can remove the indicated line.
I created a codepen you can play with here: http://codepen.io/regdoug/pen/azxVwd Note that the codepen has a max height of 350px because that allows you to see the effect without so much resizing.

Resources