How to get element to transform progressively based on scroll position - javascript

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.

Related

How can I make an element fade out when a particluar percentage of a div is scrolled to?

I have an image that I've positioned: sticky; that is also animated to fade in and fade out at 6s intervals. I want the animated image to disappear completely when the parent div is scrolled to, say 90%. I've seen scripts that control fade out at a certain pixel amount from the top of the page (or div), but that won't work since the parent div's height is content dependent (and changes on every device size), I need it to be controlled by a designated percentage, not a static pixel amount.
It sounds as though intersectionObserver Would be helpful here.
You can set it so that when a percentage of an element is in view you can do something. This is set via a threshold option.
See https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API for detail.
If you post your code we have a look to see how this might help.
UPDATE: Taking the code given this snippet places a 10% high div absolutely positioned over the bottom of the text and observes when it goes in and when it goes out of the viewport.
FURTHER UPDATE the green square is made to fade out when we are towards the bottom by adding another set of keyframes, disappear.
let observation = (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {document.querySelector('.an-inner-box').style.animation = 'disappear 3s forwards 1';}// it's in view do something as we are near the bottom
else { document.querySelector('.an-inner-box').style.animation = 'emerge 6s forwards infinite';} // it's gone out of view so do something as we are not near the bottom
}
)};
let observer = new IntersectionObserver(observation, {});// no options needed as default will trigger observer on as little as 1px overlap and viewport is the default
let observed = document.querySelector('.observed');
observer.observe(observed);
/* I don't know what this does so have commented out for now. Reinstate if required of course.
$(window).scroll(function(){
$(".an-inner-box").css("opacity", 1 - $(window).scrollTop() / 1100);
});*/
.a-container {
position: relative;
width: 70%;
height: auto;
margin-top: 0;
padding-top: 0;
margin-left: auto;
margin-right: auto;
display: flex;
justify-content: center;
background-color: pink;
}
.an-outer-box {
position: absolute;
display: flex;
justify-content: center;
top: 10%;
width: 40%;
height: 90%;
background-color: red;
}
.an-inner-box {
position: sticky;
top: 40px;
width: 80px;
height: 80px;
background-color: lime;
display: flex;
justify-content: center;
opacity: 1;
animation: emerge 6s forwards infinite;
}
#keyframes emerge {
0% {
opacity: 0;
}
50%{
opacity: 1;
}
100% {
opacity: 0;
}
}
#keyframes disappear {
100% {
opacity: 0;
}
}
.text {
font-size: 36px;
width: 100%
height: auto;
z-index: 2;
}
.observed {
position: absolute;
width: 100%;
height: 10%;
bottom: 0;
}
<div class="a-container">
<div class ="an-outer-box">
<div class="an-inner-box"></div>
</div>
<div class="text">
lots of text here coweoicnwe coinc weocw own oefn won fon ofnwofimc oewncoe fnewo now nfowc ocroorfn rockm km erocn on orcnorcnorc rocn ron o gnfn rovnreo rno rcn orfn ro nvonero nro n on ornfa ovm orn qornv ofnr ofrn owrenv eorfnqw vpofnv on oerfnq ocn orinq3ponow nvr vornv orfn qwonq onw oervn ov n wofn rogne og nqogn nrowro nvo rghq3orp irogn qrovn qrvnr wovnro gnq3r orvn rvnr oanv owrvnqw ropnrq ogring aowrnv ovn reognr gon own ownv eorvnq orwn owrnworn aovnefovenr orp nvero vneov nefon qeovnerov n gpwnvan;va rvoiaernv ae;fvn erov ner;vn s;v narinovrng 3orn veovn g[ 3q]g jap q30v eoi rogh0[ n3om iq3v30530qvmv[pq rhm350[5m haq3 3i 4m q0[4gm q30m q3ivmq3p gmq3 [vm35gq3m50vqm [3m q30 q50[g 350 mq[g m30[ m 5054vq35035q[0h ]3q 3vq3[q35g9jq35]- 3 vm350gj35[ y9v 50[5ivmq 3-gj 3]-t9 3]mq35 -[g9 jq353pgq3]-rm q3-m3 [q3mq3 q35m-[yj q35y]]]]]]]]]]]
lots of text here coweoicnwe coinc weocw own oefn won fon ofnwofimc oewncoe fnewo now nfowc ocroorfn rockm km erocn on orcnorcnorc rocn ron o gnfn rovnreo rno rcn orfn ro nvonero nro n on ornfa ovm orn qornv ofnr ofrn owrenv eorfnqw vpofnv on oerfnq ocn orinq3ponow nvr vornv orfn qwonq onw oervn ov n wofn rogne og nqogn nrowro nvo rghq3orp irogn qrovn qrvnr wovnro gnq3r orvn rvnr oanv owrvnqw ropnrq ogring aowrnv ovn reognr gon own ownv eorvnq orwn owrnworn aovnefovenr orp nvero vneov nefon qeovnerov n gpwnvan;va rvoiaernv ae;fvn erov ner;vn s;v narinovrng 3orn veovn g[ 3q]g jap q30v eoi rogh0[ n3om iq3v30530qvmv[pq rhm350[5m haq3 3i 4m q0[4gm q30m q3ivmq3p gmq3 [vm35gq3m50vqm [3m q30 q50[g 350 mq[g m30[ m 5054vq35035q[0h ]3q 3vq3[q35g9jq35]- 3 vm350gj35[ y9v 50[5ivmq 3-gj 3]-t9 3]mq35 -[g9 jq353pgq3]-rm q3-m3 [q3mq3 q35m-[yj q35y]]]]]]]]]]]
lots of text here coweoicnwe coinc weocw own oefn won fon ofnwofimc oewncoe fnewo now nfowc ocroorfn rockm km erocn on orcnorcnorc rocn ron o gnfn rovnreo rno rcn orfn ro nvonero nro n on ornfa ovm orn qornv ofnr ofrn owrenv eorfnqw vpofnv on oerfnq ocn orinq3ponow nvr vornv orfn qwonq onw oervn ov n wofn rogne og nqogn nrowro nvo rghq3orp irogn qrovn qrvnr wovnro gnq3r orvn rvnr oanv owrvnqw ropnrq ogring aowrnv ovn reognr gon own ownv eorvnq orwn owrnworn aovnefovenr orp nvero vneov nefon qeovnerov n gpwnvan;va rvoiaernv ae;fvn erov ner;vn s;v narinovrng 3orn veovn g[ 3q]g jap q30v eoi rogh0[ n3om iq3v30530qvmv[pq rhm350[5m haq3 3i 4m q0[4gm q30m q3ivmq3p gmq3 [vm35gq3m50vqm [3m q30 q50[g 350 mq[g m30[ m 5054vq35035q[0h ]3q 3vq3[q35g9jq35]- 3 vm350gj35[ y9v 50[5ivmq 3-gj 3]-t9 3]mq35 -[g9 jq353pgq3]-rm q3-m3 [q3mq3 q35m-[yj q35y]]]]]]]]]]]
</div>
<div class="observed"></div>
</div>
So this maybe isn't a perfect fix but the WayPoints Library lets you embed a waypoint in your code so when you scroll past it it triggers a function. It defaults to the top of the window, but accepts an offset parameter which can be a percentage. Using this you can make it so a function is called at 90% scrolled.

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

CSS/JS image slide out animation

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;

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>

Dock a div to left of window and clicking again to original position

I have a web page that has three section i.e. Header , Mid, Footer.
In Mid div it is again divided into two parts i.e. leftdiv and rightdiv. Leftdiv has vertical menu and right div is for content holder for the respective page.
Now i want to dock leftdiv to left of the window screen on button click and when user again click of the same button the leftdiv should set as original position.
ex: if left and right div area has respectively 30 : 70 % area of main div. when i click on button the left and right div area should like 10:90 % and clicking again both div gets their original ratio.
var checker = new Boolean();
checker = true;
if (checker = true) {
$("#Test").click(function() {
$('.left-wrap').animate({
left: "10%"
}, 400);
checker = false;
});
}
if (checker != true) {
$("#Test").click(function() {
$('.left-wrap').animate({
left: "30%"
}, 400);
checker = true;
});
}
Any help will be appreciated.Thanks in advance
Since you tagged jquery in your question:
HTML code
<div class="toggle left">
Menu
</div>
<div class="toggle right">
Page contents
</div>
<button id="switch">Big monster!</button>
CSS code
div.toggle {
float: left;
padding: 20px;
box-sizing: border-box;
}
div.left {
width: 30%;
background-color: #f00;
}
div.right {
width: 70%;
background-color: #0f0;
}
div.left.switch {
width: 10%;
}
div.right.switch {
width: 90%;
}
Javascript code
$(document).ready(function () {
$('#switch').on('click', function () {
$('div.toggle').toggleClass('switch');
});
});
See it in action: http://jsfiddle.net/9xr46zqt/
EDIT
To animate, use CSS transition property on the divs:
div.toggle {
transition: width 0.25s ease-in-out;
}
See updated fiddle with animation: http://jsfiddle.net/9xr46zqt/1/
If course, you can specify anything in both the .switch and non-switched classes. If you want to animate those as well, list their properties in the transition, like: transition: width, left, color 0.25s ease-in-out, or use transition: all 0.25s ease-in-out.

Categories