Fixed aspect ratio box with letter and pillarboxing between header and footer - javascript

Running the following code snippet will provide a framework for what I am visually hoping to accomplish, with some concessions made in the CSS that I'd like to remove:
body {
height: 100vh;
margin: 0;
}
.container>* {
height: 100%;
width: 100%;
flex: 0 0 50px;
}
.container {
width: 100%;
height: 100%;
background-color: black;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.header {
background-color: red;
}
.content {
background-color: blue;
flex: 1;
position: relative;
}
.footer {
margin-top: auto;
background-color: yellow;
}
.fixedRatio {
height: 56.25vw;
max-height: calc(100vh - 100px);
width: calc((100vh - 100px) * (1/0.5625));
;
max-width: 100vw;
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
<div class="container">
<div class="header"></div>
<div class="content">
<div class="fixedRatio"></div>
</div>
<div class="footer"></div>
</div>
Included are a header and footer of arbitrary height and a fixed aspect ratio box centered vertically and horizontally between them. I'd like it to letter- and pillar-box as the page is resized, and respond to increases/decreases in header height.
As it stands, the code accomplishes many of these goals but falls short in that it requires that the heights of the header and footer be included in the CSS for the fixed aspect ratio box. This limits my ability to freely manipulate the size of the header, or let it grow arbitrarily as a function of content (at least to the extent I am not using JavaScript).
I've managed to make this work successfully for the case of letter-boxing (top and bottom black bars) by leveraging the fact that the content is full-width. As a result, I can use 100vw / 56.25vw (in the case of 16:9) for the width/height and achieve the desired result. Unfortunately, when moving the content around to pillar-box, this obviously falls apart.
I've more or less resigned myself to needing JavaScript to - at the very least - toggle a class based on the dimensions of the inner content box to determine whether letter or pillar boxing is appropriate. However, it became very clear very quickly that setting width as a function of height is not trivial.
I was fortunate to come across this post, where a solution leveraging a 1x1 pixel is used to set width as a function of height.
I was able to successfully make this work for the pillar-boxing case in both Chrome and Safari, but not Firefox (IE11 and Edge not yet tested, but coverage is desired... pray for me). I'd like to get recent versions of Chrome/Safari/Firefox covered, as well as I11/Edge if possible.
The solution for Chrome/Safari is as follows:
body {
height: 100vh;
margin: 0;
}
.header,
.footer {
height: 100%;
width: 100%;
}
.container>* {
flex: 0 0 50px;
}
.container {
width: 100%;
height: 100%;
background-color: black;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.header {
background-color: red;
}
img {
display: block;
height: 100%;
background: orange;
}
.content {
background-color: blue;
flex: 1;
position: relative;
overflow: hidden;
height: 100%;
display: inline-block;
left: 50%;
transform: translateX(-50%);
}
.footer {
margin-top: auto;
background-color: yellow;
}
.fixedRatio {
background-color: purple;
position: absolute;
left: 0;
top: 0;
right: 0;
margin: auto;
bottom: 0;
}
<div class="container">
<div class="header"></div>
<div class="content">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" />
<div class="fixedRatio"></div>
</div>
<div class="footer"></div>
</div>
There are a few things to consider. I am comfortable with fixing the height of the footer. This constraint may prove valuable, but I've been unable to yield anything from it yet. I am also comfortable with radical alterations to the included markup, supposing it enables the desired behavior.
The end-purpose of this would be to maintain fixed aspect ratio content between this flexible header, static footer, and overlay content upon it.
I am well aware that I could run some JavaScript and set these heights manually with great success, but I am coming at this from a position largely based in intellectual curiosity. If you, too, are curious, perhaps you can lend a hand in exploring :)
Thank you!

Related

React app text rendering in different locations in different browsers

I am building a website to be used on a mobile phone. The css is very straight forward however I can't seem to get the text to align in the same place vertically. Some browsers the text will appear completely at the bottom of the screen, other browsers show the text slightly above my footer (3rem), others show the browser maybe 6 rem from the bottom.
Mobile.js
const Mobile = () => {
return (
<div className={style.app}>
<div className={style.mainContent}>
<div className={style.address}>128 W 2nd St, Winona, MN 55987</div>
</div>
</div>
);
};
css
html,
body {
margin: 0;
/* height: 100% */
}
.app {
height: calc(100vh - 8rem);
width: 100vw;
background-size: cover;
display: flex;
justify-content: center;
margin-top: 5rem;
margin-bottom: 3rem;
position: relative;
}
.mainContent {
color: white;
width: 100%;
/* display: flex;
align-self: flex-end; */
margin-bottom: 1.5rem;
margin-left: 1rem;
position: absolute;
bottom: 3rem;
left: 0;
}
Now I have tried a bunch of different things such as using flex-end on .mainContent, and using a larger margin-bottom. I am really not sure whats going on here because the css looks very simple to me.

How to keep size proportions for div's child elements

TL;DR: How to keep the div children proportional to the div itself?
I have a div, containing various elements like text, images, icons etc. It keeps 16:9 aspect ratio and fills as much viewport it can, while resizing the browser window, the div (with background different from the body background) changes size well, though the contents are staying the same size which is bad because I'm trying to make a presentation website which needs to look the same at various resolutions. How do I make the child elements align and resize properly inside the div?
I tried using viewport units though it didn't turn out really well.
My Code:
I tried using % units to set font size and then use em to scale other things but it didn't work. I also tried using only % units to set all properties but it did not work either
body {
background: black;
user-select: none;
margin: 0;
height: 100vh;
}
.container2 {
overflow: auto;
box-sizing: border-box;
resize: both;
overflow: auto;
max-width: 100%;
height: 100%;
display: flex;
justify-content: center;
}
.presentation-place {
user-select: none;
background: white;
top: 50%;
transform: translate(0, -50%);
position: absolute;
align-items: center;
aspect-ratio: 16 / 9;
}
#media screen and (max-aspect-ratio: 16 / 9) {
.presentation-place {
width: 100vw;
}
}
#media screen and (min-aspect-ratio: 16 / 9) {
.presentation-place {
height: 100vh;
}
}
.slide {
font-size: 100%;
position: absolute;
top: 0;
bottom: 0;
margin: 0 auto;
width: 100%;
height: 100%;
overflow: hidden;
background: red;
background-position: center center;
}
.title1 {
margin-left: 1em;
font-size: 6em;
position: absolute;
margin-top: 2em;
}
<html>
<body>
<div class="container">
<div class="presentation-place">
<div class="slide s1">
<h1 class="title1">test</h1>
</div>
</div>
</div>
</body>
</html>
Make sure to avoid specific units like cm, px etc because those are fixed units no matter the scale of the site itself or the monitor, the use of Units like % since vh/vw didnt work. % scales relative to the size of the monitor or website, so this should help. Alternativly you could use aspect-ratio because it scales relative to the size of the parent element

Vertically Draggable Division of Two Areas

I want to make a vertically draggable division of two areas like the following.
I just want to modify a online example of draggable divs to be what I want. Finally, I got this. Can someone give me some hints to modify it?
JSFiddle Link : https://jsfiddle.net/casperhongkong/omekvtka/14/
HTML
<div class="container">
<div class="area1">
Area 1
</div>
<div class="drag">
</div>
<div class="area2">
Area 2
</div>
</div>
CSS
.container {
position: fixed;
top: 51px;
right: 0px;
bottom: 0px;
left: 0px;
background-color: #272822;
border: 1px solid #222;
// margin: 0 auto;
//display: inline-block;
}
.area1 {
position: absolute;
height: 100%;
width: 30%;
background-color: #ddd;
display: inline-block;
}
.drag {
position: fixed;
width: 5px;
height: 100%;
background-color: #444;
display: inline-block;
}
.area2 {
position: absolute;
right: 0;
height: 100%;
width: 30%;
background-color: #ddd;
display: inline-block;
}
JavaScript
$(document).ready(function() {
$('.drag').on('mousedown', function(e) {
var $area1 = $('.area1'),
$area2 = $('.area2'),
startWidth_a1 = $area1.width(),
startWidth_a2 = $area2.width(),
pX = e.pageX;
$(document).on('mouseup', function(e) {
$(document).off('mouseup').off('mousemove');
});
$(document).on('mousemove', function(me) {
var mx = (me.pageX - pX);
$area1.css({
width: startWidth_a1 - mx;
});
$area2.css({
//left: mx / 2,
width: startWidth_a2 - mx,
//top: my
});
});
});
});
For javascript, I would recommend checking out a library, as this is slitghtly more complicated than just a few lines. #fauxserious gave Split.js as a fantastic example.
This is possible in pure HTML/CSS, though slightly limited, as discussed here.
HTML:
<div class="split-view">
<div class="resize-x panel" style="width: 216px;">
Panel A
</div>
<div class="panel">
Panel B
</div>
</div>
CSS:
/* Panels: */
.panel{
padding: 1em;
border-width: 6px;
border-style: solid;
height: 4em;
}
/* Resizing */
.resize-x {
resize: horizontal;
overflow: auto;
}
/* Split View */
.split-view {
margin: 1em 0;
width: 100%;
clear: both;
display: table;
}
.split-view .panel {
display: table-cell;
}
Based on #afischer's table-cell solution, here is an alternative one.
I have had to put accordions within the left side panel.
The sticky headers of the accordions require overflow to be visible,
while resize requires overflow to be anything but visible:
https://caniuse.com/#feat=css-sticky.
In the same time, I didn't need anything to place into the right side panel.
Thus, an overcome was employing resize on the right panel, and rotating by 180 deg to get the dragable side to the middle, as well as this way the dragable corner relocated to the top (visible without scrolling).
Plus some highlight has been added to the dragable corner.
/* Panels: */
.panel{
padding: 1em;
border-width: 6px;
border-style: solid;
height: 4em;
}
/* Resizing */
.resize-x {
resize: horizontal;
overflow: auto;
transform: rotate(180deg);
border-right: solid gray 1px;
}
/* Split View */
.split-view {
margin: 1em 0;
width: 100%;
clear: both;
display: table;
}
.split-view .panel {
display: table-cell;
}
.resize-x::-webkit-resizer {
border-width: 8px;
border-style: solid;
border-color: transparent orangered orangered transparent;
}
<div class="split-view">
<div
class="panel"
style="width: 216px;">
Panel A
</div>
<div class="panel resize-x">
Panel B
</div>
</div>
Unfortunately there are two disappointing thing with the above:
Firefox cannot handle the combination of table-cell and resize
Only a corner of the grabber is responsive, which can even be scrolled out easily
Here is an other solution which also takes the above two problems into account
without resize CSS property
and with full height responsive grabber
It's a combination of a flexbox and an input:range slider.
The trick is that the pointer-event CSS property can be different
on the slider's background
and on its grabber.
The slider covers the entire view. The background of the slider is transparent for the events too (pointer-events: none), while the dragbar itself catches the events (pointer-events: auto).
It requires minor Javascript and because I've implemented the production version in Nuxt.js, I use Vue.js here, instead of vanilla JS.
new Vue({
el: '#vue',
data: {
windowWidth: null,
splitWidth: null,
},
mounted() {
this.windowWidth = window.innerWidth;
// For arbitrary initial position:
this.splitWidth = this.windowWidth * 2/3;
},
computed: {
flexRatio() {
return this.splitWidth / this.windowWidth;
}
}
})
body {
margin:0;
}
main {
display: flex;
width: 100%;
height: 100vh;
}
article {
display: flex;
}
section {
width: 100%;
height: 100%;
text-align: justify;
padding: 20px;
}
.section-left {
background-color: darkseagreen;
}
.section-right {
background-color: orangered;
}
#split-grabber {
pointer-events: none;
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
-webkit-appearance: none;
/* Safari allows dragging behind scroll bar.
We fix it by shrinking its width on the right side via both
its range value :max="windowWidth - 12"
and its width (CSS) width: calc(100% - 12px)
...synchronously */
width: calc(100% - 12px);
height: 100vh;
background: transparent;
outline: none;
margin: 0;
}
#split-grabber::-webkit-slider-thumb {
z-index: 1;
pointer-events: auto;
-webkit-appearance: none;
appearance: none;
width: 5px;
height: 100vh;
background: lightgray;
box-shadow: 1px 2px 2px 0px gray;
cursor: col-resize;
}
#split-grabber::-moz-range-thumb {
z-index: 1;
pointer-events: auto;
-webkit-appearance: none;
appearance: none;
width: 5px;
height: 100vh;
background: lightgray;
box-shadow: 1px 2px 2px 0px gray;
cursor: col-resize;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<main id="vue">
<!-- Safari allows dragging behind scroll bar
We fix it by shrinking its width on the right side via both
its range value :max="windowWidth - 12"
and its width (CSS) width: calc(100% - 12px)
...synchronously -->
<input
id="split-grabber"
type="range"
v-model="splitWidth"
:max="windowWidth - 12"
>
<article
class="article"
:style="{'flex': flexRatio}"
>
<section
class="section section-left">
splitWidth:{{ splitWidth }}px<br>
“There was a rich man who always dressed in the finest clothes and lived in luxury every day.
And a very poor man named Lazarus, whose body was covered with sores, was laid at the rich man’s gate.
He wanted to eat only the small pieces of food that fell from the rich man’s table. And the dogs would come and lick his sores.
Later, Lazarus died, and the angels carried him to the arms of Abraham. The rich man died, too, and was buried.
In the place of the dead, he was in much pain. The rich man saw Abraham far away with Lazarus at his side.
He called, ‘Father Abraham, have mercy on me! Send Lazarus to dip his finger in water and cool my tongue, because I am suffering in this fire!’
</section>
</article>
<article
class="article"
:style="{'flex': 1-flexRatio}"
>
<section class="section section-right">
But Abraham said, ‘Child, remember when you were alive you had the good things in life, but bad things happened to Lazarus. Now he is comforted here, and you are suffering.
Besides, there is a big pit between you and us, so no one can cross over to you, and no one can leave there and come here.’
The rich man said, ‘Father, then please send Lazarus to my father’s house.
I have five brothers, and Lazarus could warn them so that they will not come to this place of pain.’
But Abraham said, ‘They have the law of Moses and the writings of the prophets; let them learn from them.’
The rich man said, ‘No, father Abraham! If someone goes to them from the dead, they would believe and change their hearts and lives.’
But Abraham said to him, ‘If they will not listen to Moses and the prophets, they will not listen to someone who comes back from the dead.’”
</section>
</article>
</main>

Alignment with fixed elements and scrollable content

Here is a simplified JSFiddle of the problem.
As you can see, the content is beat out of alingment with the header because of the scrollbar.
As far as I know, the only way to deal with this is to calculate the width of the scrollbar using Javascript (David Walsh's excellent method springs to mind) and to set it as left/right: -scrollbarwidthpx value to the header.
However, considering the dynamic nature of the page I'm working on, with the headers place in the DOM and position changing depending on at what point the user has scrolled to, this is an option I am hoping to turn to only if there's nothing else I can do.
My question is, is there any way that I can maybe take the scrollbars or the content out of the flow while preserving a scrolling overflow, or otherwise align the two elements using only HTML/CSS? Are scrollbar widths consistent across all browsers/OS's that have them affect the flow? Or will I have to resort to using Javascript to align them?
Thanks!
html, body {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
}
.scroll {
overflow: auto;
width: 100%;
height: 100%;
}
#content{
width: 400px;
height:auto;
margin: auto;
background:gray;
}
.header {
width: 400px;
position: fixed;
margin: 0 auto;
height: 60px;
background: yellow;
z-index: 10;
}
.content {
height: 1200px;
background: linear-gradient(red, orange);
width: 100%;
margin: auto;
position: relative;
z-index: 1;
border-top:61px solid;
border-bottom:1px solid;
}
<div class="scroll">
<div id="content">
<div class="header"></div>
<div class="content"></div>
</div>
</div>

CSS margin-top and top are not bound

I'm having some trouble with a page that has a floating background image (absolutely positioned) where the image is dynamically changed out via javascript. Basically this is a big gallery that changes behind a portfolio:
I have a section of markup that looks like this:
<div class="content">
<div class="content-container">
<div class="content-image">
<img id="galleryTarget" src="../images/main/source.jpg" class="image-resize" alt="background image"/>
</div>
...etc...
Here's the relevant CSS classes:
.image-resize {
position: absolute;
min-height: 750px;
min-width: 1000px;
width: 100%;
left: 0;
text-align: left;
vertical-align: middle;
margin-top: -25%;
top: 25%;
}
.content-image {
position: absolute;
top: 0;
left: 200px;
width: 100%;
min-height: 750px;
max-height: 750px;
min-width:1000px;
overflow:visible;
opacity: 0.5;
z-index: 1;
}
.content-container {
position: relative;
min-height: 750px;
}
.content {
position: absolute;
top: 0;
width: 100%;
max-height: 750px;
overflow: hidden;
background: purple;
z-index: -5;
}
This is all absolutely positioned so that I can swap out the image source with Javascript and then dynamically resize the container (background) to fill the new content. There's minimum bounds so it always has a size.
What I'm trying to do is to pin this image to a CENTER point so that when it is resized the interesting parts of the image (rarely the top left corner) are displayed.
In the inspector in chrome I see that top and margin-top are never the same value even though they have the same (percentage) value. What am I missing here?
Example:
top: 187.5px and margin-top: -389.5px. It looks as though margin-top uses the img-source resolution and top uses something for the life of me I can't figure out--I'm assuming min-height + the offset in the page?
Any help here would be appreciated, this is a rather large part of the design and I'd love to have it better than what it is.
Browsers:
Chrome Version: 30.0.1599.66 m
Android Chrome: 30.0.1599.82
This does fix the problem in chrome--but I'd like to know why it is using 1000px as the baseline for the margin instead of the 750px of the unit.
/*Hack of a vector similar to 50%*/
margin-top: calc(-50% * 0.75);
top: 50%;

Categories