Click on SIGN UP button to trigger animation
Sign up and discover great amount of new opportunities!
If you already has an account, just sign in. We've missed you!
Don't forget to check the tutorial below!
This demo is a "remaster" of my old demo back from 2017. New version is most likely worse from UX and aethestic point of views, but this website is not about that. This website is about doing excessive stupid stuff with animations and cutting-edge tech, so don't seek any meaning here.
Original demo was based around 2 following animations:
But it had one severe limitation - it required fixed width for main container, because it was the only way to know size of the background image for nested container. Limitation is tied to the fact that the image background is located inside of second-level child relative to main container, so we can't know the relative width of the main container, without fixed pixels size.
After playing around I finally found the solution - it was css clip-path! And using it led me to next idiotic addition - now I can use some goofy polygon shapes and animate them, because why not?
So let's dive in!
Tutorials will mostly be covering a general overview of the implementation with a few code embeds here and there. Full code with comments is always available on github (linked below) and codepen.
Also, if reading SCSS code with parent references nesting is too much trouble, you can always check compiled CSS in codepen to see the final classes and styles.
Here is the initial view breakdown:
Once SIGN UP button is pressed, the state changes and s--switched class is appended to demo element, which triggers following css transitions:
As for reverse animation, everything there works automatically when you are removing the class.
In the original codepen demo, switcher is just a normal container with overflow: hidden, inside of which we can put our content, and background is done via nested element (:before) with width equal to fixed demo container width, so that you could move it during animation in the opposite direction of switcher movement, to create an effect of "revealing" the background behind it. And fluid width container isn't possible with that implementation, since 2-level nested children cannot get relative width of top-level parent (unless I am missing something).
As you already know, the solution for having fluid width container is css clip-path. Please check MDN for full reference, but here is the quick tldr:
Here is the visual breakdown of switcher container:
Here you can see x values for initial and switched states:
.demo__switcher {
--x1: calc(100% - var(--switcher-width));
--x2: calc(100% - var(--arrow-offset));
--x3: 100%;
--x4: calc(100% - var(--arrow-offset));
--x5: calc(100% - var(--switcher-width));
--x6: calc(100% - var(--switcher-width) + var(--arrow-offset));
// ... other styles
clip-path: polygon(var(--x1) 0, var(--x2) 0, var(--x3) 50%, var(--x4) 100%, var(--x5) 100%, var(--x6) 50%);
transition: clip-path var(--anim-time) var(--easing);
will-change: clip-path;
@include switched {
--x1: var(--arrow-offset);
--x2: var(--switcher-width);
--x3: calc(var(--switcher-width) - var(--arrow-offset));
--x4: var(--switcher-width);
--x5: var(--arrow-offset);
--x6: 0;
}
}
We'll also need to apply very similar clip-path values to main demo container, to match the arrow shape size, since otherwise we'll end up with white corners.
.demo__inner {
--demoX1: 0;
--demoX2: calc(100% - var(--arrow-offset));
--demoX3: 100%;
--demoX4: calc(100% - var(--arrow-offset));
--demoX5: 0;
--demoX6: 0;
// ... other styles
transition: clip-path var(--anim-time) var(--easing);
will-change: clip-path;
// clip the main container to match the arrow shape, otherwise there will be white corners
clip-path: polygon(var(--demoX1) 0, var(--demoX2) 0, var(--demoX3) 50%, var(--demoX4) 100%, var(--demoX5) 100%, var(--demoX6) 50%);
@include switched {
--demoX1: var(--arrow-offset);
--demoX2: 100%;
--demoX3: 100%;
--demoX4: 100%;
--demoX5: var(--arrow-offset);
--demoX6: 0;
}
}
Here is the rough explanation of how switcher text content is positioned and moved during the animation:
.demo__switcher-inner {
/* I'm using this sub-container with full-width to allow animating switcher content position with transforms,
instead of left/right, because of performance difference
but transforms got severe limitation - relative % values are tied to element's own width/height,
not parent's, so that's why we need this sub-container
thanks to calc, we can shift it all the way to the left minus switcher width,
which will end up an equivalent of animating from left: calc(100% - var(--switcher-width)) to left: 0 */
height: 100%;
transition: var(--transition-transform);
will-change: transform;
@include switched {
transform: translateX(calc((100% - var(--switcher-width)) * -1));
}
}
.demo__switcher-content {
// our content is always positioned in the same place on the right
// so we are just moving its parent container instead, as explained above
overflow: hidden;
position: absolute;
right: 0;
top: 0;
display: flex;
flex-direction: column;
justify-content: center;
column-gap: 20px;
width: var(--switcher-width);
height: 100%;
}
Once switcher code is ready, the core animation is pretty much done.
Please follow me on twitter for my latest demos, tutorials and cooked takes.