Apple is known for its product design and innovations. It also has one of the most exciting web designs and features. One of the most interesting effects is scroll animation highlighting their products. I think there are many ways to achieve the effect, and each solution presents pros and cons of its own.
I tried to duplicate the effect using a sequence of images with CSS and Javascript. There are many resources and I few different methods found online and picked one I thought was the best. this method utilizes canvas
.
You would need a canvas element with ID so that javascript can easily call it. I used it in a bootstrap flex
container and gave it img-fluid
class to make it responsive.
<canvas id="hero-animation" class="img-fluid py-4"/>
I did not give it any style other than bootstrap class mentioned above. If you are not using the bootstrap, you might want to limit the size of image with “max-height: 100vh; max-width: 100vw;
” or something similar. It is needed so that the image does not go beyond the viewport.
Now the main course. I am using the method presented in the tutorial on CSS-Tricks.
First you will need to setup the canvas:
const canvas = document.getElementById("hero-animation");
const context = canvas.getContext("2d");
It is good to know how animation is rendered out. In animation, image sequence is usually rendered out in a following format: filename+padding###+image#
. Knowing that one should be able to create:
const currentFrame = index => ( `imgpath/${index.toString().padStart(4, '0')}.jpg')
const frameCount = 148; // total # of images
const images = [];
and pre-load to improve the performance. – example is 148 images, that is a lot of images.
const preloadImages = () => {
for (let i = 1; i < frameCount; i++) {
images[i] = new Image(); // same as document.createElement('img').
images[i].src = currentFrame(i);
}
};
preloadImages();
Now, drawing the first image
const img = new Image();
img.src = currentFrame(1);
img.onload = function(){
context.drawImage(img, 0, 0);
}
All elements are in place. We need to listen for the scroll event and get the appropriate frame – image.
const html = document.getElementsByTagName('html');
window.addEventListener('scroll', () => {
const scrollTop = html[0].scrollTop; //start
const maxScrollTop = html[0].scrollHeight - window.innerHeight; //end
const scrollFraction = (scrollTop / maxScrollTop) * 3; // control speed
const frameIndex = Math.min(
frameCount - 1,
Math.floor(scrollFraction * frameCount)
);
requestAnimationFrame(() => context.drawImage(images[frameIndex + 1], 0, 0));
});
I am still working on this but created something with added fade-in animation to other elements.
Setup html element with classes. You will need two classes – off and active
Here, I am using div
with reveal
class
<div class="text-center py-4 reveal">
<h1 class="text-light display-1 text-fu-b-i">
All New Product
</h1>
<p class="lead text-light">
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Dolorem, consequatur.
</p>
</div>
You will set the element initial class away from the intended position translateY is vertical translateX is horizontal
and make it invisible by setting opacity
to 0
on active
state, you will move it to the intended position by setting translateY
or translateX
to 0
.reveal {
position: relative;
transform: translateY(150px); // y move up
opacity: 0; // set the init opacity
transition: 1s all ease; // animation
}
.reveal.active {
transform: translateY(0); // y move up
opacity: 1; // make it visible
}
you are all set.
This is where magic happens.
function reveal() {
var reveals = document.querySelectorAll(".reveal");
for (var i = 0; i < reveals.length; i++) {
var windowHeight = window.innerHeight;
var elementTop = reveals[i].getBoundingClientRect().top;
var elementVisible = 150;
if (elementTop < windowHeight - elementVisible) {
reveals[i].classList.add("active");
} else {
reveals[i].classList.remove("active");
}
}
}
window.addEventListener("scroll", reveal);
This is easy to implement and highly effective way to make things more interesting especially if you mix things up by moving some on x axis while moving some on y axis.