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>
Related
Visual of element placement
I am trying to make a little “pet the dog game” and I would like to put a div over his head and when you click the DIV it will trigger a JS function to change the photo to a .gif then back again here is my code
JS:
function pet_head(){
var image = getElementById("image");
image.src="DogPet.gif";
setTimeout(function(){
image.src="dog.jpeg";
}, 1000//length of gif
);
};
HTML:
<div class="main">
<img id="image" src="dog.jpeg">
<div class="click></div>
</div>
CSS:
img{
height:100%;
width100%;
position:absolute;
}
If you use absolute in the image it will always be on top of everything else.
Take a look below and see if that is what you looking for.
function pet_head(event) {
/*var image = getElementById("image");
image.src = "DogPet.gif";
setTimeout(function() {
image.src = "dog.jpeg";
}, 1000 //length of gif
);*/
alert('changed');
};
document.getElementById('click').addEventListener('click', pet_head);
img {
height: 100%;
width: 100%;
}
div {
/* This will center the image horizontally */
display: flex;
justify-content: center;
position: absolute;
}
div#click {
color: green;
border: 2px solid red;
top: 14%;
height: 45%;
width: 300px;
position: absolute;
}
<div class="main">
<div id="click"></div>
<img id="image" src="https://i.insider.com/5df126b679d7570ad2044f3e?width=1100&format=jpeg&auto=webp" />
</div>
Here is a working version of your code. Also note that (besides removing code typos) I added object-fit: cover to your img, so that it preserves aspect ratio as the viewport size changes.
function pet_head() {
// var image = document.getElementById("image");
alert("petting the dog");
/* image.src = "DogPet.gif";
setTimeout(function() {
image.src = "dog.jpeg";
}, 1000 //length of gif
); */
};
document.querySelector(".click").addEventListener("click", pet_head);
img {
height: 100%;
width: 100%;
position: absolute;
object-fit: cover;
}
.click {
position: absolute;
left: 49%;
top: 22px;
height: 13vh;
width: 17vw;
cursor: pointer;
}
/* Presentational styles */
.click {
background: yellow;
opacity: .1;
}
html, body {
margin: 0;
}
*, *::before, &::after {
box-sizing: border-box;
}
<div class="main">
<img id="image" src="https://i.stack.imgur.com/FthXz.jpg">
<div class="click"></div>
</div>
jsFiddle
I'm working with an image fader program, but I'm not understanding absolute positioning. I have the images fading nicely and resizing the way I want if the screen resizes. but I have 2 problems. Div#2 gets covered up by the images. I want div2 to always appear below the image div. Also, I have control buttons on the images. I want them in the middle. I thought using top:50% would do that, but it's not. Here's an example...
var slides = document.querySelectorAll('#slides .slide');
var currentSlide = 0;
var slideInterval = setInterval(nextSlide,5000);
function nextSlide(){goToSlide(currentSlide+1);}
function previousSlide(){goToSlide(currentSlide-1);}
function goToSlide(n){
slides[currentSlide].className = 'slide';
currentSlide = (n+slides.length)%slides.length;
slides[currentSlide].className = 'slide showing';}
var next = document.getElementById('next');
var previous = document.getElementById('previous');
next.onclick = function(){nextSlide();};
previous.onclick = function(){previousSlide();};
#slides {position: relative}
.slide{
position: absolute;
left: 0px;
top: 0px;
width:100%;
height:auto;
min-height:300px;
object-fit:cover;
opacity: 0;
box-sizing:border-box;
transition: opacity 2s;}
.showing{opacity: 1;}
.controls{
background: transparent;
color: #fff;
font-size: 30px;
cursor: pointer;
border: 1px solid #555;
width: 30px;
position: absolute;
}
.controls:hover{ opacity:.5}
.fadenext{right: 10px; top: 50%;}
.fadeprev{left: 10px; top: 50%;}
<br><br>
<div id="slides">
<img src='https://www.panotools.org/dersch/Monp.JPG' class="slide showing">
<img src='https://www.panotools.org/dersch/StBp.JPG' class="slide">
<button class="controls fadeprev" id="previous"><</button>
<button class="controls fadenext" id="next">></button>
</div>
<div style='margin-top:40px;border:1px solid red;width:200px;height:100px'>
This is Div # 2</div>
I've amended your snippet to fix your issues.
Adding margin-top instead of top will fix your issue with the
controls.
Div 2 will now always remain below your slider.
P.S. I moved your div2 inline styles to keep it neat.
var slides = document.querySelectorAll('#slides .slide');
var currentSlide = 0;
var slideInterval = setInterval(nextSlide, 5000);
function nextSlide() {
goToSlide(currentSlide + 1);
}
function previousSlide() {
goToSlide(currentSlide - 1);
}
function goToSlide(n) {
slides[currentSlide].className = 'slide';
currentSlide = (n + slides.length) % slides.length;
slides[currentSlide].className = 'slide showing';
}
var next = document.getElementById('next');
var previous = document.getElementById('previous');
next.onclick = function() {
nextSlide();
};
previous.onclick = function() {
previousSlide();
};
* {
margin: 0;
padding: 0;
}
#slides {
position: relative
}
.slide {
position: absolute;
left: 0px;
top: 0px;
width: 100%;
height: auto;
min-height: 300px;
object-fit: cover;
opacity: 0;
box-sizing: border-box;
transition: opacity 2s;
}
.showing {
opacity: 1;
}
.controls {
background: transparent;
color: #fff;
font-size: 30px;
cursor: pointer;
border: 1px solid #555;
width: 30px;
position: absolute;
}
.controls:hover {
opacity: .5
}
.fadenext {
right: 10px;
margin-top: 25%;
}
.fadeprev {
left: 10px;
margin-top: 25%;
}
.div2 {
margin-top: 50%;
border: 1px solid red;
width: 200px;
height: 100px;
}
<br><br>
<div id="slides">
<img src='https://www.panotools.org/dersch/Monp.JPG' class="slide showing">
<img src='https://www.panotools.org/dersch/StBp.JPG' class="slide">
<button class="controls fadeprev" id="previous"><</button>
<button class="controls fadenext" id="next">></button>
</div>
<div class="div2">This is Div # 2</div>
It's not feasible to use % based positions when you use "top" style. So to achieve what you want to do, use margin-top instead. As shown below:
.fadenext{right: 10px; margin-top: 25%;}
.fadeprev{left: 10px; margin-top: 25%;}
And for your div2, just change it's style to:
margin-top: 50%
Maybe an obvious question but how do I make an element with a absolute position not overflow its container when moving it's position right? I know I could change it to relative position or move it 99% but for my project that won't due. I tried using margins, padding, object-fit, all with no success. Thanks for any help
var green = document.getElementById('green');
function myFunct() {
green.style.right = '100%';
}
h1 {
position: relative;
width: 80%;
height: 100px;
margin: auto;
background-color: red;
}
#green {
position: absolute;
right: 0px;
height: 100px;
background-color: green;
width: 20px;
}
<h1>
<div id = 'green'></div>
</h1>
<button onclick="myFunct()">FindHighScore</button>
Use CSS calc()
var green = document.getElementById("green");
function myFunct() {
green.style.right = "calc(100% - 20px)";
}
Or, apply left: 0 and right: auto (reset)
var green = document.getElementById("green");
function myFunct() {
green.style.left = "0";
green.style.right = "auto";
}
A <div> should not be in a <h1> tag by the way.
You can set overflow to hidden at parent container.
<h1> permitted content is Phrasing content
var green = document.getElementById('green');
function myFunct() {
green.style.right = '100%';
}
div:not(#green) {
position: relative;
width: 80%;
height: 100px;
margin: auto;
background-color: red;
overflow: hidden;
}
#green {
position: absolute;
right: 0px;
height: 100px;
background-color: green;
width: 20px;
}
<div>
<div id='green'></div>
</div>
<button onclick="myFunct()">FindHighScore</button>
I am trying to rotate background image while scrolling. The effect should look like cube. Sadly I could not find a way with css and jquery to make it look like in the video. On the gif, when scrolling down from gallery to next page, there is grill background which rotates and stretches by amount of page shown.
EDIT: Rotating animation has to look like this
What have I tried so far (unsuccessfully)
$(function() {
var rotation = 0,
scrollLoc = 0;
$(window).scroll(function() {
$("#galerie").text($(document).scrollTop() + "=ScrollTop,WinHeight=" + $(window).height());
var newLoc = $(document).scrollTop();
var diff = scrollLoc - newLoc;
rotation += diff, scrollLoc = newLoc;
var rotationStr = "rotateX(" + rotation / ($(window).height() * 2) + "turn)";
$("#home").css({
"-webkit-transform": rotationStr,
"-moz-transform": rotationStr,
"transform": rotationStr,
"background-size": -rotation
});
});
})
body,
html {
height: 100%;
}
body {
background-color: #090909;
}
#home {
height: 100%;
position: relative;
overflow: hidden;
}
#galerie {
color: green;
}
#home:before {
content: "";
background-repeat: no-repeat;
background-size: 100% auto;
background-position: center bottom;
background-color: grey;
background-attachment: initial;
position: absolute;
width: 100%;
height: 100%;
z-index: -1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id=box>
<div id="home">
TestText
</div>
</div>
<div id="galerie">
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
<div id="gale">
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
</body>
I've made the top part for you. And I'm sure you'll make the bottom one yourself (see the snippet in the Full page mode):
$(function() {
$(window).scroll(function() {
$('#home_bg').css({
'transform': 'rotateX(' + 30 * (1 + Math.PI * Math.atan($(document).scrollTop() / 300)) + 'deg)'
});
});
})
html,
body {
height: 100%; margin:0 ;padding:0
}
body {
background-color: #333;
}
#home {
height: 30vh;
position: relative;
overflow: hidden;
perspective: 300px;
color:#fff;
text-align:center;
}
#home_bg {
content: "";
background: repeating-linear-gradient(45deg, #555, #555 2px, transparent 0, transparent 60px), repeating-linear-gradient(-45deg, #555, #555 2px, transparent 0, transparent 60px) 30px 30px / 170px 170px;
position: absolute;
z-index: -1;
top: -100%;
left: -50%;
width: 200%;
height: 200%;
transform: rotateX(30deg);
transform-origin: 50% 100%;
}
#galerie {
color: green;
display: flex;
justify-content: center;
flex-flow: row wrap;
width: 50%;
margin: 0 auto 70vh;
}
#galerie img {
width: 45%;
margin: 0 auto 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="home">
<h1>Lorem ipsum?</h1>
<div id="home_bg"></div>
</div>
<div id="galerie">
<p></p>
<img src="https://picsum.photos/100/100?1">
<img src="https://picsum.photos/100/100?2">
<img src="https://picsum.photos/100/100?3">
<img src="https://picsum.photos/100/100?4">
<img src="https://picsum.photos/100/100?5">
<img src="https://picsum.photos/100/100?6">
</div>
</body>
Hope you want like this.
$(function() {
var rotation = 0;
var interval;
var gear = $('.gear');
function animate() {
gear.css('transform', 'rotate('+rotation+'deg)');
rotation += 10;
rotation %= 360;
}
function startAnim() {
if (!interval) {
interval = setInterval(animate, 50);
}
}
function stopAnim() {
clearInterval(interval);
interval = null;
}
$(document).scroll(startAnim).mouseup(stopAnim);
});
body{
height: 1000px;
}
.gear{
background: url("https://cdn.pixabay.com/photo/2016/01/03/11/24/gear-1119298_960_720.png") no-repeat;
width: 100%;
height: 100px;
position: fixed;
background-position: center center;
background-size: contain;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="gear"></div>
I am wanting to make a website that uses a background that moves based on the position that the curser is on the website. I have found this website that gives a visual representation of what I want to do. http://www.alexandrerochet.com/I just need to know how to make the letters move. I will replace them with images later.
You can achieve that using css properties.
Based on Lea Verou's talk
const root = document.documentElement;
document.addEventListener("mousemove", evt => {
let x = evt.clientX / innerWidth;
let y = evt.clientY / innerHeight;
root.style.setProperty("--mouse-x", x);
root.style.setProperty("--mouse-y", y);
});
html {
height: 100%
}
:root {
--mouse-x: .5;
--mouse-y: .5;
}
body {
height: 100%;
background-image: radial-gradient( at calc(var(--mouse-x) * 100%) calc(var(--mouse-y) * 100%), transparent, black);
}
You may want to try using parallax.js to achieve the desired effect.
Demo site.
Quick jsfiddle.
var scene = document.getElementById('scene');
var parallaxInstance = new Parallax(scene);
parallaxInstance.friction(0.2, 0.2);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
background-color: #F9F871;
width: 100vw;
height: 100vh;
}
.scene {
top: 30%;
}
.layer {
width: 100%;
height: 200px;
}
.item {
width: 50px;
height: 50px;
border-radius: 200px;
background-color: red;
position: relative;
}
.item-1 {
background-color: #FF9671;
left: 30%;
}
.item-2 {
background-color: #D65DB1;
left: 60%;
}
.item-3 {
background-color: #FF6F91;
left: 40%;
}
.item-4 {
background-color: #FFC75F;
left: 70%;
}
<div class="container">
<div id="scene" class="scene">
<div data-depth="0.2" class="layer layer-1">
<div class="item item-1"></div>
<div class="item item-2"></div>
</div>
<div data-depth="0.6" class="layer layer-2">
<div class="item item-3"></div>
<div class="item item-4"></div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/parallax/3.1.0/parallax.min.js"></script>