Open 9am opened 1 year ago
Recently I was learning more about CSS perspective and how 3D works in CSS. With that knowledge, I built a 3D 9am-clock 404 page for this blog. Then the screensaver of Monterey MacOS came back to my mind, I remember used to think about how to do it with Javascript, viewport and stuff, then lose interest cuz there was nothing new for me. But the challenge is back: Can I do it with CSS?
The initial idea is pretty simple: A mountain slider along the Z-axis.
Line up a series of <li>
with a different Z
, those are the mountains. The skyline of the mountain can be mocked up with a simple polygon clip-path
which we'll deal with later. Now add some control to the perspective-origin
and Z
of the camera, we got something to begin with.
The first thought is to animate the translateZ()
from 0
to Infinite
, but that will need an infinite number of <li>
which is impossible. And we don't want to move the camera back after the animation, so can't do animation-iteration-count: infinite;
. So how to trick the eyes to believe there are "Infinite" mountains ahead? The answer is: Move the mountains out of viewport to the end of the list.
This is an old trick to make image sliders.
Now we have Infinite
mountains. But inevitably, The CSS animation will stop since there'll be an animation-duration
, which leads to the essential idea of this animation: We don't do css animations. If every mountain's Z
depends on the nth-child
, it will transition
to the Z
every time we move the first one to the back.
This will work cuz we define the --n
value with the nth-child
. And since the counter-values()
is still a draft. I used the idea of Kacper Kula to do this more efficiently, now we can remove the pre-defined CSS variable and create more mountains.
In the origin screensaver, you'll notice that the mountains don't align in a straight line, because usually it takes a river to shape the gap between mountains, and the river curves randomly. Thanks to CSS function sin()
, we can translate the X
base on --n
. Now the river is there.
The mimic polygon clip-path
isn't sophisticated for us now, with a little help of javascript, we can make it curvy and random like a real mountain.
Couldn't find a proper way to do this without JS, maybe there'll be a random function for CSS in the future.
In CSS, background-color
can be transitioned, so if we set the CSS color based on --n
like the Z
, we'll get a nice transition as the camera goes forward. Nevertheless, that's not true for gradient
. But we can achieve this by moving a gradient background:
I know this is not as perfect as the original screensaver, which has a rich landscape and a cloudy filter. But it is so much fun to do it with CSS. Hope there are better ways to deal with randomness and dom manipulation with limitations in the future CSS so I can get rid of the JS from this. Anyway, as a survivor of making polyfill CSS for IE, I'm amazed by the fantastic things we can do with CSS🎉
Well, hope you enjoy it. I'll see you next time.
@9am 🕘
In SVG, we have <feTurbulence>
to provide randomness without writing JS code, combined with a <feDisplacementMap>
we'll have a filter
that twists the source graphic with the channel that <feTurbulence>
gives us. The only problem is that the FPS goes down after applying a filter
to the mountain, maybe it's just my laptop. Anyway, worth a shot.
<div class="parent">
<section class="child"></section>
</div>
<svg>
<filter id="curve">
<feTurbulence baseFrequency="0.02 0.01" numOctaves="2" seed="2" />
<feDisplacementMap in="SourceGraphic" scale="30" />
</filter>
</svg>
.parent {
filter: url(#curve);
}
.child {
background: green;
clip-path: polygon(
0% 100%,
0% 0%,
20% 0%,
40% 50%,
60% 50%,
80% 0%,
100% 0%,
100% 100%
);
}