Move and Resize Overlapping 16:9 Aspect Ratio Images - javascript

SOLVED (I think) -- See Below.
Quick Background: For a simple IOS game I have 16:9 background area that I am floating images over top of that need to blend into the background. For that, I need them to resize with the background and move with the background.
Hypothesis: I'm keeping the game area 16:9 because I assume that will scale well with various devices. If I also make the images that need to float on top of the background 16:9, they should scale at the same rate and provide a steady % scale to also move them in place.
Test: I have a background area of 1280x720, with a slice cut out 256x144(20%). I have an image of 256x144 that I'm trying to overlay that slice, which sometimes works, but doesn't always respond well to resizing or doesn't line up on various devices. Is my math somehow wrong?
Here's the code: JS Fiddle: Testing Image Overlap
SOLVED -
I was incorrectly assuming that the width of the floated image and the % it needed to be moved was based on a % of the window width (as is the case with the container DIV) to maintain it's 16:9 ratio, but the incorrect results occurred when the 16:9 ratio of the container maxed out at less than the screen width. What I actually want is the image nested into the container and use a % of that parent instead of the window! Can't believe I missed that for hours...
New Code (Hope this will help someone): Updated Fiddle - Proper Image Scaling
<style>
body {
margin: 0;
padding: 0;
}
#Game_Wrapper {
width: 100vw;
height: calc(100vw * 9 / 16);
margin: auto;
padding: 0;
position: absolute;
top:0;bottom:0; /* vertical center */
left:0;right:0; /* horizontal center */
border: 1px solid red;
background: lightblue url('https://i.imgur.com/BFCIQuw.png') center/contain no-repeat;
}
#Game_Area {
margin: 0;
padding: 0;
width: 100%;
height: calc(100% - 60px);
}
#GUI_Top {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Top p {
margin: 0;
padding: 0;
text-align: center;
}
#GUI_Bottom {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Bottom p {
margin: 0;
padding: 0;
text-align: center;"
}
/* 100 * 16/9 = 177.778 */
#media (min-width: 177.778vh) {
#Game_Wrapper {
height: 100vh;
width: calc(100vh * 16 / 9);
}
}
.Building_Position {
width: calc(100vw * .2);
height: calc(100vh * .2);
margin: 0;
padding: 0;
text-align: center;
font-weight: bold;
border: 1px solid #000;
}
#Building1 {
margin: 0;
padding: 0;
float: left;
position: absolute;
left: calc(100vw * .40);
top: calc(100vh * .20);
}
#Building1 img {
margin: 0;
padding: 0;
width: calc(100vw * .20);
height: calc(100vh * .20);
}
<meta charset="utf-8" name="viewport" content= "width=device-width, initial-scale=1.0">
<title>Test Game Layout</title>
</head>
<body>
<div id="Game_Wrapper">
<!-- This will hold the entire game and GUI -->
<div id="GUI_Top">
<!-- This is where the top GUI will be -->
<p>This is the top GUI / NAV</p>
</div>
<div id="Game_Area">
<!-- This is where you can interact with the actual game -->
</div>
<div id="GUI_Bottom">
<!-- This is where the bottom GUI will be -->
<p>This is the bottom GUI / NAV</p>
</div>
<div class="Building_Position" id="Building1">
<a href="#Link To Function" alt="Building Name" title="Building Name & Level" class="load_view">
<img class="building_image" src="https://i.imgur.com/dqEFcAu.png">
</a>
</div>
</div>
</body>

Related

css triangle based on page height

Currently I have the situation as shown below in the snippet.
But now I want a triangle that is the same on every page. No matter how long the page is. So for example if the page is really long, then the triangle will at one point go out of the screen and there will be no more green background. (as shown here)
But the most important thing is that on every page the triangle/angle will be the same. How to do this?
$(document).ready(function() {
function waitForElement(elementPath, callBack) {
window.setTimeout(function() {
if ($(elementPath).length) {
callBack(elementPath, $(elementPath));
} else {
waitForElement(elementPath, callBack);
}
}, 300)
}
waitForElement("#leftdiv", function() {
// Initial background height set to be equal to leftdiv
$('#rightdiv').height($('#leftdiv').height());
// Initial triangle height set to be equal to leftdiv
$('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
});
// When window resizes
$(window).resize(function() {
// Change height of background
$('#rightdiv').height($('#leftdiv').height());
// Change height of white triangle
$('#triangle').css('border-top', $('#leftdiv').height() + 'px solid transparent');
});
});
.container-general {
float: left;
position: relative;
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
float: right;
position: relative;
/*height is set in javascript*/
width: 30%;
background-color: green;
}
#triangle {
position: absolute;
height: 0;
width: 0;
bottom: 0;
left: -1px;
border-left: 10vw solid white;
border-right: 0px solid transparent;
/*border-top is set in javascript*/
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div id="triangle"></div>
</div>
You don't need JavaScript and jQuery at all for this, as long as you are willing to make minor changes to your markup:
Step 1: Update your markup
Wrap both your .container-general and .background-general with a common parent element
Use display: flex; overflow: hidden; on the parent. This has the effect of stretching the shorter background element to full height of .container-general
Step 2: Determine the fixed angle you want and set aspect ratio
Important note: If you want to keep the angle constant, you will need to know what angle you want. That will require one important trick: you want to keep .background-general the same aspect ratio in all cases, so the angle stays constant. Let's say you want it to be 60° (i.e. Math.Pi / 3): with some math, that means that the height of the .background-general should be this ratio relative to the width:
containerHeightRatioToWidth = Math.tan(Math.PI / 3) = 1.732052602783882...
There is a trick to preserve the aspect ratio: you simply set the padding-bottom of the background element. In this case, you want it to be padding-bottom: 173%); (we don't need absolute precision so we can drop the decimal points).
Here's a handy table on the height (in CSS percentages) you can use:
30deg: padding-bottom: 57%:
45deg: padding-bottom: 100%:
60deg: padding-bottom: 173%:
You can also precalculate the percentage in your browser console by pasting this:
var desiredAngleInDegrees = 60;
Math.tan(Math.PI * desiredAngleInDegrees / 180) * 100
The markup is structured as follows:
└─┬.wrapper
├──.container-general
└─┬.background-general
└─┬.background-general__background
├─::before (triangle)
└─::after (remaining fill)
To achieve the triangle effect, you have two approaches:
Step 3A: Use clip-path to trim the background element to look like a triangle
clip-path is very widely supported by modern browsers, with a notable exception for IE11 and Edge :/ This should do the trick: clip-path: polygon(100% 0, 0 0, 100% 100%);
.wrapper {
display: flex;
overflow: hidden;
}
.container-general {
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
position: relative;
width: 30%;
background-color: green;
overflow: hidden;
}
.background-general__background {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/* Triangle */
.background-general__background::before {
flex-grow: 0;
content: '';
display: block;
width: 100%;
padding-bottom: 173%;
background-color: white;
clip-path: polygon(0 100%, 0 0, 100% 100%);
}
/* Extra fill */
.background-general__background::after {
flex-grow: 1;
content: '';
display: block;
background-color: white;
/* Needed to fix subpixel rendering */
margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div class="background-general__background"></div>
</div>
</div>
Step 3B: Use an inline SVG as background image
For the greater browser compatibility, use an inline encoded SVG and stretch it to 100% width and 100% height of the parent.
We can create a simple 10×10px SVG of the following markup:
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10">
<path fill="green" d="M0,0 L10,0 L10,10 z"></path>
</svg>
Note: The preserveAspectRatio="none" is required so that we can freely stretch the SVG beyond its usual aspect ratio. For more information of how the <path>'s d attribute works, see this article: The SVG path Syntax: An Illustrated Guide
Then, all you need is to stuff this short SVG markup as data:image/svg+xml for the background image of the background container, i.e.:
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="green" d="M0,0 L10,0 L10,10 z"></path></svg>');
See example below:
.wrapper {
display: flex;
overflow: hidden;
}
.container-general {
background-color: black;
height: 500px;
width: 70%;
}
.background-general {
position: relative;
width: 30%;
background-color: green;
overflow: hidden;
}
.background-general__background {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/* Triangle */
.background-general__background::before {
content: '';
display: block;
flex-grow: 0;
width: 100%;
padding-bottom: 173%;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" viewBox="0 0 10 10"><path fill="white" d="M0,0 L0,10 L10,10 z"></path></svg>');
background-size: 100% 100%;
}
/* Extra fill */
.background-general__background::after {
flex-grow: 1;
content: '';
display: block;
background-color: white;
/* Needed to fix subpixel rendering */
margin-top: -1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container-general" id="leftdiv">
</div>
<div class="background-general" id="rightdiv">
<div class="background-general__background"></div>
</div>
</div>
A simple "border triangle" bind to vw units might do:
body {
min-height: 2000px;
}
#triangle {
position: absolute;
top: 0px;
right: 0px;
border-top: 100vw solid #ff0000; /* The height of the triangle */
border-left: 30vw solid transparent; /* The width of the triangle */
}
<div id="triangle"></div>
A fiddle to play with.

16:9 Aspect Ratio App & Background (Accounting for space taken by the Nav)

Ok, I am working on creating a simple game for IOS / Android and I'm having a little trouble with consistency in the layout across devices.
My new hypothesis is essentially if I develop the layout in 16:9 ratio, it will scale well for most / all devices. I have achieved maintaining the ratio on the outside wrapper of the game, but the game area is also a 16:9 ratio background image, which I cannot get to properly scale down to fill the "game area"
For this, let's assume there is 30px on top and 30px on bottom of the page for navigation, which means the "game area" has a height of 100% - 60px and width of 100%, which leads to the problem. despite the background image being 16:9 ratio, that area is no longer a 16:9 canvas area.
My attempts:
Change the image to lop off 60px -> this failed horribly
Centering the background image on the "game area", which isn't too bad, but we lose a lot of space on the sides. Background 100% of inner DIV
Using canvas to force the image into the area, but things get blurry: Background Using Canvas
Moving the background image to the outside wrapper / body. This actually looks quite good! BUT I lose 60px of the image still that is covered by the NAV. Seen Here: Background 100% of wrapper that is 16:9
Any other thoughts on how to get a better result?
This is the current code:
#Game_Wrapper {
width: 100vw;
height: calc(100vw * 9 / 16);
margin: auto;
position: absolute;
top:0;bottom:0; /* vertical center */
left:0;right:0; /* horizontal center */
padding: 0;
border: 1px solid red;
background: lightblue url('https://i.imgur.com/tRFltCI.png') 0 0/contain no-repeat;
}
#Game_Area {
margin: 0;
padding: 0;
width: 100%;
height: calc(100% - 60px);
}
#GUI_Top {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Top p {
margin: 0;
padding: 0;
text-align: center;
}
#GUI_Bottom {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Bottom p {
margin: 0;
padding: 0;
text-align: center;
}
/* 100 * 16/9 = 177.778 */
#media (min-width: 177.778vh) {
#Game_Wrapper {
height: 100vh;
width: calc(100vh * 16 / 9);
}
}
<meta charset="utf-8" name="viewport" content= "width=device-width, initial-scale=1.0">
<body>
<div id="Game_Wrapper">
<!-- This will hold the entire game and GUI -->
<div id="GUI_Top">
<!-- This is where the top GUI will be -->
<p>This is the top GUI / NAV</p>
</div>
<div id="Game_Area">
<!-- This is where you can interact with the actual game -->
</div>
<div id="GUI_Bottom">
<!-- This is where the bottom GUI will be -->
<p>This is the bottom GUI / NAV</p>
</div>
</div>
</body>
As you already notice, it's impossible to keep the 16:9 ratio of the image inside a container that is also 16:9 and having other elements.
What I advise is to fill the remaining space with an extension of the image so we may think it's the same image.
Here is an approximation of how it shoud look. I simply filled the empty space with the same image but blurred. The idea is to find the good pattern to make it visually better:
#Game_Wrapper {
width: 100vw;
height: calc(100vw * 9 / 16);
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 0;
border: 1px solid red;
}
#Game_Area {
margin: 0;
padding: 0;
width: 100%;
height: calc(100% - 60px);
background: url('https://i.imgur.com/tRFltCI.png') center/contain no-repeat;
position:relative;
overflow:hidden;
}
#Game_Area:before {
content:"";
position:absolute;
top:-10px;
left:-10px;
right:-10px;
bottom:-10px;
z-index:-1;
background:
url('https://i.imgur.com/tRFltCI.png') right/contain no-repeat,
url('https://i.imgur.com/tRFltCI.png') left/contain no-repeat;
filter:blur(8px);
}
#GUI_Top {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Top p {
margin: 0;
padding: 0;
text-align: center;
}
#GUI_Bottom {
margin: 0;
padding: 0;
height: 30px;
background: grey;
}
#GUI_Bottom p {
margin: 0;
padding: 0;
text-align: center;
}
/* 100 * 16/9 = 177.778 */
#media (min-width: 177.778vh) {
#Game_Wrapper {
height: 100vh;
width: calc(100vh * 16 / 9);
}
}
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0">
<body>
<div id="Game_Wrapper">
<!-- This will hold the entire game and GUI -->
<div id="GUI_Top">
<!-- This is where the top GUI will be -->
<p>This is the top GUI / NAV</p>
</div>
<div id="Game_Area">
<!-- This is where you can interact with the actual game -->
</div>
<div id="GUI_Bottom">
<!-- This is where the bottom GUI will be -->
<p>This is the bottom GUI / NAV</p>
</div>
</div>
</body>

A circle element next to a fluid rounded element

Is the following layout possible with CSS? If not, is there a better solution than my current JS solution? See the fiddle for a complete example.
+--------------------+
| |
| |
| |
| |
| |
| |
| |
| |
| |
|--------------------+
|+----+ +----------+| container height in percentage, e.g. 20% of window
|| 1 | | 2 || button 1: a circle based on container height
|+----+ +----------+| button 2: fill available space and fully round corners
+--------------------+
The basic issue is that the first element needs to be a circle, i.e. a rounded square, based on the height of the container. And the second element should fill the rest of the space with the same border-radius.
Following is how I solved it with JS, but it does not seem to be too reliable on mobile devices. And the project is basically mobile-only. Also, if the layout is too dependent on JS, it will cause other trouble when doing fancy transitions etc. with CSS.
Fiddle: http://jsfiddle.net/n52x1ws1/3/
$(document).ready(function(){
var height = $(".device-fluid").find(".btn-circle").height();
var borderRadius = height / 2;
$(".device-fluid").find(".btn-circle").css("width", height);
$(".device-fluid").find(".btn-circle").css("border-radius", borderRadius);
var fluidWidth = $(".device-fluid").find(".container").width() - height - 15;
$(".device-fluid").find(".btn-fluid").css("width", fluidWidth);
$(".device-fluid").find(".btn-fluid").css("border-radius", borderRadius);
});
* {
box-sizing: border-box;
font-family: sans-serif;
}
.device {
display: inline-block;
position: relative;
width: 320px;
height: 480px;
border: 2px solid #ccc;
margin: 30px;
text-align: center;
}
.label {
margin-top: 30px;
}
.container {
position: absolute;
bottom: 0;
width: 100%;
height: 20%;
padding: 15px;
background: #f7f7f7;
}
.btn {
height: 100%;
}
.btn-circle {
float: left;
}
.btn-fluid {
float: right;
}
.device-fixed .btn-circle {
width: 66px; /* easy since we know the height */
border-radius: 33px;
background: #2ecc71;
}
.device-fixed .btn-fluid {
width: 205px; /* available space minus a 15px margin */
border-radius: 33px;
background: #27ae60;
}
.device-fluid .btn-circle {
width: 20%; /* this needs to be equal to the height */
border-radius: 50%;
background: #2ecc71;
}
.device-fluid .btn-fluid {
width: 75%; /* this needs to fill the rest of the available space minus a 15px margin */
border-radius: 50%;
background: #27ae60;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="device device-fixed">
<div class="label">fixed</div>
<div class="container">
<div class="btn btn-circle"></div>
<div class="btn btn-fluid"></div>
</div>
</div>
<div class="device device-fluid">
<div class="label">fluid with JS</div>
<div class="container">
<div class="btn btn-circle"></div>
<div class="btn btn-fluid"></div>
</div>
</div>
I'm not exactly sure what you mean by
container height in percentage, e.g. 20% of window
If that means that the height of the container is determined by the size of the viewport you can use the viewport units. 1vh equals 1% of the viewport height.
.container {
height: 20vh;
}
You can then easily make a circle based on this height:
.btn-circle{
height: 20vh;
width: 20vh;
border-radius: 10vh;
}
The next div should fill the available space
.btn-fluid{
height: 20vh;
width: calc(100vw - 20vh); /*100% of the viewport width minus the space for the square*/
border-radius: 10vh;
}
It looks like this in a fiddle.

absolute positioning layout - vertical center over previous div

A simplified version of my problem is that I want to center a 'text' div over an 'image' div on screen mode, but position them underneath each other on mobile.
HTML
<div class="wrapper">
<article> </article>
<div class="text-cell">
<h2>Title</h2>
<h3>Category</h3>
</div>
</div>
css
.wrapper {
position: relative;
margin: 0;
padding: 0;
}
article {
height: 350px;
width: 100%;
border-top: 1px solid #000;
background-size: cover;
background-repeat: no-repeat;
}
.text-cell {
position: absolute;
z-index: 10;
width: 100%;
margin: 0;
bottom: 0;
}
#media screen and (max-width: 768px) {
.text-cell {
position: relative;
border-top: solid 1px #000;
background-color: rgba(250, 250, 250, 1);
}
}
This is the closest I got. It puts the 'text-cell' overtop of article at screen size and underneath on mobile size.
I want it to be vertically centred over the 'article' on screen size, as it resizes. 'text-cell' doesn't have a defined size, and Ideally I'd like article to be a percentage or a more responsive size, but that isn't as important.
EDIT - this html is part of a Wordpress loop, meaning it is generated a certain amount of times, and each one is stacked under the other. (for ex. 3 copies of < div class="wrapper" > stacked)
What I am going for
The first thing i'd recommend doing is placing that .text-cell div above the article, that way, on mobile, it's statically where you want it to be placed, following the document natural hierarchy. This is what i've done in the code snippet (i've also changed its position to static inside the media query.)
Having said that, i would personally consider changing the structure of the HTML to have more semantic meaning (for example, placing the h2 & h3 tags inside a header tag and place that inside your article tag.)
If you want help doing that, explain what you're trying to achieve inside the article, and i'm happy to help you achieve what you're looking for.
.wrapper {
position: relative;
margin: 0;
padding: 0;
}
article {
height: 350px;
width: 100%;
border-top: 1px solid #000;
background-size: cover;
background-repeat: no-repeat;
}
.text-cell {
position: absolute;
z-index: 10;
width: 100%;
margin: 0;
bottom: 0;
}
#media screen and (max-width: 768px) {
.text-cell {
position: static;
border-top: solid 1px #000;
background-color: rgba(250, 250, 250, 1);
}
}
<div class="wrapper">
<div class="text-cell">
<h2>The title</h2>
<h3>The category</h3>
</div>
<article style="background-image:url('http://placehold.it/300x300');">
</article>
</div>

scroll the content inside the div with fixed position using browser/page scroll bar

I have some div's with position:fixed all around the page.
one of that div has little more long content.
my aim is that I want to scroll the content inside that box, using the main browser/page scroll-bar. (its not normal overflow:auto like this)
this is the exact situation
http://s7.postimage.org/d6xl1u9mz/sample.jpg
is any plugin available ?
Without knowledge of your HTML:
<body>
<section id="bodyContent"></section>
<header></header>
<section id="lSide"></section>
<section id="rSide"></section>
</body>
#bodyContent {
box-sizing: border-box;
-moz-box-sizing: border-box;
min-height: 100%;
width: 100%;
padding: 90px 45px 0px 105px;
background-clip: content-box;
background-color: #FFFFFF;
}
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-image: url(page_background.jpg);
background-attachment: fixed;
}
header, #lSide, #rSide {
position: fixed;
}
header {
top: 0;
width: 100%;
height: 90px;
background-image: url(page_background.jpg);
background-attachment: fixed;
}
#lSide {
left: 0;
top: 0;
height: 100%;
width: 105px;
padding: 90px 0 0 0;
}
#rSide {
right: 0;
top: 0;
height: 100%;
width: 45px;
padding: 90px 0 0 0;
}
This will force the contents of #bodyContent to sit inside the opening between the various border elements, and it will cause any overflow to trigger a scrollbar on the body element as you desire. JSFiddle
Maybe it's possible. I've created a jsFiddle which does the trick. It's not perfect, but you can develope it further... Also this snippet works only with modern browsers, but is easy to fix for older IEs too. Core code below.
JavaScript:
window.onload = function () {
var content = document.getElementById('contentwrapper'),
dimdiv = document.getElementById('scrollingheight'),
wrapHeight = document.getElementById('fixed').offsetHeight,
scroller = function () {
content.style.top = -(document.documentElement.scrollTop || document.body.scrollTop) + 5 + 'px';
return;
};
dimdiv.style.minHeight = (content.scrollHeight - wrapHeight + 2 * 5) + 'px';
window.addEventListener('scroll', scroller, false);
return;
}
CSS:
#fixed {
position: fixed;
min-width: 300px;
min-height: 200px;
max-height: 200px;
background: #fff;
left: 150px;
top: 200px;
overflow-y: hidden;
border: 1px solid #000;
}
#contentwrapper {
max-width: 290px;
position: absolute;
top: 5px;
left: 5px;
}
#scrollingheight {
position: absolute;
top: 100%;
visibility: hidden;
min-width: 1px;
}
HTML:
<div id="scrollingheight"></div>
<div id="fixed">
<div id="contentwrapper">
content
</div>
</div>
Notice, that all content in the body, but #scrollingheight, must be fixed. Constant 5 is related to #contentwrapper's top value.
AFAIK you cannot do that.
At least not without some wicked JS trickery.
Why? cause you cannot force the browser's default scrollbar height (make it smaller) to embrace some content that is inside a totally different area than the html, body (document).
My suggestion is that you build a custom scrollbar, calculate the height of your nice overflow hidden area, add it to the scrollable ratio calculation.

Categories

Resources