I have been working on a new static website, it's a very simple build but has a few subtle CSS animations, triggered by isInViewport
js function.
I have noticed that Chrome renders the page with a small amount of lag on scroll, it's very jarring. Yet in Safari, Firefox it's absolutely fine.
Here is my js
for triggering when an element is in viewport.
$.fn.isInViewport = function() {
var elementTop = $(this).offset().top;
var elementBottom = elementTop + $(this).outerHeight();
var viewportTop = $(window).scrollTop();
var viewportBottom = viewportTop + $(window).height() - 100;
return elementBottom > viewportTop && elementTop < viewportBottom;
};
$(window).on('resize scroll', function() {
setTimeout(function(){
$('section').each(function() {
if ($(this).isInViewport()) {
$(this).addClass('reveal');
}
else {
}
});
}, 300);
});
Is there anything out of the ordinary I shouldn't be doing?
And here is the CSS
relevant to the animation.
.image-reveal {
position: relative;
overflow: hidden;
}
.image-reveal-cover {
background: #fcfcfc;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 5;
transform: translateX(0);
-webkit-transform: translateX(0);
transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;
-webkit-transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;
}
.image-reveal img {
position: relative;
z-index: 2;
opacity: 0;
transform: scale(1.4);
-webkit-transform: scale(1.4);
transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;
-webkit-transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;
}
section.reveal .image-reveal-cover {
transform: translateX(100%);
-webkit-transform: translateX(100%);
}
section.reveal .image-reveal img {
opacity: 1;
transform: scale(1);
-webkit-transform: scale(1);
}
Here is a codepen showing an example of the scroll jitteriness in Chrome.
Am I triggering my animations incorrectly? I thought using transform for the base of CSS movement to be relatively lag free?
Any advice is appreciated. Thank you.
Also I am using Chrome 71.0.3578.98
on Mac OS Mojave
For Chrome users, hardware acceleration can be an additional cause of lag. To disable it, click on the three-dot menu in the top-right of your browser, then select Advanced. Click System, then disable hardware acceleration from here. After doing so, you should fix YouTube lagging when you scroll in Chrome.
While developers always try their best to make everything run smooth after an upgrade, it's not always the same story on every device. And on most devices, the Android phone scrolling problem is caused by corrupted cache files in the system, bad apps, compatibility issues, software glitches, and others.
You might be experiencing slow performance issues with Chrome because it needs to be updated. Within Chrome, go to Settings window and click on Advanced > Reset and clean up. Then, click on Restore settings to their original defaults. Confirm your decision by clicking the Reset settings button in the pop-up window.
For this example i have too much concerns;
Here the example:
class ScrollControl {
constructor() {
this.sectionDOMList = document.querySelectorAll('section');
this.initListeners();
}
initListeners() {
window.addEventListener('scroll', (e) => {
this.animateSections();
});
}
animateSections() {
for (let i = 0; i < this.sectionDOMList.length; i++) {
if (this.isInViewport(this.sectionDOMList[i])) {
if(!this.sectionDOMList[i].isReveal){
this.sectionDOMList[i].isReveal = true;
this.sectionDOMList[i].classList.add('reveal');
}
}
}
}
isInViewport(element) {
const elementCenter = (element.getBoundingClientRect().top + element.getBoundingClientRect().bottom)/2;
const viewportTop = window.scrollY;
const viewportBottom = viewportTop + window.innerHeight;
return elementCenter < viewportBottom;
}
}
(function() {
new ScrollControl();
})();
/* Reset */
html {
-webkit-box-sizing: border-box;
box-sizing: border-box;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 16px;
}
*,
*:before,
*:after {
-webkit-box-sizing: inherit;
box-sizing: inherit;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
ol,
ul {
margin: 0;
padding: 0;
font-weight: normal;
}
ol,
ul {
list-style: none;
}
img {
max-width: 100%;
height: auto;
}
/* Base */
body {
width: 100%;
height: 100%;
background: white;
color: #3b3b3b;
font-family: 'Larsseit', Helvetica, Arial, sans-serif;
}
.container {
max-width: 1640px;
margin-left: 30px;
margin-right: 30px;
}
@media (min-width: 768px) {
.container {
margin-left: 80px;
margin-right: 80px;
}
}
@media (min-width: 1800px) {
.container {
margin: 0 auto;
}
}
.vc {
display: table;
height: 100%;
}
.vc-ele {
display: table-cell;
vertical-align: middle;
}
section {
padding: 400px 0;
}
section.full-vh {
position: relative;
width: 100%;
height: 100vh;
padding: 0;
}
.row {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.col-half {
width: 100%;
}
@media (min-width: 992px) {
.col-half {
width: 50%;
}
}
.row.align-center {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
/* Typography */
h1 {
font-size: 52px;
}
h2 {
font-size: 26px;
}
p {
font-family: 'Larsseit', Helvetica, Arial, sans-serif;
font-size: 16px;
font-weight: 300;
line-height: 1.5;
color: #3b3b3b;
}
/* Image reveal */
.image-reveal {
position: relative;
overflow: hidden;
}
.image-reveal-cover {
background: #fcfcfc;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 5;
transform: translateX(0);
-webkit-transform: translateX(0);
transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;
-webkit-transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;
}
.image-reveal img {
position: relative;
z-index: 2;
opacity: 0;
transform: scale(1.4);
-webkit-transform: scale(1.4);
transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;
-webkit-transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;
}
section.reveal .image-reveal-cover {
transform: translateX(100%);
-webkit-transform: translateX(100%);
}
section.reveal .image-reveal img {
opacity: 1;
transform: scale(1);
-webkit-transform: scale(1);
}
<div id="page">
<div class="container">
<section class="full-vh">
<div class="vc">
<div class="vc-ele">
<h1>Scroll down</h1>
</div>
</div>
</section>
<section>
<div class="row align-center">
<div class="col-half">
<div class="image-reveal">
<div class="image-reveal-cover"></div>
<img src="https://fearthewild.com/clients/playground/horse.jpg" alt="Horse" />
</div>
</div>
<div class="col-half">
<h2>This is my horse.</h2>
<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem
quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</p>
<p>Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem
ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
</div>
</div>
</section>
</div>
</div>
Don't listen to scroll (or resize) events and then calculate if elements are in viewport, rather use Intersection Observer (IO) for this.
IO observers one (or multiple) elements and reacts if they intersect either with each other or with the viewport.
First, you have to specify your options for your IO:
let options = {
rootMargin: '0px',
threshold: 1.0
}
let observer = new IntersectionObserver(callback, options);
In your case you need to test for intersecting with window, so no root
value needs to specified for options. We also specify that a function named callback is getting executed whenever the IO is getting triggered.
Next step is to define the elements that should be observed, in your case these would be section elements:
let targets = document.querySelectorAll('section');
targets.forEach(target => {
observer.observe(target);
});
Now everything is set up, all that's left to do is to define what should actually happen in the callback function:
let callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element
// I think you are be interested in entry.isIntersecting check.
});
};
Also check this example on how to change background color based on how much the box is visible in the viewport.
You can also use this polyfill from w3c to support older browsers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With