canvas-scroll-clip
Version:
Canvas based image sequence scroll animation. Zero dependencies.
307 lines (265 loc) • 6.21 kB
HTML
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>csc.js</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@100;400;700&display=swap" rel="stylesheet">
<style>
:root {
--color-white: #fff;
--font-weight-bold: 700;
}
*,
:after,
:before {
box-sizing: border-box;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
html {
padding: 0;
margin: 0;
font-size: 16px;
background-color: #000;
margin: 0;
padding: 0;
}
body {
min-height: 100%;
height: auto;
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
overscroll-behavior-y: none;
overflow-x: hidden;
position: relative;
}
@-webkit-keyframes ani-mouse {
0% {
opacity: 1;
top: 20%;
}
20% {
opacity: 1;
top: 30%;
}
70% {
opacity: 0.5;
top: 40%;
}
100% {
opacity: 0;
top: 20%;
}
}
@-moz-keyframes ani-mouse {
0% {
opacity: 1;
top: 20%;
}
20% {
opacity: 1;
top: 30%;
}
70% {
opacity: 0.5;
top: 40%;
}
100% {
opacity: 0;
top: 20%;
}
}
@keyframes ani-mouse {
0% {
opacity: 1;
top: 20%;
}
20% {
opacity: 1;
top: 30%;
}
70% {
opacity: 0.5;
top: 40%;
}
100% {
opacity: 0;
top: 20%;
}
}
@-webkit-keyframes ani-loader {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@-moz-keyframes ani-loader {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes ani-loader {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.scroll-indicator {
letter-spacing: 1px;
font-size: 10px;
position: fixed;
left: 0;
right: 0;
bottom: 50px;
margin: 0 auto;
color: #fff;
text-align: center;
text-transform: uppercase;
z-index: 100;
}
.scroll-indicator .scroll-indicator-icon {
position: relative;
display: block;
width: 35px;
height: 55px;
margin: 0 auto 20px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border: 3px solid var(--color-white);
border-radius: 23px;
}
.scroll-indicator .scroll-indicator-icon::before {
content: "";
position: absolute;
display: block;
top: 20%;
left: 50%;
width: 4px;
margin-left: -2px;
height: 12px;
background: var(--color-white);
border-radius: 6px;
-webkit-animation: ani-mouse 2s linear infinite;
-moz-animation: ani-mouse 2s linear infinite;
animation: ani-mouse 2s linear infinite;
}
.promo {
color: var(--color-white);
font-size: 3rem;
opacity: 0;
position: absolute;
bottom: 250px;
left: 50%;
transform: translateX(-50%);
z-index: 2;
transition: opacity 300ms ease-in-out;
font-weight: var(--font-weight-bold);
}
.promo.loaded {
opacity: 1;
}
.dummy-content {
display: block;
width: 100%;
height: 500px;
}
/* CanvasScrollClip - required */
.container {
position: relative;
}
/* .csc-container {
min-height: 100%;
position: relative;
} */
.csc {
height: 100vh;
width: 100%;
position: sticky;
top: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
padding: 5rem;
}
.csc-canvas {
display: block;
max-width: 100%;
max-height: 100%;
object-fit: contain;
position: relative;
z-index: 1;
opacity: 0;
transition: opacity 300ms ease-in-out;
}
.csc--loaded .csc-canvas {
opacity: 1;
}
/* Loader - optional */
.csc::after {
content: " ";
display: block;
width: 50px;
height: 50px;
margin: 8px;
border-radius: 50%;
border: 6px double var(--color-white);
border-color: var(--color-white) transparent var(--color-white) transparent;
animation: ani-loader 1.2s linear infinite;
position: absolute;
top: 50%;
transform: translateY(-50%);
transition: opacity 300ms ease-in-out;
opacity: 0.6;
z-index: 2;
}
.csc--loaded .csc::after {
display: none;
}
</style>
</head>
<body>
<div class="dummy-content"></div>
<div class="container">
<div class="canvas-container"></div>
<div class="promo">enjoy!</div>
</div>
<div class="scroll-indicator">
<span class="scroll-indicator-icon"></span>
<span>scroll down</span>
</div>
<script type="module">
import CSC from "./js/main.js";
const element = document.querySelector('.canvas-container');
const options = {
framePath: "https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/0001.jpg",
frameCount: 140,
scrollArea: 2000
};
window.addEventListener("DOMContentLoaded", function () {
const csc = new CSC(element, options);
csc.events.on('viewport.scroll', (scrollTop) => {
const element = document.querySelector('.promo');
const screenHeight = window.innerHeight;
const when = document.documentElement.scrollHeight - innerHeight - 200;
if (scrollTop >= when) {
element.style.setProperty("opacity", "1");
} else {
element.style.removeProperty('opacity');
}
});
});
</script>
</body>
</html>