I am trying to create a scrolling animation with 2 divs and 2 images.
For lack of a better explanation (as you might have guessed from the title) I have made a quick animation showcasing what I am trying to achieve.
here is a hosted version that I made earlier. I tried to create the effect with the help of parallax scrolling, but it's not quite what I want.
It's a Zeit Now deployment, so you can append /_src to the url and take a look at the source code.
Now I am not sure if this is even the correct way to create the animation and to be honest I wouldn't know any other way that I could approach this.
So I am not asking for a fully-fledged answer without any flaws (although it would be much appreciated), but rather a nudge in the right direction.
Made this quickly so there might be some issues, I tried to make the variables somehow general so you can play with things (check this fiddle)
const body = document.body,
html = document.documentElement;
const targetImg = document.querySelector('.second');
// our image's initial height
const imgHeight = targetImg.clientHeight;
// the final value for image height (at scroll end)
const imgTargetHeight = 0;
// total height of our document
const totalHeight = Math.max(body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight);
// visible window height
const windowHeight = window.innerHeight;
// starting scroll position we want to start calculations from (at this point and before, our image's height should equal its initial height 'imgHeight')
const fromScroll = 0;
// final scroll position (at this point and after, our image's height should equal 'imgTargetHeight')
const toScroll = totalHeight - windowHeight;
window.addEventListener('scroll', function() {
// get current scroll position, these multiple ORs are just to account for browser inconsistencies.
let scrollPos = window.scrollY || window.scrollTop || document.getElementsByTagName("html")[0].scrollTop;
// force the scroll position value used in our calculation to be between 'fromScroll` and 'toScroll'
// In this example this won't have any
// effect since fromScroll is 0 and toScroll is the final possible scroll position 'totalHeight - windowHeight',
// but they don't have to be, try setting fromScroll = 100 and toScroll = totalHeight - windowHeight - 100 for example to see the difference.
// the next line is just a shorthand for:
// if (scrollPos <= fromScroll) {
// scrollPos = fromScroll;
// } else if (scrollPos >= toScroll) {
// scrollPos = toScroll;
// } else {
// scrollPos = scrollPos;
// }
scrollPos = scrollPos <= fromScroll ? fromScroll : (scrollPos >= toScroll ? toScroll : scrollPos);
// our main calculation, how much should we add to the initial image height at our current scroll position.
const value = (imgTargetHeight - imgHeight) * (scrollPos - fromScroll) / (toScroll - fromScroll);
targetImg.style.height = imgHeight + value + "px";
});
.container {
height: 200vh;
}
.img-container {
position: fixed;
width: 100vw;
height: 100vh;
text-align: center;
background: white;
overflow: hidden;
}
.second {
background: tomato;
}
img {
position: absolute;
left: 50vw;
top: 50vh;
transform: translate(-50%, -50%);
}
<div class="container">
<div class="img-container first">
<img src="https://fixedscrollingtest-takidbrplw.now.sh/luigi.png" alt="">
</div>
<div class="img-container second">
<img src="https://fixedscrollingtest-takidbrplw.now.sh/mario.png" alt="">
</div>
</div>
Related
I tried to make zoom in Zoom out on scroll. The element is zooming on scrolling but I want to make it(the zoomed element) go up once it reached the full width of the screen.
Here's my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.zoom{
height:100vh;
width:100%;
display:grid;
place-items:center;
position:fixed;
top:0;
left:0;
background: url('img/bg.png');
}
.afterzoom{
height: 200vh;
background: red;
}
</style>
</head>
<body>
<div class="zoom">
<!-- <h1>Zoom meeeee</h1> -->
<img src="img/square.png" alt="">
</div>
<div class="afterzoom"></div>
<script>
const zoomElement = document.querySelector(".zoom");
let zoom = 1;
const ZOOM_SPEED = 0.1;
document.addEventListener("wheel", function(e) {
if(e.deltaY > 0){
if (zoomElement.style.transform >= `scale(5)`) {
console.log("now scroll down");
return false;
}
zoomElement.style.transform = `scale(${zoom += ZOOM_SPEED})`;
}else{
if (zoomElement.style.transform == `scale(1)`) {
// console.log("minus");
return false;
}
zoomElement.style.transform = `scale(${zoom -= ZOOM_SPEED})`; }
});
</script>
</body>
</html>
Fiddle: https://jsfiddle.net/mayureshpitale/6etpn0vs/3/
I am trying to make something like this: http://truegossiper.com/vilson17/
A bit of a different approach using the scroll event and window.scrollY for positioning together with requestAnimationFrame (RAF).
This also works, if the image (or other content) is not squared.
The problem with the wheel event is, that it does not trigger when the user uses the scrollbar or arrow keys to scroll the page.
The problem with the scroll event is, that it executes a lot... that's why RAF is used, to only execute necessary zoom changes.
Keep in mind, this exact code works with the document width when the page is loaded. If the user resizes the window or changes the browsers zoom, the element will resize to the same scale as before. You could fix this with some extra code by using a ResizeObserver. (Remove the existing event listener, resize the Image and execute parts of the code again, every time the document is resized...)
You will notice this, when you try to view the below snippet as full page.
const zoomElement = document.querySelector('.zoom')
const fadeElement = document.querySelector('.fade')
const afterZoomElement = document.querySelector('.afterzoom')
const imgElement = document.querySelector('img')
const WIDTH = document.body.clientWidth
const HEIGHT = zoomElement.clientHeight
const IMAGE_WIDTH = imgElement.clientWidth
const IMAGE_HEIGHT = imgElement.clientHeight
const ZOOM_SPEED = 100 // Lower is faster
const ZOOM_BREAKPOINT = WIDTH / IMAGE_WIDTH // When it should stop zooming in
const IMAGE_HEIGHT_MAX = IMAGE_HEIGHT * ZOOM_BREAKPOINT
const ABSOLUTE = ZOOM_BREAKPOINT * ZOOM_SPEED // Absolute position, when the Element reached maximum size
// Fade --------------------------------------------------------------------------------------
const FADE_SPEED = 500 // Lower is faster
let fade = 1
let prev = 0
// -------------------------------------------------------------------------------------- Fade
function anim() {
let scroll = window.scrollY
let temp = scroll / ZOOM_SPEED
let zoom = temp > 1 ? temp : 1
// Only update the Elements scale, when we are below the breakpoint
if (zoom < ZOOM_BREAKPOINT) {
// Only scale the Image, so the Zoom element does not mess with the document width
imgElement.style.transform = `scale(${zoom})`
// Sets the Elements position to fixed, so it can resize without scrolling away
zoomElement.style.top = '0px'
zoomElement.style.position = 'fixed'
} else {
// Makes sure the Element always reaches Max Size
imgElement.style.transform = `scale(${ZOOM_BREAKPOINT})`
// Sets the elements position to absolute, so it will scroll with the rest of the document
zoomElement.style.position = 'absolute'
zoomElement.style.top = ABSOLUTE + 'px'
}
// Fade --------------------------------------------------------------------------------------
let dif = prev - scroll
if (zoom < ZOOM_BREAKPOINT - FADE_SPEED / ZOOM_SPEED) {
fade = 1
} else if (zoom > ZOOM_BREAKPOINT) {
fade = 0
} else {
fade += dif / FADE_SPEED
}
fadeElement.style.opacity = fade
prev = scroll
// -------------------------------------------------------------------------------------- Fade
}
// Resets scroll position on every reload
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual'
}
document.addEventListener('scroll', () => window.requestAnimationFrame(anim))
// Fade --------------------------------------------------------------------------------------
zoomElement.style.opacity = 1
// -------------------------------------------------------------------------------------- Fade
// Positions the afterZoom element right below the zoomed image
afterZoomElement.style.top = ABSOLUTE + IMAGE_HEIGHT_MAX / 2 + HEIGHT / 2 + 'px'
body {
margin: 0;
}
img {
width: 150px;
height: 150px;
background-color: black;
}
.fade {
height: 100vh;
width: 100%;
position: fixed;
top: 0;
left: 0;
background: blue;
}
.zoom {
height: 100vh;
width: 100%;
display: grid;
place-items: center;
position: fixed;
top: 0;
left: 0;
}
.afterzoom {
position: absolute;
height: 200vh;
width: 100%;
background: red;
overflow-x: auto;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div class="fade"></div>
<div class="zoom">
<img src="https://via.placeholder.com/150" alt="">
</div>
<div class="afterzoom">
<p>This should appear after the above element is fully zoomed.</p>
</div>
</body>
I got a bit carried away during coding and added some fading-in and -out to the blue background. Not necessary, but looks nice. You can remove the Fade ----- Fade parts of the code, without affecting functionality.
To alter zoom and fade speed, simply change the ZOOM_SPEED and FADE_SPEED variables.
I removed the body scrollbar and then must compensate for its lack with padding. To do this, I try to calculate the width of the scrollbar of the div block that appears after removing the body scrollbar and pass the resulting value as padding. This works in Chrome, but does not work in other browsers. Here is my code:
element = document.getElementById('div');
var scrollBarWidth = element.offsetWidth - element.clientWidth;
document.body.style.paddingRight = scrollBarWidth + 'px';
I would really appreciate a suitable solution.
It seems to work, but maybe there is a simpler solution that works in all browsers?
// Create the measurement node
var scrollDiv = document.createElement("div");
scrollDiv.className = "scrollbar-measure";
document.body.appendChild(scrollDiv);
// Get the scrollbar width
var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
console.info(scrollbarWidth); // Mac: 15
// Delete the DIV
document.body.removeChild(scrollDiv);
.scrollbar-measure {
width: 100px;
height: 100px;
overflow: scroll;
position: absolute;
top: -9999px;
}
document.body.style.paddingRight = (scrollbarWidth) + 'px';
I have a problem with a JavaScript I'm developing for my website. I have images which opens on hovering over them.
First my script calculates if the image should be displayed on the right or on the left of my window:
$("html,body").live("mousemove", function (e) {
//console.log("mousemove: "+e.pageX)
var width_window = $(window).width();
var center = width_window / 2;
if (e.pageX < center) {
side = 'left';
} else {
side = 'right';
}
});
Then, once we know on which side of the window the image will be displayed, I have another script to resize the image, depending of the height & width of my window, including the margins:
this.resizeImg = function (img, offset) {
var d = new Date();
//console.log(d, side);
var window_height = $(window).height();
var img_height = $(img).height();
var img_top = $(img).offset().top;
var window_width = $(window).width();
var img_width = $(img).width();
var img_left;
side == 'left' ? img_left = offset.left : img_left = window_width - offset.left;
console.log(window_width, img_left)
var image_resize_height = window_height - img_top - 20;
var image_resize_width = window_width - img_left - 20;
if (img_height + img_top > window_height && img_width + img_left > window_width) {
console.log("h w")
if (image_resize_width > image_resize_height) {
$(img).css('height', image_resize_height + 'px').css("width", "auto");
} else {
$(img).css('width', image_resize_width + 'px').css("height", "auto");
}
} else if (img_height + img_top > window_height) {
//console.log("h")
$(img).css('height', image_resize_height + 'px').css("width", "auto");
} else if (img_width + img_left > window_width) {
//console.log("w")
$(img).css('width', image_resize_width + 'px').css("height", "auto");
} else {
//console.log("non")
}
};
It almost works, but sometimes my images exceed the window width or height. I can't find the solution...
Here is my CSS:
.vignette {
max-height: 800px;
max-width : 800px;
z-index : 2;
top : 25px;
}
.info{
position : relative;
}
.info img {
position : absolute;
display : none;
cursor : pointer;
}
My full script in jsFiddle: http://jsfiddle.net/CrnNZ/
Here is the link to my website : http://olivierlellouche.com/
Thanks a lot for your help !
Are you taking care of the fact that you are moving the image down 25px in:
.vignette {
top : 25px;
}
The only height adjustment I see is 20px:
var image_resize_height = window_height - img_top - 20;
You may just need to subtract few more pixels to your calculations?
Or better yet:
var img_top = $(img).offset().top;
May be top of the offset area and not the raw top of the image. In which case, you still need to subtract 25px for that.
(From your website) The other thing that may be useful is to always enable, or always disable the vertical scroll-bar on the right. Or re-size the text area to be smaller than the available area when their isn't a scroll-bar. (Unfortunately, I could not get your jsfiddle to work at all and the only error from their I could view was vertical calculation errors. I could not see any horizontal errors.)
Does the problem continue if you subtract a few more pixels off the height?
I can't tell from your code but, does it place the image then re-size it? It may be better idea to calculate the size available before trying to place the image, that way it never changes sizes once it is placed.
EDIT:
After looking at your webpage with much smaller sized window I thought of something else. $(window).height() is not the same as $(document).height(). See: $(window).height() vs $(document).height You may need to calculate the remaining page differently if they are not the same.
It's easy to keep a column in my layout fixed so it's always visible, even when the user scrolls down.
It's also easy to only move the column down the page when the page is scrolled down far enough for it to be out of the viewport so it's anchored before scrolling starts.
My problem is, I have left hand column that is taller than the average window so you need to be able to scroll down to see all the content (controls) in the left column but at the same time when you scroll up you want to see the top of the controls again.
Here's a visual of what I want to accomplish:
So the left column is always occupying 100% of the height of the window but as the user scrolls down they can see the bottom of the div, and when they start to scroll up the scrolls up until it reaches the top of the window again. So no matter how far they scroll the page, the top of the div is always nearby.
Is there some jQuery magic to make this happen?
Did you mean something like this? (Demo)
var sidebar = document.getElementById('sidebar');
var sidebarScroll = 0;
var lastScroll = 0;
var topMargin = sidebar.offsetTop;
sidebar.style.bottom = 'auto';
function update() {
var delta = window.scrollY - lastScroll;
sidebarScroll += delta;
lastScroll = window.scrollY;
if(sidebarScroll < 0) {
sidebarScroll = 0;
} else if(sidebarScroll > sidebar.scrollHeight - window.innerHeight + topMargin * 2) {
sidebarScroll = sidebar.scrollHeight - window.innerHeight + topMargin * 2;
}
sidebar.style.marginTop = -sidebarScroll + 'px';
}
document.addEventListener('scroll', update);
window.addEventListener('resize', update);
#sidebar {
background-color: #003;
bottom: 1em;
color: white;
left: 1%;
overflow: auto;
padding: 1em;
position: fixed;
right: 80%;
top: 1em;
}
body {
line-height: 1.6;
margin: 1em;
margin-left: 21%;
}
It almost degrades gracefully, too…
I made a fiddle for you, hope this helps you out abit.
I detect scroll up or scroll down, and set the fixed position accordion to the direction.
http://jsfiddle.net/8eruY/
CSS
aside {
position:fixed;
height:140%;
background-color:red;
width:100px;
top:20px;
left:20px;
}
Javascript
//Detect user scroll down or scroll up in jQuery
var mousewheelevt = (/Firefox/i.test(navigator.userAgent)) ? "DOMMouseScroll" : "mousewheel" //FF doesn't recognize mousewheel as of FF3.x
$('html').bind(mousewheelevt, function(e){
var evt = window.event || e //equalize event object
evt = evt.originalEvent ? evt.originalEvent : evt; //convert to originalEvent if possible
var delta = evt.detail ? evt.detail*(-40) : evt.wheelDelta //check for detail first, because it is used by Opera and FF
if(delta > 0) {
$('aside').css('top', '20px');
$('aside').css('bottom', 'auto');
}
else{
$('aside').css('bottom', '20px');
$('aside').css('top', 'auto');
}
});
http://jsfiddle.net/KCrFe/
or this:
.top-aligned {
position: fixed;
top: 10px;
}
with
var scrollPos
$(window).scroll(function(event){
var pos = $(this).scrollTop();
if ( pos < scrollPos){
$('.sidebar').addClass('top-aligned');
} else {
$('.sidebar').removeClass('top-aligned');
}
scrollPos = pos;
});
I have implemented a parallax scrolling effect based on a tutorial I found. The effect works great. However, when I specify the background images, I am unable to control the y (vertical) axis. This is causing problems because I'm trying to set locations on multiple layered images.
Any thoughts on what's causing the problem?
Here is one external script:
$(document).ready(function(){
$('#nav').localScroll(800);
//.parallax(xPosition, speedFactor, outerHeight) options:
//xPosition - Horizontal position of the element
//inertia - speed to move relative to vertical scroll. Example: 0.1 is one tenth the speed of scrolling, 2 is twice the speed of scrolling
//outerHeight (true/false) - Whether or not jQuery should use it's outerHeight option to determine when a section is in the viewport
$('#mainimagewrapper').parallax("50%", 1.3);
$('#secondaryimagewrapper').parallax("50%", 0.5);
$('.image2').parallax("50%", -0.1);
$('#aboutwrapper').parallax("50%", 1.7);
$('.image4').parallax("50%", 1.5);
})
This is another external script:
(function( $ ){
var $window = $(window);
var windowHeight = $window.height();
$window.resize(function () {
windowHeight = $window.height();
});
$.fn.parallax = function(xpos, speedFactor, outerHeight) {
var $this = $(this);
var getHeight;
var firstTop;
var paddingTop = 0;
//get the starting position of each element to have parallax applied to it
$this.each(function(){
firstTop = $this.offset().top;
});
if (outerHeight) {
getHeight = function(jqo) {
return jqo.outerHeight(true);
};
} else {
getHeight = function(jqo) {
return jqo.height();
};
}
// setup defaults if arguments aren't specified
if (arguments.length < 1 || xpos === null) xpos = "50%";
if (arguments.length < 2 || speedFactor === null) speedFactor = 0.1;
if (arguments.length < 3 || outerHeight === null) outerHeight = true;
// function to be called whenever the window is scrolled or resized
function update(){
var pos = $window.scrollTop();
$this.each(function(){
var $element = $(this);
var top = $element.offset().top;
var height = getHeight($element);
// Check if totally above or totally below viewport
if (top + height < pos || top > pos + windowHeight) {
return;
}
$this.css('backgroundPosition', xpos + " " + Math.round((firstTop - pos) * speedFactor) + "px");
});
}
$window.bind('scroll', update).resize(update);
update();
};
})(jQuery);
Here is the CSS for one section:
#aboutwrapper {
background-image: url(../images/polaroid.png);
background-position: 50% 0;
background-repeat: no-repeat;
background-attachment: fixed;
color: white;
height: 500px;
width: 100%;
margin: 0 auto;
padding: 0;
}
#aboutwrapper .image4 {
background: url(../images/polaroid2.png) 50% 0 no-repeat fixed;
height: 500px;
width: 100%;
margin: 0 auto;
padding: 0;
}
.image3{
margin: 0 auto;
min-width: 970px;
overflow: auto;
width: 970px;
}
Both of these are being called to achieve the parallax scrolling. I really just want to more specifically control the background image locations. I've tried messing with the CSS background position and I've messed with the first javascript snippet as well. No luck.
just a quick shot, have you tried actually placing the images, either in a div or just using the img src tag to actually move the element rather than manipulating the y axis of a background image?