Background-size cover jumping when background-position switches to fixed - css

I am working on a Parallax/Scrolling Timeline project and I am having a problem with the CSS3 Background-size cover property.
The div has these properties:
background: url(../images/timeline/back-6.jpg) no-repeat top center black;
background-size: cover;
padding-top: 90px;
height: 1855px;
position: relative;
Using jQuery I switch the background-attachment to fixed. When I do this the background image jumps "in" (meaning that parts of the image that were past the edge of the screen are now visible). Which isn't the desired result.
In testing I can switch the div to use background-size: 100% cover but it is causing different vertical jumping issues when scrolling.
Any ideas of how to prevent it from jumping in when I switch the background to fixed? (It also happens in reverse when I set the background to scroll).
I sadly can't link to a demo of this code as the page isn't ready to be deployed yet.

I had the same issue, when setting background-size to cover or contain
Setting a fixed height, in example for smaller screens via #media prevents the background-image from jumping. After my tests I came to the conclusion, that the jumping is due to the orientation of the element after setting background-attachment to fixed
Setting it to fixed, the size is calculated by the size of the viewport, not the element containing the background-image. This is where the jumping comes from and why setting a fixed height or width for the background-size solves this issue.

I had the same problem while creating a one page layout i wanted to use with a scrollTo-Plugin and so on....
The page layout was devided in two parts:
Left side for the background image which should change/scroll with the content on the right side.
So i used to make a kind of jquery Plugin to combine both "background-position: fixed" and "background-size: cover".
you just need to define the element by class/id for aligning the background-images.
dont complain about the code. im relatively new to javascript/jquery. but its working ;)
there it is:
function fixedResize() {
var targetEl = $('element where bg-images are in');
var targetWidth = targetEl.width();
var targetHeight = targetEl.height();
var targetPosX = targetEl.offset().left;
var targetPosY = targetEl.offset().top;
var leftRatio = targetWidth / targetHeight;
//console.log('TargetWidth', targetWidth, 'TargetHeight', targetHeight, 'Offset', targetPosX, targetPosY, 'leftRatio', leftRatio);
targetEl.each(function(){
var imgTarget = $(this);
var url = $(this).css('background-image').replace('url(', '').replace(')', '').replace("'", '').replace('"', '');
var bgImg = $('<img />'); // make background-image as image tag for getting width and height of the image
imgTarget.css('background-attachment','fixed');
bgImg.hide();
bgImg.bind('load', function(){
var imgHeight = $(this).height();
var imgWidth = $(this).width();
var imgRatio = imgWidth / imgHeight;
$(this).remove(); // remove img Tags again
// Calculate resize dimensions
if (imgRatio > leftRatio) {
var currentWidth = imgRatio * targetHeight; // image width after resize
var currentHeight = (currentWidth/imgWidth)*imgHeight;
var setToLeft = ((currentWidth - targetWidth)/2);
var imgPosX = targetPosX - setToLeft;
var imgPosY = (currentHeight - targetPosY - currentHeight/2 - targetHeight/2)* -1;
var resizeImg = 'background-size: auto '+ targetHeight +'px;';
} else if (imgRatio < leftRatio){
var currentWidth = targetWidth;
var currentHeight = (currentWidth/imgWidth)*imgHeight;
var imgPosX = targetPosX;
var imgPosY = (currentHeight - targetPosY - currentHeight/2 - targetHeight/2)* -1;
var resizeImg = 'background-size: '+ targetWidth +'px auto;'; // resize background
}
imgTarget.attr('style','background-attachment: fixed; background-position: '+ imgPosX +'px '+ imgPosY +'px;' + resizeImg);
console.log('imgWidth', imgWidth, 'imgHeight', imgHeight, 'imgRatio', imgRatio, 'currentWidth', currentWidth, 'currentHeight', currentHeight, 'setToLeft', setToLeft);
console.log('imgPos', imgPosX, imgPosY, 'setToLeft', setToLeft, targetPosX);
});
$(this).append(bgImg);
bgImg.attr('src', url);
});
}
fixedResize(); // initiate function
$(window).resize(function() {
fixedResize(); // initiate function for window resize (Fluid behavior)
});
or
jsfiddle.net/rowphant/eXb6e/14/

Related

CSS: Element fixed to viewport height, scaled proportionally, centered

I have elements which have a set height of 100% viewport height. Inside of these are a background image which is also fixed to the same height - so the image's top and bottom are always visible, it's centered, and sometimes it's edges get cut off.
What I'm trying to add on top of this is another element which matches the same behavior of the background image (to place other things over the image, but ensure they always line up).
I have a working example using Javascript, but wondering if this same behavior could be replicated with CSS. The pinkish box is the element which I am scaling based on the viewport's size and it should always match up with the background image behind it.
https://jsfiddle.net/louiswalch/p1rkohzt/
And all the scaling logic is as follows:
var $window = $(window);
var base_width = 1600;
var base_height = 960;
var base_ratio = (base_width / base_height);
var contents = $('SECTION .content');
$window.on('resize', function() {
var window_width = $window.width();
var window_height = $window.height();
var window_ratio = (window_height / window_height);
var scaled_width = (window_height * 100/base_height) * base_width/100;
contents.css({
width: (scaled_width+'px'),
height: (window_height+'px'),
marginLeft: ('-'+scaled_width/2+'px'),
});
}).trigger('resize');

Full Height gmap in primefaces

I'm using primefaces ronin theme and I'm trying to make a full screen gmap no matter what the resolution of the screen is. how ever, unless I state the height in pixels, it won't work.
for example, if i set the height css attribute of the map to 100%, it doesn't show, and if I wrap the gmap with a div container with 100% height, it still doesn't work. how can I make a full screen responsive gmap?
You can show a full screen responsive p:gmap on following way:
Lets assume that p:gmap is defined like this (no need for style attribute)
<p:gmap id="gmap" center="41.381542, 2.122893" zoom="13" type="hybrid" />
Place following JavaScript on your page that will do the trick
<script>
function resizeElement(elementId,width,height){
console.log("Resizing element " + elementId + " W/H="+ width + "/" + height);
var element = document.getElementById(elementId);
element.style.width=width+"px";
element.style.height=height+"px"
}
function resizePfGmapFullScreen() {
var width = window.innerWidth - 20;
var height = window.innerHeight - 20;
resizeElement("gmap", width, height);
}
window.onload = function() {
window.dispatchEvent(new Event('resize'));
};
window.onresize = function(event) {
console.log("Screen is resized");
resizePfGmapFullScreen();
};
</script>
Advantage of this solution is that p:map will be automatically resized when screen is resized including screen orientation change on mobile devices.
It is tested running on the latest Primefaces version 6.1.

CSS: Make Canvas as big as possible while keeping aspect ratio

I have a Canvas element that is inside a container div. When the user selects an image from his machine, this image should be displayed on the canvas. I want the canvas to be big as possible but at the same time keep the aspect ratio of the image. I know neither the proportions of the image nor the size of the container div, as this is relative to the screen/window size of the user.
If I set max-width and max-height to e.g 100% the canvas will not fill the container if the selected image is smaller then the container. If I set width and height instead of max-width and max-height the canvas doesn't keep the aspect ratio.
Does anyone have an idea how to solve this?
If you're willing to use JQuery (or regular JavaScript), then a solution like this might work:
<script>
// Note: this uses jQuery.
// It makes getting/setting the dimensions easier,
// but you can do this with normal JavaScript
var img = $("#img");
var container = $("#container");
var width = img.width();
var height = img.height();
var maxWidth = container.width();
var maxHeight = container.height();
var ratio = maxWidth / width;
if(height * ratio > maxHeight) {
ratio = maxHeight / height;
}
img.width(width * ratio);
img.height(height * ratio);
</script>
What this does is that it finds the ratio to multiply the width and the height by, whichever one is smaller (so that it will always fit in the window).
Update: Tested on JSFiddle.net. See it here.
I hope this helps you!
After reading your clarification about video, check out the following:
https://jsfiddle.net/d0dox9xt/
body {
background: #eee;
}
#container {
margin:0 2% 0 2%;
}
#v {
position:absolute;
top:0;
left:0;
width:100%;
}
The only trick is setting width:100%; This will maintain aspect ratio.
Note that in the JS
document.addEventListener('DOMContentLoaded', function(){
var v = document.getElementById('v');
var canvas = document.getElementById('c');
var context = canvas.getContext('2d');
}
function draw(v) {
c.drawImage(v, 0, 0);
}
The drawImage function can take many arguments. The first argument is inserting the media, the next two are for positioning. There are many arguments you can have to position and change the height and width. I left them alone so it will follow the CSS rules.
Here is a link to more on placing in canvas: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage

Delete white spaces on html5 video tag in Ionic

I have a video tag in my Ionic app, video element is added after click on a button.
function addVideo(videoId){
var path = $scope.getVideo(videoId).newVideoLocation.nativeURL;
path = $sce.trustAsResourceUrl(path);
var container = document.getElementById('videoContainer' + videoId);
var video = document.createElement('video');
video.src = path;
video.setAttribute('id', 'video' + videoId);
video.setAttribute('poster', $scope.getVideo(videoId).thumbnailPath);
video.setAttribute('width', '100%');
container.appendChild(video);
};
Video is added successfully but there are bottom and top white spaces / bars:
After click play button spaces are no longer there:
I set border to all elements to know what is happening. Blue border is video tag:
It could be margin o padding however I set them to 0:
* {
border: 1px solid red !important;
}
video {
border: 2px solid blue !important;
margin: 0;
padding: 0;
}
Any idea what is the problem?
After a lot of research I found a solution.
I start understanding what happened after read HTML 5 Video stretch post:
Video content should be rendered inside the element's playback area
such that the video content is shown centered in the playback area at
the largest possible size that fits completely within it, with the
video content's aspect ratio being preserved. Thus, if the aspect
ratio of the playback area does not match the aspect ratio of the
video, the video will be shown letterboxed or pillarboxed. Areas of
the element's playback area that do not contain the video represent
nothing.
Then in google books I found and explanation how works video width, the book is call The Definitive Guide to HTML5 Video
If width and height are not same aspect ratio as original video it doesn't work. Set a 100% to width doesn't mean you want video to fit the container. So I decided to calculate width and height of the container and set to video element:
function addVideo(videoId){
var path = getTrustUrl($scope.getVideo(videoId).newVideoLocation.nativeURL);
// Create container element and get padding
var container = document.getElementById('videoContainer' + videoId);
var paddingLeft = window.getComputedStyle(container, null).getPropertyValue('padding-left');
var paddingRight = window.getComputedStyle(container, null).getPropertyValue('padding-right');
// Get only numeric part and parse to integer
paddingLeft = parseInt(paddingLeft.slice(0,-2));
paddingRight = parseInt(paddingRight.slice(0,-2));
//Get internal width of container and calculate height
var width = container.offsetWidth - paddingLeft - paddingRight;
var height = (width * 9 ) / 16; // TODO, if not 16:9 error
// Create video element and set attributes
var video = document.createElement('video');
video.src = path;
video.setAttribute('id', 'video' + videoId);
video.setAttribute('poster', $scope.getVideo(videoId).thumbnailPath);
video.setAttribute('width', width);
video.setAttribute('height', height);
// Append video to container
container.appendChild(video);
};
I don't see it straightforward... if someone know another solution let me know!

How to position an element absolute to the window regardless of its DOM position?

I am creating a pop-up overlay modal and am having problems getting the positioning/scrolling working correctly.
I can set my modal to be position:fixed but then if the modal's height is too much, then the modal overflows off of the window and you cannot see the bottom of it.
If I set the modal to be position:absolute then the element becomes positioned relative to the closest ancestor with position:relative, correct? (or at least thats what it appears to do) Instead I want the modal to ALWAYS be relative to the window so that I can center it easily.
Is there a way to make the below .modal positioned relative to the window ( or element) even if the element is nested deep inside the DOM like this:
<body ng-app="myapp" ng-controller="mycontroller">
<div>
<div>
<div ui-view>
<div class=".modal"></div>
</div>
</div>
</div>
</body>
If you insist on having it in that same markup and nested in the same manner, your best bet is in JavaScript.
Here's some JS code that gives a good method of accomplishing what you asked for:
function ShowDivInCenter()
{
try
{
divWidth = 100;
divHeight = 100;
divId = 'divLogin'; // id of the div that you want to show in center
// Get the x and y coordinates of the center in output browser's window
var centerX, centerY;
if (self.innerHeight)
{
centerX = self.innerWidth;
centerY = self.innerHeight;
}
else if (document.documentElement && document.documentElement.clientHeight)
{
centerX = document.documentElement.clientWidth;
centerY = document.documentElement.clientHeight;
}
else if (document.body)
{
centerX = document.body.clientWidth;
centerY = document.body.clientHeight;
}
var offsetLeft = (centerX - divWidth) / 2;
var offsetTop = (centerY - divHeight) / 2;
// The initial width and height of the div can be set in the
// style sheet with display:none; divid is passed as an argument to // the function
var ojbDiv = document.getElementById(divId);
ojbDiv.style.position = 'absolute';
ojbDiv.style.top = offsetTop + 'px';
ojbDiv.style.left = offsetLeft + 'px';
ojbDiv.style.display = "block";
}
catch (e) {}
}
You can then call the function through any event, for example:
<body onload='ShowDivInCenter();' onresize='ShowDivInCenter();'>
if you want it to be dynamic.

Resources