Apple’s product animations, significantly the scrolly teardowns (technical time period), have at all times been inspiring. However these bleeding-edge animations have at all times used JavaScript and different applied sciences. Plus, they aren’t at all times responsive (or, no less than, Apple switches to a static picture at a sure width).
I’ve been wowed by CSS’s newer scrolling animation capabilities and questioned if I might rebuild one in all these animations in simply CSS and make it responsive. (In reality, CSS certain has come a good distance since the final try on this publication.) The one I’ll be making an attempt is from the Imaginative and prescient Professional website and to see it you’ll must scroll down till you hit a black background, somewhat greater than midway down the web page. Should you’re too lazy errr… environment friendly to go look your self, and/or they resolve to alter the animation after this text goes dwell, you possibly can watch this video:
Notice: Whereas Apple’s model works in all main browsers, the CSS-only model, on the time of this writing, won’t work in Firefox.
Apple’s Animation
The very first thing we have now to do is determine what’s happening within the authentic animation. There are two main phases.
Stage 1: “Exploding” {Hardware}
Three digital parts rise in sequence from the Imaginative and prescient Professional machine on the backside of the web page. Every of the three parts is a set of two photographs that go each in entrance of and behind different parts like a sub roll round a sizzling canine bun round a bread stick. (Sure, that’s a bizarre analogy, however you get it, don’t you?)
The primary, outermost part (the sub roll) contains the frontmost and the hindmost photographs permitting it to look as if it’s each in entrance of and behind the opposite parts.
The subsequent part (the recent canine bun) wraps the third part (the bread stick) equally. This offers depth, visible curiosity, and a 3D impact, as clear areas in every picture enable the photographs behind it to indicate by.
Stage 2: Flip-As much as Eyepieces
The ultimate piece of the Imaginative and prescient Professional animation flips the machine up in a easy movement to indicate the eyepieces. Apple does this portion with a video, utilizing JavaScript to advance the video because the consumer scrolls.
Let’s recreate these, one stage at a time.
“Exploding” {Hardware}
Since Apple already created the six photographs for the parts, we will borrow them. Initially, I began with a stack of img tags in a div and used place: fastened to maintain the photographs on the backside of the web page and place: absolute to have them overlap one another. Nevertheless, once I did this, I bumped into two points: (1) It wasn’t responsive — shrinking the width of the viewport made the photographs go off display, and (2) the Imaginative and prescient Professional couldn’t scroll into view or scroll out of view because it does on the Apple website.
After banging my head towards this for a bit, I went again and checked out how Apple constructed it. That they had made every picture a background picture that was at background-position: backside middle, and used background-size: cowl to maintain it a constant facet ratio. I nonetheless wanted them to have the ability to overlap although, however I didn’t wish to pull them out of circulation the way in which place: absolute does so I set show: grid on their guardian ingredient and assigned all of them to the identical grid space.
.visionpro { /* the overarching div that holds all the photographs */
show: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.half { /* every of the photographs has a component class */
grid-area: 1 / 1 / 2 / 2;
}
As my logic professor used to say within the early aughts, “Now we’re cooking with fuel!” (I don’t actually understand how that applies right here, nevertheless it appeared acceptable. Considerably illogical, I do know.)
I then started animating the parts. I began with a scroll timeline that might have allowed me to pin the animation timeline to scrolling your complete html ingredient, however realized that if the Imaginative and prescient Professional (that means the weather holding all the photographs) was going to scroll each into and out of the viewport, then I ought to change to a view timeline in order that scrolling the ingredient into view would begin the animation reasonably than attempting to estimate a keyframe proportion to begin on the place the weather can be in view (a reasonably brittle and non-responsive strategy to deal with it).
Scrolling the Imaginative and prescient Professional into view, pausing whereas it’s animating, after which scrolling it out of view is a textbook use of place: sticky. So I created a container div that totally encapsulated the Imaginative and prescient Professional div and set it to place: relative. I pushed the container div down previous the viewport with a high margin, and set high on the imaginative and prescient professional div to 0. You possibly can then scroll up until the place: sticky held the imaginative and prescient professional in place, the animation executed after which, when the container had been solely scrolled by, it could carry the Imaginative and prescient Professional div up and out of the viewport.
Now, to deal with the part strikes. Once I first used a translate to maneuver the photographs up, I had hoped to make use of the pure order of the weather to maintain all the pieces properly stacked in my bread-based turducken. Alas, the browser’s sneaky optimization engine positioned my sub roll solely on high of my sizzling canine bun, which was solely on high of my breadstick. Fortunately, utilizing z-index allowed me to separate the layers and get the overlap that’s a part of why Apple’s model seems to be so superior.
One other downside I bumped into was that, at sizes smaller than the 960-pixel width of the photographs, I couldn’t reliably and responsively transfer the parts up. They wanted to be far sufficient away that they didn’t intervene with Stage 2, however not so far-off that they went totally out of the viewport. (The place’s a bear household and a blonde lady once you want them?) Fortunately, because it so typically does, algebra saved my tuchus. Since I’ve the scale of the full-size picture (960px by 608px), and the total width of the picture is the same as the width of the viewport, I might write an equation like beneath to get the peak and use that in my translation calculations for a way far to maneuver every part.
--stage2-height: calc(min(100vw, 960px) * 608 / 960);
Nevertheless, this calculation breaks down when the viewport is shorter than 608px and wider than 960px as a result of the width of the picture is now not equal to 100vw. I initially wrote an analogous equation to calculate the width:
--stage2-width: calc(min(100vh, 608px) * 960 / 608);
However it additionally solely works if the peak is 608px or much less, and so they each gained’t work whereas the opposite one applies. This is able to be a easy repair utilizing an “if” assertion. Whereas CSS does have an if() operate as I’m penning this, it doesn’t work in Safari. Whereas I do know this complete factor gained’t work in Firefox, I didn’t wish to knock out a complete different browser if I might assist it. So, I fastened it with a media question:
:root {
--stage2-height: calc(min(100vw, 960px) * 608 / 960);
--stage2-width: calc(min(100vh, 608px) * 960 / 608);
}
@media display and (max-height: 608px) {
:root {
--stage2-height: calc(var(--vid-width) * 608 / 960);
}
}
I patted myself on the again for my mathematical genius and problem-solving abilities till I spotted (as you smarty pants individuals have most likely already discovered) that if the peak is lower than 608px, then it’s equal to 100vh. (Sure, vh is a sophisticated unit, significantly on iOS, however for this proof of idea I’m ignoring its downsides).
So, actually all I wanted was:
:root {
--stage2-height: calc(min(100vw, 960px) * 608 / 960);
}
@media display and (max-height: 608px) {
:root {
--stage2-height: 100vh;
}
}
However no matter my mathematical tangents (Ha! Horrible math pun!), this allowed me to base my vertical translations on the peak of the Stage 2 graphics, e.g.:
translate: 0 calc(var(--stage2-height) * -1 - 25vh);
…and thus get them out of the way in which for the Stage 2 animation. That mentioned, it wasn’t good, and at viewports narrower than 410px, I nonetheless needed to make an adjustment to the heights utilizing a media question.
Flip-As much as Eyepieces
Sadly, there’s no strategy to both begin a video with simply CSS or modify the body price with simply CSS. Nevertheless, we will create a set of keyframes that adjustments the background picture over time, similar to:
/* ... */
50% {
background-image: url(imgs/video/00037.jpg);
z-index: -1;
}
51% {
background-image: url(imgs/video/00039.jpg);
z-index: -1;
}
52% {
background-image: url(imgs/video/00041.jpg);
z-index: -1;
}
/* ... */
(Since there’s, like, 60-some photographs concerned on this one, I’m not supplying you with the total set of keyframes, however you possibly can go take a look at the cssvideo keyframes within the full CodePen for the total Monty.)
The draw back of this, nonetheless, is that as an alternative of 1 video file, we’re downloading 60+ information for a similar impact. You’ll discover that the file numbers skip a quantity between every iteration. This was me halving the variety of frames in order that we didn’t have 120+ photographs to obtain. (You would possibly be capable to pace issues up with a sprite, however since that is extra proof-of-concept than a production-ready resolution, I didn’t have the persistence to sew 60+ photographs collectively).
The animation was a bit uneven on the preliminary scroll, even when operating the demo regionally.
So I added:
…for each picture, together with the part photographs. That helped loads as a result of the server didn’t need to parse the CSS earlier than downloading all the photographs.
Utilizing the identical view timeline as we do for Stage 1, we run an animation shifting it into place and the cssvideo animation and the eyepieces seem to “flip up.”
animation: vpsf-move forwards, cssvideo forwards;
animation-timeline: --apple-vp, --apple-vp;
Superb Tuning
Whereas a view timeline was nice, the animation didn’t at all times start or finish precisely once I wished it to. Enter animation-range. Whereas there’s a whole lot of choices what I used on all the .halfs was
animation-range: include cowl;
This made certain that the Imaginative and prescient Professional ingredient was contained in the viewport earlier than it began (include) and that it didn’t totally end the animation till it was out of view (cowl). This labored nicely for the components as a result of I wished them totally in view earlier than the parts began rising and since their endpoint isn’t vital they’ll hold shifting till they’re off display.
Nevertheless, for Stage 2, I wished to make sure the flip up animation had ended earlier than it went off display so for this one I used:
animation-range: cowl 10% include;
Each cowl and 10% discuss with the beginning of the animation, utilizing the cowl key phrase, however pushing its begin 10% later. The include ensures that the animation ends earlier than it begins going off display.
Right here’s all the pieces collectively:
And right here’s a video in case your browser doesn’t assist it but:
Conclusion
CSS certain has come a good distance and whereas I positively used some leading edge options there have been additionally a whole lot of comparatively current additions that made this doable too.
With scroll timelines, we will connect an animation to the scroll both of a complete ingredient or simply when a component is in view. The animation-range property allow us to fine-tune when the animation occurred. place: sticky lets us simply maintain one thing on display whereas we animate it at the same time as its scrolling. Grid format allowed overlap parts with out pulling them out of circulation. Even calc(), viewport models, customized properties, and media queries all had their roles in making this doable. And that doesn’t even depend the HTML improvements like preload. Unimaginable!
Possibly we should always add a W to WWW: The World Broad Wondrous Net. Okay, okay you possibly can cease groaning, however I’m not fallacious…
