CSS/JS image slide out animation - javascript

I'm trying to recreate an effect like this: https://www.brontidebg.com/product
The main image at the top of the screen (to the left) has a really smooth animation out into the screen (same with the image at the bottom). When you scroll to either image, they animate out in the same manner.
Here is what I've come up with:
HTML
<div class="top">
<h1>scroll down<h1>
</div>
<div class="container">
<div class="block image-block slideright">
<figure>
<img src="https://i.guim.co.uk/img/media/11d4c182d094199e26ddb36febe67123a9bbc93a/34_246_2966_4275/master/2966.jpg?w=700&q=55&auto=format&usm=12&fit=max&s=4a5b5fe1d34627003607df532913292d">
</figure>
</div>
<div class="block text-block">
<h2> Some text </h2>
</div>
</div>
CSS
.top{
height:100vh;
}
h1{
text-align: center;
}
.block{
display: inline-block;
height: 100vh;
}
.image-block{
}
figure{
position: relative;
overflow: hidden;
height: 100vh;
width: 34vw;
text-align: center;
margin: 0;
}
image{
height: 100vh;
width: 34vw;
position: relative;
object-fit: cover;
}
.slideright{
transform: translateX(-34vw);
transition: all .8s ease-out;
}
.slideright.slideinright{
transform: translateX(0);
}
JS
$(window).scroll(function() {
var winTop = $(window).scrollTop();
$(".slideright").each(function(){
var pos = $(this).offset().top;
if (pos < winTop + 600) {
$(this).addClass("slideinright");
}
});
$(".slideleft").each(function(){
var pos = $(this).offset().top;
if (pos < winTop + 600) {
$(this).addClass("slideinleft");
}
});
});
Codepen (view in fullscreen since I'm using vh): https://codepen.io/Caj/pen/GdZwYP
As you can see, the image slides out as you scroll towards it, but it's not a smooth, professional looking animation like the example link. I'm also hoping to have the image slide out if you were to scroll up to the top and then back down (have the function run repeatedly, not just the first time you scroll to within view). Thanks in advance!

Try this:
https://codepen.io/anon/pen/ZoWVxr
$(".slideright").each(function(){
var pos = $(this).offset().top;
if (winTop + 600 > pos) {
$(this).addClass("slideinright");
}
if(winTop === 0) {
$(this).removeClass('slideinright')
}
});
Added opacity, changed the speed and added the reset when the scroll is at the top. I changed your logic a bit so that it doesn't start the animation immediately, it only starts right when the image is in view. You can change the winTop + 600 to control when it starts. Add more to make it start earlier, less to make it start later. winTop + 200 would start the animation further down the scroll.

You are almost there, but what gives that subtle touch of professionality to the animation is the choice of the ease function. I would try with a softer transition like this one:
transition: all 2s cubic-bezier(0.23, 1, 0.32, 1) 400ms;

Related

How do I prevent scroll back up with JavaScript or jQuery?

I have a webpage where there is a full height intro image. Underneath this image is the main body of the site with a regular site header at the top, I'm trying to create an effect where once the user scrolls down to the site header, they cannot scroll back up to view the intro image.
CSS Classes:
Main Intro Image: .cq-fullscreen-intro
Site Header: .nav-down
I had a poke around on StackOverflow but I can't find anything that addresses this circumstance, can anyone point me in the right direction to achieve this using jQuery?
you can use JQuery scrollTop function like this
$(function() {
$(window).scroll(function() {
var scroll = $(window).scrollTop();
// set the height in pixels
if (scroll >= 200) {
// after the scroll is greater than height then you can remove it or hide it
$(".intro-image").hide();
}
});
});
So instead of scrolling, I personally think it would be better to have it be actionable. Forcing the user to manually do the transition (and all in between states) is a bad idea. If the user scrolls half way, and see's something actionable (menu, button, input field) is it usable? If it is, what happens if they submit... very awkward. If it isn't usable, how do they know when it is? How do they know it's because they haven't scrolled all the way. It's very poor user experience.
In the following example, I've created a pseudo-screenport for you to see what's actually going on. The .body container in your real site would be the body element.
Code Pen Example
$(document).ready(function(){
$('.splash-screen').on('click', function(){
$('.splash-screen').addClass("is-hidden");
});
})
html, body{
background: #eee;
margin: 0;
padding: 0;
}
.flex-root {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
.web-container {
width: 640px;
height: 480px;
background: #fff;
}
.body {
font-size: 0; // this is only to prevent spacing between img placholders
position: relative;
}
.splash-screen{
position: absolute;
transition: transform 1s ease-in-out;
}
.splash-screen .fa {
position: absolute;
z-index: 1;
font-size: 24px;
color: #fff;
left: 50%;
bottom: 15px;
}
.splash-screen.is-hidden {
transform: translateY(-110%);
transition: transform 1s ease-in-out;
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="flex-root">
<div class="web-container">
<div class="body">
<div class="splash-screen">
<img src="https://via.placeholder.com/640x480?text=Splash+Screen"/>
<i class="fa fa-chevron-circle-up"></i>
</div>
<img src="https://via.placeholder.com/640x60/cbcbcb?text=Menu"/>
<img src="https://via.placeholder.com/640x420/dddddd?text=Site Body"/>
<div>
</div>
</div>
While its not direclty preventing you from scrolling up and its not jQuery, I would suggest to remove/hide the element once its out of view.
You could get the current scroll position, relative to the top of the page, and check if its greater than the elements height:
const target = document.getElementById('my-target')
const targetHeight = target.getBoundingClientRect().height
const scrollEventListener = () => {
if (
document.body.scrollTop > targetHeight ||
document.documentElement.scrollTop > targetHeight
) {
target.remove()
window.removeEventListener('scroll', scrollEventListener)
document.documentElement.scrollTop = 0
}
}
window.addEventListener('scroll', scrollEventListener)
Here is a codepen https://codepen.io/bluebrown/full/aboagov

How to get element to transform progressively based on scroll position

I am attempting to use waypoints for two specific functions.
To identify if a user is scrolling up or down and the container comes into view. This is not working.
The second thing I am trying to figure out how to do doesn't necessarily have to do with waypoints. I want the image you see in the snippet to progressively transform: translateX based on the scroll progression. I am not sure how to do this. I put translate in the snippet to show the movement.
If you go to this site and scroll down a little to the "Nike and Snapchat" section, you will see a phone image of Lebron. As you progressively scroll up or down, the image moves accordingly. This is what I am trying to replicate.
Does anyone know what I can do to achieve this?
var homeMainSec = $('#homeMainSec');
homeMainSec.waypoint(function(direction) {
if (direction === 'down') {
$('#homeBoxGridRight img').addClass('slideLeftDisplay');
console.log('Left Slide');
}
}, {
offset: '25%'
});
homeMainSec.waypoint(function(direction) {
if (direction === 'up') {
$('#homeBoxGridRight img').addClass('slideRightDisplay');
console.log('Right Slide');
}
}, {
offset: '25%'
});
#homeMainSec {
width: 100%;
height: 95vh;
position: relative;
margin-top: 70px;
}
.homeMainBlock {
height: 100%;
width: 50%;
display: inline-block;
vertical-align: top;
box-sizing: border-box;
}
/*- HomeBoxGridRight Section -*/
#homeBoxGridRight img {
display: block;
width: 40%;
height: auto;
margin-left: 50%;
}
.slideLeftDisplay {
transform: translateX(-100px);-webkit-transform: translateX(-100px);
transition: 1s;-webkit-transition: 1s;
opacity: 1;
}
.slideRightDisplay {
transform: translateX(100px);-webkit-transform: translateX(100px);
transition: 1s;-webkit-transition: 1s;
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/waypoints/4.0.1/jquery.waypoints.min.js"></script>
<section id="homeMainSec">
<div class="homeMainBlock" id="homeBoxGridLeft">
</div><div class="homeMainBlock" id="homeBoxGridRight">
<img src="https://slidesjs.com/examples/standard/img/example-slide-1.jpg" alt="Image">
</div>
</section>
<br><br><Br><br><br><br><br><br>
This gives you page scroll position for vertical scroll
window.pageYOffset
This gives you total scroll height for body
document.querySelector("body").scrollHeight
You need to subtract the height of scrollbar element that is given by
document.scrollingElement.offsetHeight
You can translate with respect to ratio of vertical scroll position and body height
window.pageYOffset/(document.querySelector("body").scrollHeight - document.scrollingElement.offsetHeight)
This will give you, ratio of current position to max scrollable position.
Manipulate it as you like to translate the elements wrt their initial position.

Custom scroll UX

I'm currently working on a project where the desired user experience involves a very customized interaction with scroll events.
Problem to solve:
The page has X sections, each of them with a height equal to the viewport hight height: 100vh;. When a user scrolls down on the viewport, the current visible section stays where it is and a scroll indicator animates based on a threshold of distance scrolled (30px, for example). Once the user has scrolled the threshold, the next section comes up from the bottom of the screen and covers up the current section (which still doesn't move).
Initial Approach:
Set each section to an absolute position and adjust them with by changing CSS classes based on the scrollwheel event. Body overflow:hidden and transform property to manipulate the sections. I am running in to issues, though.
The scrollwheel event seems to be documented as very unstable solution to implement.
The .wheelDelta aspect of the event fires asynchronously and is difficult to capture a gesture with. (On chrome, it just spits out a multiple of 3 a bunch of times rather than a distance of the gesture in px). This makes it difficult to set a threshold and animate the elements that are responsive to that threshold.
I basically want to be able to track the number of pixels a scrollwheel-like event is covering and apply that number to the position of a certain scroll-hint element until the scroll threshold is met. Once it is met, a function fires to change the classes and update some information on the page. If the threshold is not met, the scroll hint element goes back to it's default position.
My attached approach doesn't feel very conducive to accomplishing this, so I'm looking for either 1) a different and more stable approach or 2) revisions / criticisms on what I'm doing wrong with this approach to make it stable.
(function scrollerTest($){
$('body').on ('mousewheel', function (e) {
var delta = e.originalEvent.wheelDelta,
currentScreenID = $('section.active').data('self').section_id,
currentScreen = $('section.part-' + currentScreenID),
nextScreenID = currentScreenID + 1,
nextScreen = $('section.part-' + nextScreenID);;
if (delta < 0) { // User is Scrolling Down
currentScreen.removeClass('active').addClass('top');
nextScreen.removeClass('bottom').addClass('active')
} else if (delta > 0) { // User is Scrolling Up
}
});
}(jQuery));
body {
overflow: hidden;
padding: 0;
margin: 0;
}
section {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
z-index: 999;
background-color: #CA5D44;
transition: 0.8s all ease-in-out;
}
section.part-1 {
position: relative;
z-index: 9;
}
section.part-2 {
background-color: #222629;
}
section.active {
transform: translateY(0);
}
section.top {
transform: translateY(-10%);
}
section.bottom {
transform: translateY(100%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<body>
<section class="part-1 home active" data-self='{ "section_id" : 1, "section_title" : "Home", "menu_main_clr" : "#fff" , "menu_second_clr" : "#CA5D44", "logo_clr" : "white" }'>
</section>
<section class="part-2 about bottom" data-self='{ "section_id" : 1, "section_title" : "About", "menu_main_clr" : "#CA5D44" , "menu_second_clr" : "#fff", "logo_clr" : "white" }'>
</section>
<section class="part-3 contact bottom" data-self='{ "section_id" : 1, "section_title" : "Contact", "menu_main_clr" : "#fff" , "menu_second_clr" : "#CA5D44", "logo_clr" : "white" }'>
</section>
</body>
Edit Note:
The snippet seems to have some issue with firing the event and changing classes after the first instance - not sure why. On my local example it fires them all at once..
**Edite Note 2: **
Listed code is just a copy of the closest behaviour I could achieve. The whole threshold functionality seems pretty unattainable with this method, unfortunately, as the wheel event doesn't behave like a scroll event.
the whole scroll topic is rather complex especially when you think about touch scroll events, too.
There are some libraries out there - see this list for example: http://ninodezign.com/30-jquery-plugins-for-scrolling-effects-with-css-animation/
I used https://projects.lukehaas.me/scrollify/ before and it might allow you to do what you intend (using the before callback eventually) but I can't tell without trying myself. Also regard that scrollify is relatively big (8kb minified) in comparison to other libraries.
You can approach this using by ensuring each content-filled section is followed by a blank transparent gap (in the example below, also 100vh in height) and then using javascript to apply position:fixed to each content-filled section when it hits the top of the viewport.
Example:
var screens = document.getElementsByClassName('screen');
function checkPosition() {
for (var i = 0; i < (screens.length - 1); i++) {
var topPosition = screens[i].getBoundingClientRect().top;
if (topPosition < 1) {
screens[i].style.top = '0';
screens[i].style.position = 'fixed';
}
}
}
window.addEventListener('scroll',checkPosition,false);
.screen {
position: absolute;
display: block;
left: 0;
width: 100%;
height: 100vh;
}
.red {
position: fixed;
top: 0;
background-color: rgb(255,0,0);
}
.orange {
top: 200vh;
background-color: rgb(255,140,0);
}
.yellow {
top: 400vh;
background-color: rgb(255,255,0);
}
.green {
top: 600vh;
background-color: rgb(0,191,0);
}
.blue {
top: 800vh;
background-color: rgb(0,0,127);
}
p {
font-size: 20vh;
line-height: 20vh;
text-align: center;
color: rgba(255,255,255,0.4);
}
<div class="red screen">
<p>Screen One</p>
</div>
<div class="orange screen">
<p>Screen Two</p>
</div>
<div class="yellow screen">
<p>Screen Three</p>
</div>
<div class="green screen">
<p>Screen Four</p>
</div>
<div class="blue screen">
<p>Screen Five</p>
</div>

How to get a css transition to not start until scrolled into a specific div

I am running into some issues trying to get a css transition/sequence to start once I scroll over its parent div. I have done this with javascript functions, just never a css transition.
Right now my image will not even show, let alone start at the sequence.If I comment out the javascript the sequence plays as normal.
How can I get the css transitions to start when I get into the parent div section1?
I put a jsfiddle of this in the comments as it is easier to see this.
/*$("#think").hide();
//init scrolling event heandler
$(document).scroll(function() {
var docScroll = $(document).scrollTop(),
boxCntOfset = $("#section1").offset().top - 100;
//when rich top of boxex than fire
if (docScroll >= boxCntOfset) {
$("#think").fadeIn(200)
} else {
$("#think").fadeOut(200)
}
})*/
//Scroll for think images
/*$("#think").hide();
$(function() {
var oTop = $('##section1').offset().top - window.innerHeight;
$(window).scroll(function() {
var pTop = $('body').scrollTop();
console.log(pTop + ' - ' + oTop);
if (pTop > oTop) {
$("#think").fadeIn(200)
}
});
});*/
#red {
width: 100%;
height: 700px;
background:red;
}
#section1 {
width: 100%;
height: 600px;
background:blue;
}
#think {
/*opacity: 0;*/
margin-left: 200px;
width: auto;
height: 500px;
-webkit-animation-name: think;
animation-name: think;
-webkit-animation-duration: 8s;
animation-duration: 8s;
-webkit-animation-direction: normal;
animation-direction: normal;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
/*min-height: 500px; min-width: 500px;*/
background-repeat: no-repeat;
/*background-size: 100% auto;*/
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="red"></div>
<div class="section" id="section1">
<div id="section1-right-container">
<div id="think"></div>
</div>
</div>
You can use the jquery.visible plugin to easily determine if the element is in the viewport: http://www.jsdelivr.com/projects/jquery.visible
Next you will need to determine if is visible in a window scroll function and add a css class that holds the animations:
$(window).on("scroll", function(){
// Determine if the element is in the viewport
if($('.body-main').visible(true)) {
$('.body-main').addClass("think");
}
});
JSfiddle Example: https://jsfiddle.net/Zachary1748/tqh00p9n/
You can use IntersectionObserver object like I show below so when your div element comes into view you can add a class or remove a class:
const observer = new IntersectionObserver(elements => {
elements.forEach( entry => {
// if the element is present
if(entry.isIntersecting){
removableClass.classList.add('class-to-add');
return;
}
// if not then remove the class
removableClass.classList.remove('class-to-remove');
})
})
And to observe the division:
observer.observe(document.querySelector('.any-class'));
And remember that if you want to do some transition then always put the class which shows the content and then remove it afterwards because even if the JS fails to load your content it would still be displayed.

Increase element opacity when user scrolls down the page

I've seen a lot of question related to modifying an element opacity when user scrolls but haven't found one that helps me the way I need. I have tried several formulas and haven't been able to achieve the effect I want.
I have a header with a BG image, and inside it a div that I use as an overlay, and I want it to get darker and darker smoothly (opacity increase) while the user scrolls down.
EDIT:
The desired effect is:
Opacity is by default set to 0.2 in CSS. When user starts scrolling down it will start increasing from 0.2 to 1. When user scrolls up again it will decrease from 1 (or whatever value it was) to 0.2.
Fiddle: https://jsfiddle.net/z7q2qtc6/
<div class='nice-header'>
<div class='header-overlay'></div>
</div>
CSS
.nice-header {
position: relative;
height: 300px;
background: center center;
background-size: cover;
background-image: url(http://www.boeing.com/resources/boeingdotcom/commercial/787/assets/images/marquee-787.jpg);
}
.header-overlay {
position: absolute;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgb(0,0,0);
opacity: 0.2;
}
JS
$(window).scroll(function() {
$('.header-overlay').css({
opacity: function() {
var opacity = 0;
//TODO:
//Set opacity to a higer value whilst user scrolls
return opacity;
}
});
});
You can retrieve the current scrolling position by using the .scrollTop() method.
To calculate the opacity, subtract the scrollTop value from the height of the element and then divide that by the element's height.
Example Here
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
$('.header-overlay').css({
opacity: function() {
var elementHeight = $(this).height();
return 1 - (elementHeight - scrollTop) / elementHeight;
}
});
});
If you want to account for the element's initial opacity of 0.2:
Updated Example
$('.header-overlay').css({
opacity: function() {
var elementHeight = $(this).height(),
opacity = ((1 - (elementHeight - scrollTop) / elementHeight) * 0.8) + 0.2;
return opacity;
}
});
For anyone trying to do this but in the reverse (the elements fades out as you scroll)
opacity = ((elementHeight - scrollTop) / elementHeight);
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
$('.header-overlay').css({
opacity: function() {
var elementHeight = $(this).height(),
opacity = ((elementHeight - scrollTop) / elementHeight);
return opacity;
}
});
});
.nice-header {
position: relative;
height: 300px;
background: center center;
background-size: cover;
background-image: url(http://www.boeing.com/resources/boeingdotcom/commercial/787/assets/images/marquee-787.jpg);
}
.header-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgb(0, 0, 0);
opacity: 1;
}
.dummy {
height: 900px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='nice-header'>
<div class='header-overlay'>
</div>
</div>
<div class='dummy'>
</div>
Use rbga instead of rbg and change the alpha value as the user scrolls. I'm obviously not 100% sure what effect you are going for but in most cases using rgba is a better approach than using rgb and opacity.
What are differences between RGB vs RGBA other than 'opacity'
Here is the link to another post that explains this in further detail.
Pretty new to programming. Manage to do it without JQuery
window.addEventListener(`scroll`, function (e) {
const heroOpas = this.scrollY / 1000;
if (heroOpas === 0) {
sectionHero.style.opacity = 1;
}
if (heroOpas > 0) {
sectionHero.style.opacity = `${1 - heroOpas}`;
}
});

Categories