Canvas rendering resolution - how to use a canvas in a flex layout - javascript

I'm using a flex environment to automatically scale a container with fixed sides and header (header loaded using jquery on document ready).
--------------
| header |
--------------
| |canvas| |
| | | |
--------------
There are 2 flex environments, one from the top to bottom (column) and one from left to right (row).
This seems to work normally when not taking the canvas into account.
As the canvas is scaled when the page loads and the flex is applied, its size changes while leaving the context's "render" width/height the same. This leads to the image being rendered at a small resolution and getting stretched.
To solve this issue, before rendering I run this function each time:
Map._resizeCanvas = function (canvas) {
let displayWidth = canvas.clientWidth;
let displayHeight = canvas.clientHeight;
if(displayWidth !== canvas.width || displayHeight !== canvas.height) {
canvas.width = displayWidth;
canvas.height = displayHeight;
}
};
This most of the time works but leads to an image that is strechted by a few pixels (along X).
Sometimes it causes the header to disappear or increase the page height therefore adding a scroll bar.
How can I use a canvas inside of a flex environment? Is there something I need to take into account when I set the canvas width? (like margins, paddings, I already set the display style to block, inline broke the entire paage)
Is there a way to set the context's rendering resolution without changing the layout and affecting the page?
HTML:
<body>
<div id="header-navigation"></div> <!-- set by jquery on document ready -->
<div id="page-container">
<div class="container" id="container1">
abc
<div style="height: 50pt;"></div>
</div>
<div class="container" id="container2">
<canvas id="canvas1" style="width: 100%; height: 100%; display: block"></canvas>
</div>
<div class="container" id="container3">
abc
<div style="height: 50pt;"></div>
</div>
</div>
</body>
CSS:
html,
body {
margin: 0;
width: 100%;
height: 100%;
display: flex;
flex-flow: column;
}
p {
margin: 0;
}
#header-navigation {
overflow: hidden; /* FIXME, in the flex environment, this also overflows in width */
flex: 0 1 auto;
}
#page-container {
margin: 5pt;
flex: 1 1 auto; /* part of the column flex layout */
display: flex; /* contains the row flex */
flex-flow: row;
}
.container {
height: 100%;
margin-left: 5pt;
margin-right: 5pt;
}
#container1 {
background: lightgray;
flex: 0 1 15%;
}
#container2 {
flex: 1 1 auto;
}
#container3 {
background: lightgray;
flex: 0 1 15%;
}
#canvas1 {
width: 100%;
height: 100%;
}

My main issue was the shrink value in the flex parameter. It allowed the top bar to vanish on decreasing the page size.
I didn't find a way to solve the issue of the canvas rendering resolution not exactly matching it's actual size, so lines sometimes still blur. Setting the canvas' shrink option to 0 mostly solves this, but the rest of the UI breaks then. I suspect it is caused by the Element actually having a non integer width which isn't represented by the clientWidth/offsetWidth parameters.

Related

Include my Javascript animation on all the html body so it stays while scrolling the page

Hi i'm learning html/css and javascript and I think I'm having an issue with my html structure. Basically what I want to do is that my particles animation stays on the website while scrolling the page. I have a Javascript file that does a getElementById('particles') to run the canvas on a div but it only stays on the first page.
I tried to move the "particles" div as a main div that will contain all the sections but it didn't work.
Here's the repository of the files if anyone is interested: https://github.com/DanielVillacis/DanielVillacis.github.io
Here's my html structure :
document.addEventListener('DOMContentLoaded', function() {
particleground(document.getElementById('particles'), {
dotColor: '#FFFFFF',
lineColor: '#FFFFFF'
});
var intro = document.getElementById('intro');
intro.style.marginTop = -intro.offsetHeight / 2 + 'px';
}, false);
html,
body {
width: 100%;
height: 100vh;
}
canvas {
display: inline-block;
vertical-align: baseline;
}
header,
section {
display: block;
}
#particles {
width: 100%;
height: 100vh;
overflow: hidden;
position: relative;
}
.container {
scroll-snap-type: y mandatory;
overflow-y: scroll;
height: 100vh;
}
section {
height: 100vh;
width: 100%;
display: inline-block;
scroll-snap-align: start;
}
<body>
<div class="container">
<main role="main">
<section class="intro">
<div id="particles">
<header class="splash" id="splash" role="banner">
<div id="intro">
</div>
</header>
</div>
</section>
<section class="AboutMe">
<div class="introduction">
</div>
</section>
<section class="box">
<div class="projectContainer">
</div>
</section>
<section class="Contact">
<h2 class="ContactTitle">Contact</h2>
<div class="contactLinks">
</div>
</section>
</main>
</div>
</body>
Use the CSS position: fixed; property.
With position set to fixed, your canvas is positioned relative to the viewport and hence would remain even while scrolling.
.pg-canvas {
display: block;
position: fixed;
top: 0;
left: 0;
pointer-events: none;
width: 100%;
height: 100vh;
}
You have put the particles (which are shown on a canvas) into a section which will scroll out of view.
The particles library you are using places this canvas just before the element you have given it, which has id particles.
You can fix just the canvas by adding position: fixed to the canvas selector in your style sheet (watch out if you have other canvases to give a more definite selector).
This will work in many cases to fix the canvas with the particles to the viewport. But note this description from MDN
The element is removed from the normal document flow, and no space is
created for the element in the page layout. It is positioned relative
to the initial containing block established by the viewport, except
when one of its ancestors has a transform, perspective, or filter
property set to something other than none (see the CSS Transforms
Spec), in which case that ancestor behaves as the containing block.
(Note that there are browser inconsistencies with perspective and
filter contributing to containing block formation.) Its final position
is determined by the values of top, right, bottom, and left.
You are OK at the moment because you move intro with top but if that were a translate you’d have to put the canvas out of intro.

Image max crop percentage

I'm building an responsive Image gallery using flexbox.
I want to make it respect aspect ratios, and still aim a 100% page with.
The term page in this case is equal to the parent container.
Parameters I want:
Aim for page with of 100% (exept don't force it if a image is upright and will be forced to crop more than 35% of the image)
If widescreen/landscape try to scale it to max-page width and still respect to not crop more than 35% of the image horizontal or vertical in total.
Max row height of around 25vh
-All rows don't need to have equal heights.
Problem
It's always cropping images way too much. So the main content is often not visible.
How can I avoid cropping it more than a defined percentage?
My approach so far:
<div className="App">
<div className="gallery__container">
{images.map((i) => (
<div className="gallery__image__container">
<img className="gallery__image" src={i} alt="ginger" />
</div>
))}
</div>
</div>
.gallery__container {
display: flex;
flex-wrap: wrap;
}
.gallery__image__container {
height: 25vh;
flex-grow: 1;
border: 3px solid transparent;
}
.gallery__image {
max-height: 100%;
min-width: 100%;
max-width: 100%;
object-fit: cover;
}
To clarify my problem I made a simplified example code sandbox:
https://codesandbox.io/s/adoring-spence-4qtc3?file=/src/App.js

Controlling size of a HTML element at fixed aspect ratio

None of the answers I've looked up quite works for my use case.
I want to fix the aspect ratio of a HTML element on my page (specifically, a canvas which I want to always be square) in such a way that
it's as large as fits in the alloted space;
it fits inside the browser window without scrolling.
I don't think this can be done by CSS alone (please prove me wrong). I did find various CSS tricks purported to do it, but they take into account only either the available width or the available height, not both.
I tried javascript that looks up the size (.parentElement.clientWidth and .parentElement.clientHeight) of the parent element, finds the smaller one and sizes the canvas according to that by setting canvas.style.width and canvas.style.height, but for some reason I don't understand this doesn't work - the canvas ends up too small and only resizes to the correct setting on a window resize. In this case, the parent element's size was determined by flexbox.
EDIT: the following code does almost what I am looking for. There is only one problem, which you can observe by:
loading the page in a non-maximed window
maximizing and un-maximizing that window.
The resulting layout is not the same as the original (directly after the load).
<!DOCTYPE html>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body {
margin: 0;
background: #404040;
height: 100vh;
min-width: 800px;
display: flex;
justify-content: center;
align-items: stretch;
}
main {
background: yellow;
flex: 0 1 100vh;
display: flex;
flex-direction: column;
}
.sidepanel {
background: blue;
flex: 0.2 0.4 250px;
text-align: center;
}
.spacer {
flex: 0.5 1 0;
}
#square {
background: black;
margin: auto;
}
</style>
<script type="module">
const canvas = document.querySelector('#square')
const parent = canvas.parentElement
const setSquare = () => {
const { clientWidth, clientHeight } = parent
const L = Math.min(clientWidth, clientHeight)
canvas.style.width = canvas.style.height = `${L}px`
}
window.addEventListener('resize', setSquare)
setSquare()
</script>
<div class="spacer"></div>
<section class="sidepanel">
</section>
<main>
<canvas id="square"></canvas>
</main>
<section class="sidepanel">
</section>
<div class="spacer"></div>

Get a Circles Width Defined by a Percent in Pixels and apply them to the Height Value

After 4 hours of trial and errors, I cannot find a way to get the width of the circle that is defined by a percent and convert it to pixels and make the circles height the same size as the width in pixels. Below is what I have right now. (I have tried many variations of this but cannot figure it out) Right now it only works on my screen. I try it on other devices and the height is just not right. This button is created onload.
Example: Circle Width = 12% , the Pixel Value of 12% on a screen is "70px". So somehow make Circle Height = 70px.
<!DOCTYPE html>
<head>
<title>Circle Test</title>
<meta charset='UTF-8'>
<script type="text/javascript" src="/js/fs"></script>
</head>
<body>
</body>
</html>
// Creates a button
var mainButton = document.createElement("button");
mainButton.style.width = "10%";
// Get the Screens Avaliable Width and Get 10% of it and convert it to pixels
var wad = screen.availWidth * .1 + "px";
// Thinking this would return the pixel amount the circle button is, but it only works on the regular screen and not when resized. It also does not work for mobile.
console.log(wad);
mainButton.style.height = wad;
You set the width of your button to 10% of its containing block's width, and the height to 10% of the width of one of
The available area of the rendering surface of the output device, in CSS pixels.
The area of the output device, in CSS pixels.
The area of the viewport, in CSS pixels.
It's very likely that these are measurements of two different things, or that resizing the window will affect one but not the other. Fortunately, CSS has a unit for "percentage of the window's width": vw. Set your button's height and width in vw units, and you don't need any JavaScript:
button {
width: 10vw;
height: 10vw;
border-radius: 50%;
}
<button>Go</button>
If you really meant that the button is 10% as wide as its container, even if its container isn't as wide as the whole window, you can use the padding-bottom technique detailed in this answer. Unlike height, percentages in padding refer to the width of the containing block:
.wrapper {
width: 40%; /* here I use 40% instead of 10% for aesthetics */
padding-bottom: 40%; /* should match width */
position: relative;
}
.wrapper > button {
display: block;
position: absolute;
width: 100%;
top: 0;
bottom: 0;
border-radius: 50%;
}
/* the rest is just to make figuring out
the exact width of the button difficult */
body {
display: flex;
align-items: stretch;
height: 90vh;
}
body > div {
flex: 1 1 0;
background: rebeccapurple;
margin: 0 4px;
}
<div>
<div class="wrapper"><button>Go</button></div>
</div>
<div></div>
<div></div>
And finally, just for completeness, if you really must get the computed width from JavaScript, you can use window.getComputedStyle:
let button = document.querySelector('button');
let width = window
.getComputedStyle(button)
.getPropertyValue('width');
console.log(button.style.height = /* DO NOT DO THIS */ width);
button {
width: 40%;
border-radius: 50%;
}
/* the rest is just to make figuring out
the exact width of the button difficult */
body {
display: flex;
align-items: stretch;
height: 90vh;
}
body > div {
flex: 1 1 0;
background: rebeccapurple;
margin: 0 4px;
}
<div>
<button>Go</button>
</div>
<div></div>
<div></div>
If you try putting that into a resize handler, though, performance will suffer.

Align div with first vertically center div

I have a page, where I'm showing images side by side according to the category they belong to, each image array begins with the category it belongs to. Images vary in their width & height, but are put into a div with an absolute height of 330px.
CSS:
.front-index {
margin: 0 1em 0 2em;
}
.front-work {
margin: 0 2em 2em 0;
display: table;
width: 66px;
position: relative;
height: 330px;
background-color:#f0f0f0;
}
.front-work img {
margin: .3em 0 .3em 0;
}
.wraptocenter {
display: table-cell;
text-align: center;
vertical-align: middle;
}
.wraptocenter * {
vertical-align: middle;
}
.front-index,
.front-work {
float: left;
}
HTML:
<div class="front-index"><p>How to get<br /> this text on the same line with<br /> yellow image on the right?</p></div>
<div class="front-work">
<div class="wraptocenter">
<img width="162" height="250" src="http://images.fanpop.com/images/image_uploads/Yellow-Wallpaper-yellow-646738_800_600.jpg"/>
</div>
</div>
<div class="front-work">
<div class="wraptocenter">
<img width="250" height="166" src="http://www.takenseriouslyamusing.com/wp-content/uploads/2012/08/Blue.png"/>
</div>
</div>
…
JSFIDDLE: http://jsfiddle.net/rCAKa/9/
I'd like to align the text to the same line as the first image on the right.
What I had in mind, is that may-be this should be done in jquery. Ie. somehow measure the image distance from the top inside the .front-work div and then assign the value to the .front-index div as an inline code (top: whatever px ).
Maybe someone of you have faced this kind of problem and know a solution to this kind of problem? CSS or JS.
In my humble opinion I don't think that what you're doing is possible through CSS - it requires some simple JavaScript trickery because you have to know the relative position (from the top of the container) of the first image on the right in order to position the text - something which CSS isn't quite designed for.
The strategy in JS would be:
Loop through each element with text that you want to position
Fetch the vertical top offset of the first image to the right (relative to containing parent)
Set top padding matching to top position of image. Alternatively, you can set the top position, paddings or margins of the child elements, or other ways to reposition the text.
$(document).ready(function() {
$(".front-index").each(function() {
var fromTop = $(this).next().find("img").position().top;
$(this).css({
"padding-top":fromTop
});
});
});
I have forked your fiddle, and you can see it in action here - http://jsfiddle.net/teddyrised/LT54V/1/
p/s: On a related note, .wraptocenter * { } is probably not the best (as in, most efficient) selector out there, because if you have many child elements in the element (who may or may have even more child elements), CSS will have to iterate through all of them. Instead, try using .wraptocenter > * { } or just .wraptocenter img { } :)
I first tried to solve the problem using css. After a while I figured out the following logics:
Create a div with the same height as the cell on the right with the display set as table
Make a table-cell div in the first one that centers vertically
In this div make another subdiv with the same height as the image.
The HTML code is then this:
<div class="front-index">
<div class="front-index-inner">
<div style="height:250px;">
<p>How to get<br /> this text on the same line with<br /> yellow image on the right?</p>
</div>
</div>
</div>
and as my CSS part this:
.front-index {
margin: 0 1em 0 2em;
display: table;
height: 330px;
overflow: hidden;
}
.front-index-inner {
display: table-cell;
width: 100%;
vertical-align: middle;
}
You can see the result over here: http://jsfiddle.net/rCAKa/10/
I hope this brings a solution to you that is clear, understandable and useful.
Greetings,
Jef

Categories