滚动时垂直img-list的高效“视差效果”

时间:2017-02-15 12:25:14

标签: javascript css3 parallax

我有一个图片列表,并希望向上/向下缓慢向上/向下移动图片,同时向下/向上滚动

图像本身由“物体贴合:封面”设定。和“物体位置:中心50%;”,所以我有空间将图像向下移动到“物体位置:中心100%”的最大值;滚动时。相反,将图像移动到“物体位置:中心0”的最小值;向下滚动。

简而言之:当用户向下滚动图像列表时,每个图像应从其隐藏的顶部缓慢显示更多,反之亦然。

我在whatsapp客户端(ios)上找到了这个很好的parralax效果。当用户向下滚动时,媒体内容向上滚动一点,反之亦然。我想达到完全一样的目标。

所以我做了以下代码 - 但是,性能很差,我寻找更好的解决方案。有谁知道这个问题的更好的解决方案?也许通过使用“纯css parralax”(http://keithclark.co.uk/articles/pure-css-parallax-websites/)。这将是最好的解决方案 - 但是,我无法对我的问题采用这种技术。

您可以将其复制/粘贴到新的 index.html ,它将按原样运行。

以下是完整的工作代码(性能不佳):

(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>

1 个答案:

答案 0 :(得分:0)

我无法在csstriggers.com上找到对象位置。但是我自己检查过,一旦我用object-fit:cover更改了元素的对象位置,我就得到Recalculate stylePaint

如果您对这些内容不熟悉,这些是您的浏览器在特定事件后更新屏幕的方式。你的代码触发的事件非常繁重。现在,在每个滚动事件中,您的代码会在浏览器窗口上触发昂贵的绘制事

基本上你想要做的就是重构它,这样你的代码就不必重新绘制。您可能希望将objectPosition更改为.style.transorm = "translate3d(0, " + new_y + ", 0)";。那些不应该像现在那样触发油漆事件。

然而,尽管如此,它仍有可能触发油漆的方式。我建议您阅读有关滚动性能的内容,因为它对于堆栈溢出的解释太多了,Paul Lewis wrote这是一篇很好的文章。