I have a list of images and want to move the images slowly up/down while scrolling down/up.
The images itself are set by "object-fit: cover;" and "object-position: center 50%;", so I have space to move the image down to the maximum of "object-position: center 100%;" while scrolling up. And the opposite, to move the image up to the minimum of "object-position: center 0;" while scrolling down.
For short: When user scrolls the list of images down, then each image should slowly show more from its hidden top and vice versa.
I find this nice parralax-effect on whatsapp client (ios). When user is scrolling down, the media content is scrolling a little up and vice versa. I want to achieve exactly the same.
So I made the following code - it works, however, performance is bad and I look for a better solution. Does anyone know a better solution for this problem? Maybe by using "pure css parralax" (http://keithclark.co.uk/articles/pure-css-parallax-websites/). This would be the best solution - however, I cannot adopt this technique to my problem.
You can copy/paste it to a new index.html and it will work as it is.
Here is the full working code (with bad performance):
(function() {
document.addEventListener('DOMContentLoaded', function() {
/** returns true, if node is in viewport.
*/
function node_is_in_viewport(element) {
var rect = element.getBoundingClientRect();
var html = document.documentElement;
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || html.clientHeight) &&
rect.right <= (window.innerWidth || html.clientWidth)
);
}
/** returns "y-value" of css-property "object-position"
*/
function getObjectPosY(translateString){
var n = translateString.indexOf('%');
var n1 = translateString.lastIndexOf("%");
var res = parseInt(translateString.slice(n+1,n1));
return res;
}
var list_img = document.querySelector('.list-img');
var pos_list = list_img.scrollTop;
var all_imgs = document.querySelectorAll('.list-img>li img');
console.log('actual position of the list:');
console.log(pos_list);
list_img.addEventListener('scroll', function(e){
var scroll = this.scrollTop;
// positive number means "scroll down"
// negative number means "scroll up"
var offsetScrollTreshold = scroll - pos_list; // unused
// move image 1 unit up/down
var offsetScroll = 3;
var speed = 1;
console.log('position of list (pos_list):');
console.log(pos_list);
console.log('scroll:');
console.log(scroll);
console.log('offset (scroll - pos_list):');
console.log(offsetScrollTreshold);
if(scroll > pos_list) {
console.log('User is scrolling DOWN');
console.log('I want to show more from the hidden top of all the images within the actual viewport while user is scrolling down. But each image should not exceed its original image size.');
list_img.classList.add('scroll-down');
list_img.classList.remove('scroll-up');
[].forEach.call(all_imgs, function(node) {
var node_in_view = node_is_in_viewport(node);
// if image is in viewport move it up to "make a parallax effect"
if(node_in_view){
console.log('-----');
console.log('actual img to move up (new Y must be lower):');
console.log(node.src);
var old_y = 0;
var theCSSprop = window.getComputedStyle(node, null).getPropertyValue('object-position');
if( theCSSprop.indexOf('%')!=-1 ){
console.log(theCSSprop);
// returns the actual "y"-position of the image
old_y = getObjectPosY(theCSSprop);
console.log('old_y:');
console.log(old_y);
}
// User is scrolling down, so I want to view more from the "top of the image" which is actually a little hidden (because of object-fit: center 50%)
// to view more from the top of the image, I have to set the y-value (actually: 50%) lower, but not more than 0.
if(old_y > 0 && old_y <= 100){
console.log('scroll image "slowly" up while user is scrolling down (parallax effect)');
var new_y = old_y - offsetScroll * speed;
if(new_y<0){
new_y = 0;
}
console.log('new_y:');
console.log(new_y);
node.style.objectPosition = '50% '+ new_y + '%';
}
}
});
}
else {
console.log('User is scrolling UP');
console.log('I want to show more from the hidden bottom of all the images within the actual viewport while user is scrolling up. But each image should not exceed its original image size.');
list_img.classList.remove('scroll-down');
list_img.classList.add('scroll-up');
[].forEach.call(all_imgs, function(node) {
var node_in_view = node_is_in_viewport(node);
// if image is in viewport move it down to "make a parallax effect"
if(node_in_view){
console.log('actual img to move down (new Y must be higher):');
console.log(node.src);
var theCSSprop = window.getComputedStyle(node, null).getPropertyValue('object-position');
if(theCSSprop.indexOf('%')!=-1){
console.log(theCSSprop);
// returns the actual "y"-position of the image
old_y = getObjectPosY(theCSSprop);
console.log('old_y:');
console.log(old_y);
}
// User is scrolling up, so I want to view more from the "bottom of the image" which is actually a little hidden (because of object-fit: center 50%)
// to view more from the bottom of the image, I have to set the y-value (actually: 50%) higher, but not more than 100%.
if(old_y >= 0 && old_y < 100){
console.log('scroll image "slowly" down while user is scrolling up (parallax effect)');
var new_y = old_y + offsetScroll * speed;
if(new_y>100){
new_y = 100;
}
console.log('new_y:');
console.log(new_y);
node.style.objectPosition = '50% '+ new_y + '%';
}
}
});
}
pos_list = scroll;
console.log('pos_list:');
console.log(pos_list);
});/*end eventlistener scroll */
}); /*end .DOMContentLoaded */
}());
body, html, #ctn_infinity, .list-img{
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
.list-img{
height: 90vh;
overflow: auto;
}
.list-img{
list-style: none;
width: 400px;
}
.list-img>li{
display: flex;
flex-direction: column;
border: 2px solid black;
margin: 16px auto;
}
.list-img div{
background: yellow;
}
.list-img img{
object-fit: cover;
width: 100%;
/* original image size: 400 width x 200 height */
/* I use object position center: when scrolling up or down, images should "slowly" show its hidden part of its images (max. 25px top or 25px down) */
height: 150px;
/* Unfortunately, I cannot use "transform: translateY(-5px);", because this will move the whole img-container instead of the img itself up/down" */
object-position: center 50%;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTML-TEST</title>
</head>
<body>
<div id="ctn_infinity">
<ul class="list-img">
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
<li><img src="http://lorempixel.com/400/200">
<div>lorem epsum..</div></li>
</ul>
</div>
</body>
</html>