Skip to content

Instantly share code, notes, and snippets.

@landsurveyorsunited
Created January 21, 2024 03:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save landsurveyorsunited/2f1fd49df23a61e554a083ffc56b73cc to your computer and use it in GitHub Desktop.
Save landsurveyorsunited/2f1fd49df23a61e554a083ffc56b73cc to your computer and use it in GitHub Desktop.
Web Gears ⚙️ (Scroll-driven animations)
<nav>
<a href="https://landsurveyorsunited.com" target="_blank">
<img style="width:50px;"class="w-9" src="https://storage.ning.com/topology/rest/1.0/file/get/12222126291?profile=original" alt="Logo">
</a>
</nav>
<div class="belt"></div>
<div class="gears">
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 146 146">
<path fill-rule="evenodd" clip-rule="evenodd" d="m0 68 13.306-1.094a59.756 59.756 0 0 1 2.202-11.126L3.643 49.683l3.827-9.238 12.705 4.079a60.097 60.097 0 0 1 6.308-9.424l-8.637-10.183 7.07-7.071 10.185 8.637a60.15 60.15 0 0 1 7.65-5.311L38.597 8.236l9.239-3.827 6.018 11.71a59.682 59.682 0 0 1 13.052-2.813L68 0h10l1.094 13.306a59.683 59.683 0 0 1 13.052 2.813l6.018-11.71 9.239 3.827-4.154 12.936a59.613 59.613 0 0 1 3.728 2.368 60.13 60.13 0 0 1 3.922 2.943l10.184-8.637 7.071 7.07-8.637 10.185a60.086 60.086 0 0 1 6.308 9.423l12.705-4.08 3.827 9.24-11.864 6.096a59.682 59.682 0 0 1 2.201 11.126L146 68v10l-13.306 1.094a59.762 59.762 0 0 1-2.201 11.126l11.864 6.097-3.827 9.238-12.705-4.079a60.134 60.134 0 0 1-6.18 9.267l-.051.063-.077.093 8.637 10.184-7.071 7.071-10.184-8.637a60.158 60.158 0 0 1-7.42 5.176l-.152.09-.078.045 4.154 12.936-9.239 3.827-6.018-11.71a59.67 59.67 0 0 1-13.052 2.813L78 146H68l-1.094-13.306a59.67 59.67 0 0 1-13.052-2.813l-6.018 11.71-9.239-3.827 4.154-12.936a60.132 60.132 0 0 1-7.65-5.311l-10.184 8.637-7.071-7.071 8.637-10.184a60.072 60.072 0 0 1-6.308-9.423L7.47 105.555l-3.827-9.238 11.864-6.097a59.733 59.733 0 0 1-2.201-11.126L0 78V68Zm73 23c9.941 0 18-8.059 18-18s-8.059-18-18-18-18 8.059-18 18 8.059 18 18 18Z" fill="currentColor" />
</svg>
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
<path fill-rule="evenodd" clip-rule="evenodd" d="m68.001 10.593-8.587-6.107-6.59 6.458a32.084 32.084 0 0 0-6.063-1.93L45.307 0H34.693l-1.456 9.022a32.435 32.435 0 0 0-6.06 1.923l-6.591-6.458-8.587 6.106 4.24 8.135a31.65 31.65 0 0 0-3.745 5.044L3.28 22.34 0 32.22l8.312 4.141a30.607 30.607 0 0 0 .005 6.236L0 46.74l3.28 9.881 9.208-1.432a30.966 30.966 0 0 0 3.748 5.047l-4.238 8.131 8.588 6.107 6.59-6.457a32.083 32.083 0 0 0 5.89 1.892L34.692 80h10.614l1.63-10.099a32.441 32.441 0 0 0 5.722-1.816l7.38 7.23 8.586-6.107-4.745-9.106a31.655 31.655 0 0 0 3.626-4.914l9.214 1.433 3.28-9.88-8.312-4.142c.212-2.079.208-4.169-.005-6.235L80 32.22l-3.28-9.881-9.208 1.432a30.972 30.972 0 0 0-3.748-5.048l4.237-8.13ZM40 51c6.075 0 11-4.925 11-11s-4.925-11-11-11-11 4.925-11 11 4.925 11 11 11Z" fill="currentColor" />
</svg>
</div>
<header>
<h1>Scroll to work 👉</h1>
</header>
<main>
<section>
<h2>Use scroll animation to rotate the gears.</h2>
<iframe id="iframe1" src="your-iframe-source-url-1.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe1')">Full Screen</button>
</section>
<section>
<h2>Rotate the small gear twice (10 tooth).</h2>
<iframe id="iframe2" src="your-iframe-source-url-2.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe2')">Full Screen</button>
</section>
<section>
<h2>Rotate the big gear 0.625 times that (16 tooth).</h2>
<iframe id="iframe3" src="your-iframe-source-url-3.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe3')">Full Screen</button>
</section>
<section>
<h2>fin.</h2>
<iframe id="iframe4" src="your-iframe-source-url-4.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe4')">Full Screen</button>
</section>
<section>
<h2>Use scroll animation to rotate the gears.</h2>
<iframe id="iframe1" src="your-iframe-source-url-1.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe1')">Full Screen</button>
</section>
<section>
<h2>Rotate the small gear twice (10 tooth).</h2>
<iframe id="iframe2" src="your-iframe-source-url-2.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe2')">Full Screen</button>
</section>
<section>
<h2>Rotate the big gear 0.625 times that (16 tooth).</h2>
<iframe id="iframe3" src="your-iframe-source-url-3.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe3')">Full Screen</button>
</section>
<section>
<h2>fin.</h2>
<iframe id="iframe4" src="your-iframe-source-url-4.html" style="width:100%; height:100%;" loading="lazy" frameborder="0"></iframe>
<button onclick="toggleFullScreen('iframe4')">Full Screen</button>
</section>
</main>
<footer>jf &copy; 2024 ┬┴┬┴┤•ᴥ•ʔ├┬┴┬┴</footer>
<script>
function toggleFullScreen(iframeId) {
var iframe = document.getElementById(iframeId);
if (!document.fullscreenElement) {
if (iframe.requestFullscreen) {
iframe.requestFullscreen();
} else if (iframe.mozRequestFullScreen) { /* Firefox */
iframe.mozRequestFullScreen();
} else if (iframe.webkitRequestFullscreen) { /* Chrome, Safari & Opera */
iframe.webkitRequestFullscreen();
} else if (iframe.msRequestFullscreen) { /* IE/Edge */
iframe.msRequestFullscreen();
}
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) { /* Firefox */
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) { /* Chrome, Safari & Opera */
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) { /* IE/Edge */
document.msExitFullscreen();
}
}
}
</script>
import gsap from 'https://cdn.skypack.dev/gsap@3.12.0'
import ScrollTrigger from 'https://cdn.skypack.dev/gsap@3.12.0/ScrollTrigger'
if (!CSS.supports('animation-timeline: scroll()')) {
gsap.registerPlugin(ScrollTrigger)
/**
* Two animations.
* 1. Belt translation.
* 2. Gear rotation,
* */
const bodyScroller = {
ease: 'none',
scrollTrigger: {
scroller: 'body',
scrub: true,
start: 0,
end: document.body.scrollHeight,
},
}
// 1. Belt translation
const belt = document.querySelector('.belt')
gsap.to('.belt', {
translateY:
(parseInt(
getComputedStyle(document.documentElement).getPropertyValue(
'--gutter-size'
),
10
) /
6) *
-20,
...bodyScroller
})
// 2. Gear rotation
gsap.to('.gears svg', {
rotate: (index) => {
return index === 0 ? 720 * 10 / 16 * -1 : 720
},
...bodyScroller,
})
}
@font-face {
font-family: 'Geist Sans';
src: url('https://assets.codepen.io/605876/GeistVF.ttf') format('truetype');
}
*,
*:after,
*:before {
box-sizing: border-box;
}
:root {
--gutter-size: 120px;
}
body {
min-height: 100vh;
font-family: "Geist Sans", "SF Pro Text", "SF Pro Icons", "AOS Icons", "Helvetica Neue", Helvetica, Arial, sans-serif, system-ui;
padding-inline: var(--gutter-size);
font-weight: 80;
color: hsl(0 0% 10%);
}
body::after {
content: "";
width: var(--gutter-size);
position: fixed;
top: 0;
bottom: 0;
right: 0;
background: hsl(0 0% 80%);
}
.belt {
--color: white;
--size: calc(var(--gutter-size) / 6);
content: "";
width: var(--size);
position: fixed;
top: 0;
left: calc(100% - var(--gutter-size));
background:
linear-gradient(var(--color), var(--color)) 0 0 / calc(var(--size) / 2) var(--size) no-repeat repeat,
linear-gradient(-10deg, transparent 80%, var(--color) 81%) 0 0 / calc(var(--size) * 0.8) var(--size) no-repeat repeat,
linear-gradient(10deg, var(--color) 20%, transparent 21%) 0 0 / calc(var(--size) * 0.8) var(--size) no-repeat repeat;
filter: drop-shadow(0 0 6px hsl(0 0% 50%));
clip-path: inset(0 -200% 0 0);
height: calc(100vh + (var(--size) * 20));
z-index: 2;
}
header {
height: 100vh;
display: grid;
place-items: center;
}
section {
height: 100vh;
width: 100%;
display: grid;
place-items: center;
}
h2 {
font-size: clamp(2rem, 1vw + 1rem, 6rem);
font-weight: 80;
color:orange;
}
main {
width: 80ch;
max-width: 100%;
margin: 0 auto;
padding: 0 1rem;
}
.gears {
color: hsl(0 0% 40%);
position: fixed;
top: 0;
right: 0;
width: var(--gutter-size);
z-index: 2;
filter: drop-shadow(0 1px 2px hsl(0 0% 50%));
}
h1 {
font-size: clamp(2rem, 6vw + 1rem, 12rem);
text-transform: uppercase;
font-family: Impact;
padding: 0 1rem;
color: white;
}
nav {
width: 48px;
position: fixed;
bottom: 2rem;
right: calc(var(--gutter-size) * 0.5);
z-index: 10;
translate: calc(50% + (var(--gutter-size) / 12)) 0;
opacity: 0.75;
}
nav a {
color: hsl(0 0% 10%);
}
footer {
padding: 2rem 1rem;
text-align: center;
}
/**
* 10 tooth vs. 16 tooth cog
* So the smaller moves at 1.6 times the rate and the bigger is 10 / 16
* */
.gears svg:first-of-type {
width: 96%;
position: absolute;
left: 100%;
translate: -80% -22%;
}
.gears svg:last-of-type {
width: 66%;
translate: 16% 102%;
position: absolute;
top: 0;
left: 0;
}
@media(max-width: 600px) {
:root {
--gutter-size: 56px;
}
body {
padding-inline: unset;
padding-right: var(--gutter-size);
}
}
@supports (animation-timeline: scroll()) {
.gears svg:first-of-type {
--end: calc((720 * (10 / 16)) * -1deg);
}
.gears svg:last-of-type {
--end: 720deg;
}
.gears svg {
animation: work both linear;
animation-timeline: scroll();
}
@keyframes work {
to { rotate: var(--end, 360deg); }
}
.belt {
animation: belt both linear;
animation-timeline: scroll();
}
@keyframes belt {
to { translate: 0 calc(var(--size) * -20); }
}
}
body {
background-color: #111827!important;
color: #FFF;
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
font-family: "SF Pro Text", "SF Pro Icons", "AOS Icons", "Helvetica Neue", Helvetica, Arial, sans-serif, system-ui;
background: hsl(0 0% 0%);
gap: 2rem;
}
body::before {
--line: hsl(0 0% 95% / 0.25);
content: "";
height: 100vh;
width: 100vw;
position: fixed;
background:
linear-gradient(90deg, var(--line) 1px, transparent 1px 10vmin) 0 -5vmin / 10vmin 10vmin,
linear-gradient(var(--line) 1px, transparent 1px 10vmin) 0 -5vmin / 10vmin 10vmin;
mask: linear-gradient(-15deg, transparent 30%, white);
top: 0;
z-index: -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment