The faces of the moon
Up in the sky, sometimes on the opposite end of a jovial presence, you might spot a new face.
A nice, soft, expression.
Surrounded by stars which differ in size and details.
Joyfully bouncing and following the approaching cursor.
You may think this a shallow copy of the radiant character, but not by the end of the article. Especially if you have the time to take a breather and wait for a handful of seconds. And maybe a handful more.
But let’s proceed in order, let’s start from the top.
Face
There is nothing inherently unique in the first visual. Just a series of overlapping <circle>s with a specific color. For the body, the eyes, but also the small craters adding detail in the bottom right edge.
Circles and a <path> element tracing the cheerful mug.
<g
fill="#d1bfae"
stroke="#d1bfae"
stroke-width="2"
stroke-linejoin="round"
stroke-linecap="round">
<path d="M -1.5 0 v 1.5 a 1.5 1.5 0 0 0 3 0 v -1.5z" />
</g> Stars
There are a few stars so distant to make the <circle> element a good fit.
<g fill="#d1bfae">
<circle cx="-48" cy="-46" r="1" />
<circle cx="-40" cy="-38" r="1.1" />
<!-- ... -->
</g> There are others, however, which shimmer with a more distinct mass.
<path
fill="#d1bfae"
stroke="#d1bfae"
stroke-width="1"
stroke-linejoin="round"
stroke-linecap="round"
d="M -4 0 l 2.5 1.5 1.5 2.5 1.5 -2.5 2.5 -1.5 -2.5 -1.5 -1.5 -2.5 -1.5 2.5z"
/> I repeat the shape here and there, in different spots and sizes, making a case for an SVG definition. Define the path early, in a <defs> block to avoid drawing the element immediately.
<defs>
<path
id="star"
stroke-width="1"
stroke-linejoin="round"
stroke-linecap="round"
d="M -4 0 l 2.5 1.5 1.5 2.5 1.5 -2.5 2.5 -1.5 -2.5 -1.5 -1.5 -2.5 -1.5 2.5z"
/>
</defs> Repeat the custom visual through the <use> element, referencing the id attribute.
<g fill="#d1bfae" stroke="#d1bfae">
<use href="#star" x="35" y="-38" />
<!-- ... -->
</g> You can place the element through the x and y attributes, but also with a translation. This option is particularly useful the moment you want to change the size.
<g fill="#d1bfae" stroke="#d1bfae">
<!-- ... -->
<use href="#star" transform="translate(-38 28) scale(0.8)" />
</g> You move the star in position, and change its scale in the precise (x, y) coordinate.
Faces
Perhaps expectedly, the mascot hides a side. For the alternative, start with a darker body and even darker details.
These are the same shapes with a gloomier fill and stroke.
-<circle r="28" fill="#e3d3ba" />
+<circle r="28" fill="#d1bfae" />
-<g fill="#d1bfae">
+<g fill="#a49191"> But wait, no round eyes? Exactly. Unlike the restless center of the galaxy, our satellite enjoys the occasional break. In a moment of pause the eyes close and all you see is an outline, a nice set of eyelashes. These are shapes shown in stroke only.
<g
fill="none"
stroke="#a49191"
stroke-linejoin="round"
stroke-linecap="round">
<!-- ... -->
</g> Start with an arc.
<path stroke-width="1.5" d="M -4 0 a 4 4 0 0 0 8 0" /> Continue with thinner lines.
<g stroke-width="1">
<!-- ... -->
</g> A line escaping the semicircle from below.
<g stroke-width="1">
<path d="M 0 4 v 2" />
<!-- ... -->
</g> And then the same segment, rotated at different angles.
<g stroke-width="1">
<path d="M 0 4 v 2" />
<path transform="rotate(-60)" d="M 0 4 v 2" />
<path transform="rotate(60)" d="M 0 4 v 2" />
<path transform="rotate(-30)" d="M 0 4 v 2" />
<path transform="rotate(30)" d="M 0 4 v 2" />
</g> As you rotate the path, from the center of the arc, the vertical piece leaves the area now on the left, now on the right, completing the rather convincing visual.
Since you need a pair, it helps to rehearse the logic introduced with the stars. Define the shape, this time with the group element wrapping around the multiple <path>s.
<defs>
<g
id="eyelash"
fill="none"
stroke-width="1"
stroke-linejoin="round"
stroke-linecap="round"
>
<!-- ... -->
</g>
</defs> Use and repeat.
<g stroke="#a49191">
<use href="#eyelash" x="-10" y="-4" />
<use href="#eyelash" x="10" y="-4" />
</g> In the top half section, the two complete the somber look.
Clip
We have two faces, but show only one at a time. The goal is to move from one to the other, slowly, but smoothly. This calls for a CSS animation, but first, a way to hide SVG elements.
Indeed, the moment you draw the two faces.
<g>
<circle r="28" fill="#d1bfae" />
<!-- ... -->
</g>
<g>
<circle r="28" fill="#e3d3ba" />
<!-- ... -->
</g> You’ll see only the last one.
You see the lighter variant, but the other side is drawn. Just earlier. Just behind.
To hide the bright face, and grant that long-awaited break, SVG offers the <clipPath> element.
Technically, you don’t need to add the element with the other definitions — a clip doesn’t show anything on the screen. That being said, I like to keep things separate, I prefer to group the helper structures in between the <defs> tags.
<defs>
<clipPath id="clip-moon">
<!-- ... -->
</clipPath>
</defs> Regardless, you create a clip and then apply the same through the clip-path attribute, say on the lighter, later face.
<g clip-path="url(#clip-moon)">
<circle r="28" fill="#e3d3ba" />
<!-- ... -->
</g> The area described by the clip dictates what is actually shown on screen. Shapes falling inside of this area will be visible, while others will be, well, clipped.
In this instance, the result is that the bright face completely disappears.
The <clipPath> is empty, there is no clippable area, and the entire face is removed from sight.
<clipPath id="clip-moon">
<!-- clip area -->
</clipPath> A good measure for the clip is the area described by the larger circle. What was the radius, however? We could look up the value, but this prompts a tweak to the SVG syntax.
We are going to repeat the circle at least thrice, in the clip and in each of the two faces. We are going to use the shape again and again. It is therefore convenient to fall back to the <defs> block. It is convenient to define the circle once and for all.
Define.
<defs>
<circle id="circle-moon" r="28" />
</defs> Use:
in the clip
<clipPath id="clip-moon"> <use href="#circle-moon" /> </clipPath>for the two faces
<use href="#circle-moon" fill="#d1bfae" /> <use href="#circle-moon" fill="#e3d3ba" />
Allow to change the position of the clipped face.
<g clip-path="url(#clip-moon)">
<g transform="translate(5 -5)">
<use href="#circle-moon" fill="#e3d3ba" />
<!-- ... -->
</g>
</g> And there you have it, a momentous reveal.
Drag the handles to translate the face.
Except that, something is quite off…
The faces describe the sides of the same body and, barring a minor interaction, are not supposed to stray from the center. What should move is the area revealing the hidden shapes. What should move is the clip.
<clipPath id="clip-moon" transform="translate(5 -5)">
<use href="#circle-moon" />
</clipPath> Drag the handles to translate the clip.
That’s the result we were looking for.
Transition
Updating the position manually is already entertaining, but with CSS we are able to change the values over time.
Target the clip, be it through the element or a selector of your choosing, and apply the animation.
clipPath {
animation: translate 20s infinite;
} Fittingly, create the animation to have the clip move from the top right, pause in the center, and then retreat in the bottom left corner.
@keyframes translate {
0%,
12.5% {
transform: translate(50px, -50px);
}
37.5%,
62.5% {
transform: translate(0px, 0px);
}
87.5%,
100% {
transform: translate(-50px, 50px);
}
} The perfect point to add the stars back, perhaps the interaction as well. Culminating in the finally unique presence in the sky.