How to overlay a new image onto a fixed container when scrolling? - javascript

I'm trying to replicate this overlay phone effect from this website:
https://www.beemit.com.au/
Basically, when you scroll down, the contents inside the fixed div (phone) also change.
I cannot grasp my head around the revealing effect created when you scroll down. I have only managed to create the fixed div and the various sections on the webpage.

Here's a simple version of the overlay-on-scroll.
There are 3 elements, the first image you want to be shown in the 'phone', the second image which gradually gets revealed and the footer element. They have different z-indexes so footer is behind both first and second and second is behind first.
The phone has a fixed position so it doesn't move on scrolling. The footer is placed relative to the body (or whatever container you have) just out of view at 100%.
We introduce a simple event listener on scrolling which tests whether there is an overlap between the footer and the phone. If there is then we set the height of the first image element to be its original height minus the overlap. This reveals the bottom part of the second element.
Without seeing your code I can't tell whether you need more sophistication (for example, you have to be aware of stacking contexts if your phone and footer are not in the same one).
const footer = document.querySelector('.footer');
const first = document.querySelector('.first');
const firstBottom = first.getBoundingClientRect().bottom;
const firstHeight = firstBottom - first.getBoundingClientRect().top;
function checkOverlay() {
const top = footer.getBoundingClientRect().top;
if ( top < firstBottom) {
first.style.height = firstHeight - firstBottom + top + 'px';
}
}
body {
width:100vw;
height: 100vh;
overflow-y: scroll;
overflow-x: hidden;
}
.first, .second {
position: fixed;
top: 50%;
left: 50%;
border: 1px solid black;
width: 20vmin;
height: 30vmin;
overflow:hidden;
}
.first {
background-image: linear-gradient(magenta, pink);
z-index: 0;
}
.second {
background-image: linear-gradient(cyan, lime);
z-index: -1;
}
.footer {
width: 100%;
height: 50%;
background-image: linear-gradient(red,blue);
position: relative;
top: 100%;
z-index: -2;
}
<body onscroll="checkOverlay();">
<div class="first"></div>
<div class="second"></div>
<div class="footer"></div>
</body>

Related

Creating responsive 'Image Map' for elements to match elements of background image (JS/CSS) (React) [duplicate]

I'm trying to create a map of button elements that overlay a full-screen image, positioned over all the 'buttons' depicted on the image. When the image resizes, the button elements should resize as well.
I initially used an SVG image map for this, but it had a positioning bug (the link below) that didn't allow for the video player inside the foreignObject to show.
SVG foreignObject and absolute positioning
I figured using absolute positioning, and JS to measure the width of the background image was the best way to go. However, I'm having trouble making it work.
Below is the project, with the white box needing to be positioned over the background image (the entire box, with chocolates, and the screen).
The image is within a div as a background, like so:
export const BackgroundLightsOn = styled.div`
position: absolute;
background: url(${BgLightsOn});
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
height: 100%;
width: 100%;
border: 1px solid red;
`
Unfortunately, getting the dimensions of this DOM element gets the dimensions of the div, not the image (which is sometimes larger than the viewport). Below is the 'resizing' code, but this connects to backgroundRef (which is the div element the image is the background, not the image itself).
import React, { useEffect, useRef } from 'react';
import {
TruffleTinContainer,
TruffleTinOutside,
} from '../truffle-tin/styledTruffleTin';
export default function TruffleTin({ backgroundRef }) {
const truffleTinRef = useRef();
const initialPos = { x: 150, y: 150 };
const padding = 25;
let truffleBoxWidth = 0;
let truffleBoxHeight = 0;
useEffect(() => {
truffleBoxWidth = truffleTinRef.current.offsetWidth;
truffleBoxHeight = truffleTinRef.current.offsetHeight;
resize();
}, []);
function resize() {
let backgroundDivRect = backgroundRef.current.getBoundingClientRect();
truffleTinRef.current.style.left =
(initialPos.x / truffleBoxWidth) * backgroundDivRect.width -
padding +
'px';
truffleTinRef.current.style.top =
(initialPos.y / truffleBoxHeight) * backgroundDivRect.height -
padding +
'px';
}
useEffect(() => {
window.addEventListener('resize', resize);
return () => window.removeEventListener('resize', resize);
});
return (
<TruffleTinContainer ref={truffleTinRef}>
I'm the truffle tin
</TruffleTinContainer>
);
}
See the GIF below for the issue:
What's the best way to get this div to proportionally match the width of the background image?
(Edit: I changed the 'div with image background' to an img tag, but even then, the getBoundingClientRect shows not the width of the image, but the width of the viewport:
(in other words, even though the image is much wider than the viewport, I'm still getting the viewports dimensions, when what I want is the image's dimensions))
You don't need JS for that. This is achieved with plain CSS, all you need is the button center positions relative to left-top corner of the block. Sample HTML/CSS:
<div class='wrapper'> <!-- Will need this later -->
<div class='container'>
<a class='button' style='--left: 25%; --top: 20%'>1</a>
<a class='button' style='--left: 35%; --top: 80%'>2</a>
</div>
</div>
.container{
position: relative;
background: url('/path/to/image');
background-size: 100% auto;
}
.button{
position: absolute;
left: var(--left);
top: var(--top);
transform: translate(-50%, -50%); /* Move button a bit so its center matches the actual position */
/* These are just for visualization */
width: 50px;
height: 50px;
border-radius: 100%;
background: pink;
display: flex; align-items: center; justify-content: center;
}
The next step is to make .container have a constant width, or at least a constant width/height ratio if you care about vertical resizing. The constant width is achieved with a simple min-width. To avoid horizontal scroll we will add a .wrapper to hide the cut-off sides:
.wrapper{
display: flex;
align-items: center;
max-width: 100%;
overflow: hidden;
}
/* A-and a min-width for .container */
.container{
min-width: 1200px; /* Or whatever */
}
If you need a fixed ratio:
.container::before{
content: '';
display: block;
padding-top: 56.25%; /* 56.25% of the parent width this is, ratio 16x9 */
}
min-width can still be applied if needed. There's an aspect-ratio CSS property that can be used instead of that pseudo-element hack, but it is supported only in relatively fresh browser versions; still might be used if you don't plan supporting a little outdated browsers.

Maintain aspect ratio of a video when resizing the browser

I'm working on a Video editing tool, and I need to maintain the 16:9 aspect ratio of the video when resizing the screen horizontally and vertically. So far I got it to work as expected when resizing horizontally, and when resizing down vertically, but can't get it to work when sizing up vertically. The Javascript code I used to calculate the height of the video and resize it is below (notice how the else clause is empty because that's where the code should go):
const calculateHeight = () => {
// Get the other elements on the page
const header = document.querySelector('.main-navigation');
const meshTopBar = document.querySelector('.mesh__top-bar');
const footer = document.querySelector('.mesh__bottom-bar');
// Get the section to apply the window height to it
const mainSection = document.querySelector('.insert-level-container');
// Get the video elements
const editor = document.querySelector('.mesh__insert-editor-container');
const video = document.querySelector('.mesh__insert-editor-video-container');
// Apply the height to the main section by calculating the window height minus the other elements' height
if(mainSection !== null) {
mainSection.style.height = (window.innerHeight - header.offsetHeight - meshTopBar.offsetHeight - footer.offsetHeight) + 'px';
}
// This should be the ideal height for the video
video.style.minHeight = ((video.offsetWidth * 9) / 16) + 'px';
// If the video height is bigger than the section height (calculated above), then resize it
if(video.offsetHeight + 115 > mainSection.offsetHeight) {
video.style.minHeight = video.offsetHeight - 1 + 'px';
editor.style.maxWidth = video.offsetHeight * 16 / 9 + 'px';
} else {
// This is where the logic for the vertical resizing should go
}
}
The relevant CSS for these items is:
.mesh__insert-editor-container {
margin-left: auto;
margin-right: auto;
}
.mesh__insert-editor-video-container {
position: relative;
width: 100%:
}
And the HTML:
<section class="mesh__insert-editor-container flex__one flex-container flex-column horizontally-left-aligned" id="video-main-container">
<div class="mesh__insert-editor-video-container flex-container horizontally-right-aligned flex-wrap">
<video class="mesh__insert-editor-video-placeholder"></video>
</div>
</section>
All this code is:
Get the height of all the elements on the page, sum them and calculate the main section height by subtracting that height;
If the video height gets bigger than the section height, I reduce its height by -1px each time the window gets resized, and calculate the new width.
All the above code is giving me this result, which works great for most scenarios, but I need the video to size up when the condition on the if statement is not met. Everything I tried inside the else statement gets "jumpy".
Any better alternatives to solve this would be much appreciated. Thanks all!
The CSS aspect ratio trick might be a good solution: https://css-tricks.com/aspect-ratio-boxes/
The approach takes advantage of a quirk in CSS where padding based on a percentage value will be relative to the element's width. Create a container using this trick, the important bit is this line:
padding-top: calc(9/16 * 100%);
The value is calculating the correct height based on the aspect ratio you want (9 tall over 16 wide in this case) and generating it relative to the width of the element by multiplying by 100%.
With the container maintaining aspect ratio, just place the content inside an absolute positioned inner div and you should be good. This solution is fully responsive at that point.
* { box-sizing: border-box }
.outer-max-width {
max-width: 960px;
margin: 0 auto;
}
.aspect-ratio-box {
width: 100%;
padding-top: calc(9/16 * 100%);
position: relative;
border: 2px solid red; /* for demo visibility, remove */
}
.aspect-ratio-box-content {
position: absolute;
width: 100%;
top: 0;
left: 0;
border: 2px solid blue; /* for demo visibility, remove */
}
.video-placeholder {
display: block;
width: 100%;
height: auto;
}
<div class="outer-max-width">
<div class="aspect-ratio-box">
<div class="aspect-ratio-box-content">
<img class="video-placeholder" src="https://placeimg.com/640/360/nature" />
</div>
</div>
</div>
Got it to work! I used this amazing CSS-only solution: https://codepen.io/EightArmsHQ/pen/BvNzrm similar to BugsArePeopleToo's suggestion, from eightarmshq:
.content{
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
background: #555;
box-shadow: inset 1vh 1vh 10vh 0 rgba(0,0,0,0.5);
display: flex;
box-sizing: border-box;
border: 25px solid #cecece;
}

jquery increase/decrease image contrast on scroll

This site I am developing is using HTML5, CSS3, Bootstrap 4, and Jquery. I would like to have a scroll effect on a full-screen background-image that is at the very top of my page (100vh hero banner type thing). I am trying to gradually increase the contrast (css filter: contrast(some%)) of an image as the user scrolls down (its fine if the image is completely unrecognizable by the time it leaves viewport).
I have some Jquery that somewhat does the effect I am looking for, however I would like the effect to be more gradual.
The main issue I am having is that when the user scrolls back to the top of the page the contrast value gets set to 0% leaving a completely grayed out image. What I would like is for the contrast to gradually decrease back to normal (100%) as the user scrolls back up all the way to the top of the page.
I have set up a very simplified codepen. I couldn't get a css background-image url value to reference an external link from codepen, so I am targeting the effect on a full screen image ().
Thanks!
Link to the Pen: [codepen-link][1]
[1]: http://codepen.io/wdzajicek/pen/MVovZE
See code below in snippet
$(document).ready(function (){
$(window).scroll(function(){
var pixelstop = $(window).scrollTop();
$(".myimage ").css("filter", "contrast(" + pixelstop + "%)");
});
});
.header {
height: 100vh;
}
.myimage {
position:absolute;
top: 0;
left: 0;
min-width: 100%;
width; 100%;
z-index: -1;
}
.jumbotron {
position: relative;
background-color: unset;
margin-top: 150px;
z-index: 999;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header class="header text-center">
<img src="https://raw.githubusercontent.com/wdzajicek/portfolio/master/assets/img/header-bg.jpg" class="myimage" alt="">
</header>
There is the main problem in $(window).scrollTop(); it will return 0 value
that's why contrast value gets set to 0% leaving a completely grayed out image
var pixelstop = $(window).scrollTop();
replace the code with
var pixelstop = 100+100*$(window).scrollTop()/$(window).height();
don't just copy this code please understand thanks.
$(document).ready(function (){
$(window).scroll(function(){
var pixelstop = 100+100*$(window).scrollTop()/$(window).height();
console.log(pixelstop);
$(".myimage ").css("filter", "contrast(" + pixelstop + "%)");
});
});
.header {
height: 100vh;
}
.myimage {
position:absolute;
top: 0;
left: 0;
min-width: 100%;
width; 100%;
z-index: -1;
}
.jumbotron {
position: relative;
background-color: unset;
margin-top: 150px;
z-index: 999;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<header class="header text-center">
<img src="https://raw.githubusercontent.com/wdzajicek/portfolio/master/assets/img/header-bg.jpg" class="myimage" alt="">
</header>
100 is default value of filter contrast not 0. that's why the background is grey out because it reaches zero.

Check if element can be scrolled to the left or right

I would like to display indicators for a certain div to show that it can be scrolled right or left depending on its state. To do so I would need to know if element can be scrolled to respective positions, e.g. if there is content to be seen on the right show indicator and after scrolling show another indicator on the left to indicate that users can now scroll there as well. I have a simple setup like this one: https://jsfiddle.net/udv8u596/
(You can scroll horizontally, scrollbar is hidden intentionally)
HTML:
<div class="scroll-container">
<div class="scroll-content">
Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally
</div>
</div>
CSS:
.scroll-container {
width: 80%;
margin: 0 auto;
background: cyan;
overflow-y: hidden;
height: 36px;
}
.scroll-content {
padding: 10px 0;
height: 50px;
white-space: nowrap;
overflow-x: scroll;
overflow-y: hidden;
}
To check if an element is overflowing along the x-axis, you can simply compare its computed width, accessible via jQuery's .width() method, and its scrollWidth, a native JS function:
var $ele = $('.scroll-content'),
overflowing = $ele[0].scrollWidth > $ele.width();
You can then check the boolean value of overflowing if the element is overflowing or not. However, note that if you want this variable to be updated if the window resizes, a little more work has to be done:
var $ele = $('.scroll-content'),
overflowing = function() {
if($ele[0].scrollWidth > $ele.width()) {
return true;
} else {
return false;
}
};
console.log(overflowing());
$(window).resize(function() {
console.log(overflowing());
});
Here's a fiddle with the above logic implemented, with some slight modifications: https://jsfiddle.net/teddyrised/udv8u596/5/
Ilya basically you need to check your element right postion. On way of achieving this is to set the inner element to have absolute oistion and get right postion with jQuery
parseInt($('.scroll-content').css('right')) >= 0
I have modified you code as: https://jsfiddle.net/udv8u596/4/
In this example before animating the element it checks if the righ position is bigger than 0.
Please not that righ position is calculated based on the parent element. Left position is set to be 0 in the css but righ postion will be calculated in this example is ~-250.
I hope this gives you an idea how to solve your problem.
Here's a quick start for what you are looking for :
HTML
<div class="scroll-container">
<div class="mask">
<div class="scroll-content">
Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally Scroll Me Horizontally
</div>
</div>
</div>
<div class="scrollRight">Scroll Right »</div>
<div class="scrollLeft">» Scroll Left </div>
CSS
.scroll-container {
width: 80%;
margin: 0 auto;
background: cyan;
overflow-y: hidden;
overflow-x: hidden;
height: 36px;
}
.mask{
position:relative;
overflow-x: hidden;
overflow-y: hidden;
height: 36px;
width: 100%;
}
.scroll-content {
position:absolute;
padding: 10px 0;
height: 50px;
white-space: nowrap;
overflow-x: visible;
width:auto;
}
.scrollRight, .scrollLeft{
font-size:10px;
display:none;
}
JS
var contentWidth = $(".scroll-content").width();
var containerWidth = $(".scroll-container").width();
if(contentWidth>containerWidth){
$(".scrollRight").show();
}
$("body").on("click", ".scrollRight", function(){
var scrollValue = contentWidth-containerWidth;
$(".scroll-content").animate({"margin-left":"-"+scrollValue+"px"});
$(".scrollRight").hide();
$(".scrollLeft").show();
})
$("body").on("click", ".scrollLeft", function(){
var scrollValue = contentWidth-containerWidth;
$(".scroll-content").animate({"margin-left":"0px"});
$(".scrollRight").show();
$(".scrollLeft").hide();
})
See Update JSFiddle

Div Position: Fixed. Absolute when certain length away?

I'm currently making a website with a "Support is Live" div which will be following the user when scrolling. So I gave it Position: Fixed; and all works fine.
But when the user scrolls back up, I want the Support div to stop so it doesn't "touch" the header.
Here is a picture that hopefully makes it easier to understand:
http://gyazo.com/2694b03181a39c3b6673901b42b5952d
I want the yellow div to stop in line with the orange field on the picture. But when the user starts to scrolling down again, it will follow.
My Best Regards
Philip
This will need some JQuery to work properly:
JSFIDDLE
JQuery
$(document).on("scroll", function() {
if($(document).scrollTop() < 100) {
$('#alert').addClass("absolute");
} else if($(document).scrollTop() > 100) { //100 is the height of your header. Adjust if needed
$('#alert').removeClass("absolute");
}
});
CSS
.absolute {
top: 100px; //same as the value in the conditions
position: absolute;
}
#alert{
background-color: #FF0;
float: left;
height: 400px;
width: 230px;
margin-left: 20px;
position: fixed;
z-index:999;
}
HTML
<div id="alert" class="absolute"> </div>
/!-- add the class so that it doesn't mess up the layout when you load the page --!/
The srolltop function checks how much space is between your viewport and the top of your document. When it reaches the height of the header, a class absolute is applied in order to keep the #alert div in its place.

Categories

Resources