Scaling Divs Perfectly Within Background-Size: Cover Or Alternatives - javascript

I am trying to create a full screen responsive background image (like css background-size:cover) that contains divs that keep there exact position on the image whilst being scaled via a browser resize.
<div class="full_Screen_background">
<div class="image_to_fill_and_scale_fullscreen">
<div class="content_to_be_scaled_with_window_resizing">
</div>
</div>
</div>
And the css:
.content_to_be_scaled_with_window_resizing{
border:5px solid #000;
position:absolute;
top:30%;
left:40%;
height:10%;
width:10%;
}
.image_to_fill_and_scale_fullscreen{
background: url(../4706825697_c0367e6dee_b.jpg);
position: relative;
background-size: cover;
background-position: center;
background-attachment: scroll;
height: 100vh;
background-repeat: no-repeat;
}
The desired outcome is to have the div perfectly scale proportionally with the image as the browser is resized.
Any ideas would be much appreciated?
https://jsfiddle.net/sfLgvyz0/

I tried it with a canvas. Hope that works for you. I fixed the rectangle to the head of the biker.
window.onload = drawFullImage;
window.onresize = drawFullImage;
function drawFullImage() {
var c = document.getElementById("myCanvas");
var imageDiv = c.parentNode;
c.width = imageDiv.getBoundingClientRect().width;
c.height = imageDiv.getBoundingClientRect().height;
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
var img = document.getElementById("imagetorender");
ctx.drawImage(img, 0, 0, c.width, c.height);
ctx.strokeRect(c.width / 100 * 75, c.height / 100 * 30, c.width / 100 * 10, c.height / 100 * 10);
}
body {
width: 100vw;
position: relative;
margin: 0;
}
#imagetorender {
visibility: hidden;
max-height: 100%;
width: 100%;
}
#myCanvas {
position: absolute;
top: 0;
left: 0;
}
<div id="wrapper">
<img id="imagetorender" src="http://farm5.static.flickr.com/4005/4706825697_c0367e6dee_b.jpg" />
<canvas id="myCanvas"></canvas>
</div>
Edit: I used a hidden image to get the aspect ratio right.

Related

Dynamically assign background of div to blend in with background of body

I have a grid of divs, each of which rotate to reveal another div. Currently the "front" divs have no background, but this makes the transition pretty bad (nothing happens, text goes away, backface is revealed). I'd like to assign them a background based on the portion of background they cover so that they will blend in but change when rotating. I'm not sure how to get the correct location of the background image.
HTML:
<section class='grid-1'>
<div class='day-1'>
<label>
<div class='door'>
<div class='front'>1</div>
<div class='back'></div>
</div>
</label>
</div>
</section>
CSS:
body{
background: url(...) no-repeat top center;
background-size: cover;
}
.day-1 .front{
background: url(...) no-repeat var(--offset-x) var(--offset-y);
}
JavaScript:
const setBgTest = () => {
const img = new Image();
img.src = [...];
const days = document.getElementsByClassName('day-1');
const day = days[0];
const dayRect = day.getBoundingClientRect();
const bodyRect = document.body.getBoundingClientRect();
const offX = ((dayRect.x-bodyRect.x)/document.body.clientWidth)*img.naturalWidth;
const offY = ((dayRect.y-bodyRect.y)/document.body.clientHeight)*img.naturalHeight;
}
setBgTest();
(This will be turned into a loop for all days and invoked on window resize)
This doesn't work, but I'm not sure how to calculate the correct offsets.
Updated: Calculates percentage offset within body and uses that percentage of original image size. It's close, but not quite right.
New answer
Well, you just added background-size: cover, so it's now clear your background size is dynamic. Since javascript can't know directly the size of the background you have to mimic the cover behaviour yourself, this can be achieved with the following steps:
download the original image through javascript to know its original size;
compute the scaling to let the image fit in the window;
apply the scaling to all the doors background along with the offset as in my previous answer.
Here is the full code:
$(function() {
var background = $('body');
var bgImg = background.css('background-image').replace(/^url\(['"](.+)['"]\)/, '$1');
const img = new Image();
$(img).load(function() {
var resizeHandler = function() {
var bgw = window.innerWidth;
var bgh = bgw * img.height / img.width;
background.css('background-size', bgw+'px '+bgh+'px');
var bgPos = background.offset();
bgPos.left -= parseInt(background.css('marginLeft'), 10);
bgPos.top -= parseInt(background.css('marginTop'), 10);
$('.day-1 .front').each(function() {
var elem = $(this);
var pos = elem.offset();
var x = pos.left - bgPos.left;
var y = pos.top - bgPos.top;
elem.css('background-position', (-x)+'px '+(-y)+'px');
elem.css('background-size', bgw+'px '+bgh+'px');
});
};
$(window).resize(resizeHandler);
resizeHandler();
});
img.src = bgImg;
});
body {
background: url('https://i.ibb.co/d2DJp02/wallpaper-2.jpg') no-repeat left top;
background-size: cover;
position: relative;
}
.door {
border: 1px solid #ff0000;
position: relative;
width: 60px;
height: 120px;
}
.door>div {
position: absolute;
width: 60px;
height: 120px;
}
.day-1 {
position: absolute;
left: 80px;
top: 90px;
}
.day-1 .front {
background: url('https://i.ibb.co/d2DJp02/wallpaper-2.jpg') no-repeat;
color: red;
text-align: center;
font-size: x-large;
font-weight: bold;
border: 1px solid black;
}
.door:hover .front {
transform-origin: top left;
transform: rotateY(85deg);
transition: transform .8s ease-in-out;
}
.day-1 .back {
background: black url('https://i.ibb.co/rZW5T2v/dog.jpg') no-repeat center center;
background-size: contain;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<section class='grid-1'>
<div class='day-1'>
<label>
<div class='door'>
<div class='back'></div>
<div class='front'>1</div>
</div>
</label>
</div>
</section>
Old answer
I've arranged this code using jquery, but you can rewrite using pure javascript. You can use the "offset" method to know where an element is within the document. If you subtract the door coordinates from the main element (the one which contains the whole background) coordinates, then you get the relative coordinates. Then just apply them as negative background-position:
$(function() {
var background = $('.grid-1');
var bgPos = background.offset();
var elem = $('.day-1 .front');
var elemPos = elem.offset();
var relPos = {left: elemPos.left - bgPos.left, top: elemPos.top - bgPos.top};
elem.css('background-position', (-relPos.left)+'px '+(-relPos.top)+'px');
});
.grid-1 {
height: 300px;
background: url('https://i.ibb.co/d2DJp02/wallpaper-2.jpg') no-repeat left top;
background-size: 400px 300px;
position: relative;
}
.door {
border: 3px solid #ff0000;
position: relative;
width: 60px;
height: 120px;
}
.door>div {
position: absolute;
width: 60px;
height: 120px;
}
.day-1 {
position: absolute;
left: 80px;
top: 90px;
}
.day-1 .front {
background: url('https://i.ibb.co/d2DJp02/wallpaper-2.jpg') no-repeat;
background-size: 400px 300px;
color: red;
text-align: center;
font-size: x-large;
font-weight: bold;
border: 1px solid black;
}
.door:hover .front {
transform-origin: top left;
transform: rotateY(85deg);
transition: transform .8s ease-in-out;
}
.day-1 .back {
background: url('https://i.ibb.co/rZW5T2v/dog.jpg') no-repeat center center;
background-size: contain;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<section class='grid-1'>
<div class='day-1'>
<label>
<div class='door'>
<div class='back'></div>
<div class='front'>1</div>
</div>
</label>
</div>
</section>

Parallax with mouse

I'm trying to 'transform' elements, img here when the mouse moves on the page. I have integrated a vanilla code to create this effect and thought I understoond it but it seems I was wrong. The element from the code snippet is the orange square (3.png), but I want to apply this effect on the human pic (2.png) behind aswell and can't figure out how. (here's the full code as I don't rly know what's messed up except for my whole architecture prbly: https://github.com/KPq66dw8L/b-code-fiverr)
<section class="container bot-container-img">
<img class="layer closeUp" src="images/1.png" data-speeed="2" alt="">
<img class="layer ellipse2" src="images/2.png" data-speeed="-5" alt="">
<img class="layer" src="images/images/3.png" data-speed="2" alt="">
</section>
CSS:
.bot-container-img {
grid-row-start: 3;
grid-column-start: 1;
grid-column-end: 3;
width: 100%;
height: 100%;
}
section {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
align-items: flex-end;
justify-content: flex-start;
}
section img {
position: absolute;
object-fit: cover;
width: 100%;
height: 100%;
}
JS:
document.addEventListener("mousemove", parallax);
function parallax(e){
this.querySelectorAll('.layer').forEach(layer => {
const speed = layer.getAttribute('data-speed')
const x = (window.innerWidth - e.pageX*speed)/100
const y = (window.innerHeight - e.pageY*speed)/100
layer.style.transform = `translateX(${x}px) translateY(${y}px)`
})
}
There were a few minor errors I corrected:
typo data-speeed="2" corrected to data-speed="2"
I preferred getBoundingClientRect() over window.innerWidth / window.innerHeight
The items being effected by parallax are centered using left, top, width, height, and negative margin-left and margin-top - this allows the transform.translate property to translate them relative to their centers
I wrapped all the logic into a nice applyParallax function, in case you wish to apply it to multiple section elements
I also had to make some changes to get this to work with stackoverflow's snippet system:
Instead of <img> I used <div class="img"></div>, with css to colour different div.img elements distinctly
I decreased the size of the div.img elements to make the effect more visible in the small window
I increased the values for data-speed to make the effect more obvious
I made the html and body elements fill the whole viewport (and the section element fills the whole body element)
let applyParallax = section => {
section.addEventListener('mousemove', e => {
let { width, height } = section.getBoundingClientRect();
let offX = e.pageX - (width * 0.5);
let offY = e.pageY - (height * 0.5);
for (let layer of document.querySelectorAll('.img')) {
const speed = layer.getAttribute('data-speed')
const x = (offX * speed) / 100;
const y = (offY * speed) / 100;
layer.style.transform = `translateX(${x}px) translateY(${y}px)`
}
});
section.addEventListener('mouseleave', e => {
for (let layer of document.querySelectorAll('.img')) {
layer.style.transform = `translateX(0px) translateY(0px)`
}
});
};
applyParallax(document.querySelector('section'));
html, body { position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; }
section {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
align-items: flex-end;
justify-content: flex-start;
}
section > .img {
position: absolute;
left: 50%; top: 50%;
width: 120px; height: 120px;
margin-left: -60px; margin-top: -60px;
}
section > .img.r { background-color: rgba(200, 0, 0, 0.5); }
section > .img.g { background-color: rgba(0, 200, 0, 0.4); }
section > .img.b { background-color: rgba(0, 0, 200, 0.3); }
<section class="container bot-container-img">
<div class="img r" data-speed="22"></div>
<div class="img g" data-speed="-5"></div>
<div class="img b" data-speed="32"></div>
</section>

Save the original image + draggable blur mask applied to it

I was wondering if there is any way to save image original + Draggable blur mask over image
Here is an example of a draggable blur mask over the image : https://codepen.io/netsi1964/pen/AXRabW
$(function() {
$("#mask").draggable({
containment: "parent"
});
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#mask {
width: 50px;
height: 50px;
border-radius: 100%;
overflow: hidden;
position: absolute;
top: calc(50% - 25px);
left: calc(50% - 25px);
}
#unblurred {
position: absolute;
top: 0;
left: 0;
z-index: 999;
width: 100%;
height: 100%;
overflow: hidden;
-webkit-filter: blur(0px);
}
#unblurred img {
position: fixed;
left: 0;
top: 0;
}
#blurred {
-webkit-filter: blur(20px);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<div id="mask">
<div id="unblurred">
<img src="https://i.ytimg.com/vi/LRVsxe5OJVY/maxresdefault.jpg">
</div>
</div>
<img id="blurred" src="https://i.ytimg.com/vi/LRVsxe5OJVY/maxresdefault.jpg">
Wanted to save image with draggable blur mask over image... Maybe using canvas or something of the type
I think that I have a some what working solution, here is the JS code:
function saveMask() {
$("#blurred").hide()
html2canvas(document.querySelector("#mask"), {allowTaint: true}).then(h2c => {
var pos = $("#mask")[0].getBoundingClientRect();
$("#mask").hide()
var image = document.getElementById('blurred');
var canvas = document.createElement("canvas");
canvas.height = image.height;
canvas.width = image.width;
var ctx = canvas.getContext('2d')
ctx.filter = 'blur(20px)'
ctx.drawImage(image, 0, 0);
ctx.filter = 'none'
ctx.drawImage(h2c, pos.x, pos.y);
document.body.appendChild(canvas);
});
}
My idea here is to get as the mask using html2canvas and then we create a canvas with the blurred image and "paste" the mask on top of that.
I have a fully functional example here:
https://raw.githack.com/heldersepu/hs-scripts/master/HTML/html2canvas.html

placing two canvases on top of eachother CSS

I'm trying to place 2 canvases ontop of eachother, whilst at the same time having them centered like this, but I suspect that one canvas is out of frame.
CSS:
body { background-color: #000000; text-align: center; margin: 0px; padding: 0px; width:100%; height:100%; }
* { margin:0; padding:0; }
canvas { display:block; padding: 0; margin: auto; position: absolute; top: 0; bottom: 0; left: 0; right: 0; }
cnv1 { z-index: 2; }
cnv2 { z-index: 1; }
HTML:
<canvas id="cnv1">U no do HTML5, fix.</canvas>
<canvas id="cnv2">U no do HTML5, fix.</canvas>
Javascript:
var cnv = document.getElementById('cnv1')
var ctx = cnv.getContext('2d');
var cnv2 = document.getElementById('cnv2')
var ctx2 = cnv2.getContext('2d');
If I now try to write things like
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.fillText('images loaded and ready to go', 180, 45);
then that won't show up. but writing to ctx2 will.
OK, so turns out it was just me being tired. I just forgot to add # to the cnv1 & cnv2 parts of the css:
#cnv1 { z-index: 2; }
#cnv2 { z-index: 1; }
It works as intended now. Thank you all for your time.
Is it something like this you want? The back layer and front layer now overlap.
Only thing I had to do for it was to reverse the order in which they appear in the html.
I have no idea why the z-index didn't work/wasn't obeyed. But reversing their order of appearance worked good.
var cnv = document.getElementById('cnv1')
var ctx = cnv.getContext('2d');
var cnv2 = document.getElementById('cnv2')
var ctx2 = cnv2.getContext('2d');
ctx.font = "30px Arial";
ctx.fillStyle = "red";
ctx.fillText('images loaded and ready to go', 25, 95);
ctx2.save();
//ctx2.globalAlpha = 0.5;
ctx2.clearRect(0,0,ctx2.width,ctx2.height);
ctx2.fillStyle="#eeeeee";
ctx2.fillRect(0,0,50,200);
ctx2.fillRect(50,50,50,150);
ctx2.fillRect(100,0,25,200);
ctx2.fillRect(150,50,50,150);
ctx2.fillStyle="#202020";
ctx2.fillText('Gotham City.', 20, 128);
body {
background-color: #000000;
text-align: center;
margin: 0px;
padding: 0px;
width:100%;
height:100%; }
* {
margin:0;
padding:0;
}
canvas {
display:block;
padding: 0;
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color:transparent;
}
#cnv1 {
z-index: 2;
}
#cnv2 {
z-index: 1;
}
<canvas id="cnv2">U no do HTML5, fix.</canvas>
<canvas id="cnv1">U no do HTML5, fix.</canvas>

maintain aspect ratio of a child div with both height and width changes of the parent

I want to create a div that maintains aspect ratio with height and width changes of the parent.
In the above gif, you can see that I was able to maintain the box div's aspect ratio when changing width but I'm unable to maintain it when changing height of the parent.
.box {
width: 100px;
background-color: #dfdfdf;
max-width: 100%;
}
.box:after {
content: '';
display: block;
width: 100%;
height: 100%;
padding-bottom: 160%;
}
.wrap9 {
width: 125px;
height: 190px;
}
<div class="wrap9">
<div class="box"></div>
</div>
I want the grey box to behave like the following:
With your default width and height:
.box {
width: 100px;
background-color: #dfdfdf;
max-width: 100%;
max-height:100%;
}
.box:after {
content: '';
display: block;
width: 100%;
height: 100%;
padding-bottom: 160%;
}
.wrap9 {
width: 150px;
height: 200px;
border: 1px solid black;
}
<div class="wrap9">
<div class="box"></div>
</div>
width lower width and height:
.box {
width: 100px;
background-color: #dfdfdf;
max-width: 100%;
max-height:100%;
}
.box:after {
content: '';
display: block;
width: 100%;
height: 100%;
padding-bottom: 160%;
}
.wrap9 {
width: 100px;
height: 120px;
border: 1px solid black;
}
<div class="wrap9">
<div class="box"></div>
</div>
Is this the desired effect?
I'd have to say that this is not currently possible. While you can use calc() and variables, there is no way to keep a dynamic reference to object's current width nor height.
When I looked at doing this with js there was no clean way either. You'll just have to write an interval to periodically check if width or height of parent element has changed.
Google is pushing a dom element observer but the spec is currently very limited.
The funny thing is, just like in your example, images do this by default. However, there seems to be absolutely no clean way of doing this with another element.
You can use JavaScript to calculate the ratio of the width and the height between you object and the parent object. Then your can either scale to FILL the parent object (think CSS background-size: contain;) or scale to FIT the parent object (CSS: background-size: cover;)
Example of the two type of scaling methods.
(A) The left shows SCALE-TO-FILL and (B) on the right we are using SCALE-TO-FIT.
// Find the ratio between the destination width and source width.
RATIO-X = DESTINATION-WIDTH / SOURCE-WIDTH
// Find the ratio between the destination height and source height.
RATIO-Y = DESTINATION-HEIGHT / SOURCE-HEIGHT
// To use the SCALE TO FILL method,
// we use the greater of the two ratios,
// let's assume the RATIO-X is greater than RATIO-Y.
SCALE-W = RATIO-X * SOURCE-WIDTH
SCALE-H = RATIO-X * SOURCE-HEIGHT
// To use the SCALE TO FIT method,
// we use the lesser of the two ratios,
// let's assume the RATIO-Y is less than RATIO-X.
SCALE-W = RATIO-Y * SOURCE-WIDTH
SCALE-H = RATIO-Y * SOURCE-HEIGHT
When your parent object scales, you will need to determine these ratios.
Take a look at the embedded example and you can see how it works.
var $parent = $('.parent'),
$child = $('.child'),
w = $parent.width(),
h = $parent.height(),
cw = $child.width(),
ch = $child.height(),
winc = -10,
hinc = -5;
/*
This function is what you need.{
First we grab the ratio for the width (rx).
Then the ratio for the height (ry).
To scale to FIT, we want the MIN of the two.
To scale to FILL, we want the MAX of the two.
r is used to calculate the new size for ther child obj.
*/
function calcChildSize() {
var rx = w / cw,
ry = h / ch,
r1 = Math.min(rx, ry),
r2 = Math.max(rx, ry);
$('.child.fit').css({
width: r1 * cw,
height: r1 * ch
});
$('.child.fill').css({
width: r2 * cw,
height: r2 * ch
});
}
// just a simple function to change the size
// of the parent object
window.setInterval(function() {
if (w < 70) {
winc = 10;
} else if (w > 200) {
winc = -10;
}
if (h < 50) {
hinc = 5;
} else if (h > 200) {
hinc = -5;
}
w += winc;
h += hinc;
$parent.css({
width: w,
height: h
});
calcChildSize();
}, 100);
.parent {
display: inline-block;
width: 200px;
height: 200px;
border: 1px solid #aaa;
margin-right: 50px;
}
.child {
box-sizing: border-box;
width: 50px;
height: 70px;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 0, 0, 0.5);
text-align: center;
font-family: monospace;
font-size: 11px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent fit">
<div class="child fit">FIT</div>
</div>
<div class="parent fill">
<div class="child fill">FILL</div>
</div>
Edit #2: I also went ahead and added an example of the two different scale methods in action.
Add max-width: 100% and object-fit:cover, here's a link https://jsfiddle.net/jv1f4bhL/1/
body {
position: relative;
background-color: wheat;
padding: 0;
margin: 0;
}
img {
object-fit: cover;
max-width: 100%;
width: auto;
}
<img src="https://mdbootstrap.com/img/Others/documentation/img%20(7)-mini.jpg" />
.box {
max-height: 100%;
}
....worked perfect.

Categories