Tag: Drawing

Libraries for SVG Drawing Animations

In 2013, Jake Archibald introduced this cool trick of animating an SVG path to look like it’s drawing itself. It’s 2020 now, and the trick is still popular. I’ve seen it on a lot of websites I’ve visited recently. I, too, feature an animated SVG loader on my website using one of the libraries I’ll introduce below.

In a previous article, Chris Coyier wrote about how SVG path animations work under the hood, using the CSS stroke-dasharray and stroke-dashoffset properties. In this article, I want to introduce you to four JavaScript libraries that can be used to create SVG path drawing animations with fewer lines of code, like this cool example. Why a library? Because they’re ideal for complex animations involving two or more SVGs with multiple paths.

To get started, l’ll first secure an SVG to demo. Let’s use this castle from svgrepo. The castle SVG downloads as an SVG image. But, since we’re dealing with path animation, what we need is the code format of the SVG. To get this, I’ll import the file into Figma and use the “Copy as SVG” feature (Right Click → Copy/Paste → Copy as SVG) to grab the SVG code.

To successfully animate an SVG path, the SVG shape should have a fill of none and each individual SVG path must have a stroke (we’ll set it to #B2441D) and a stroke-width (set to 2px).

The animation effect we want to create is to first draw the outline (or stroke) of the SVG and then fill in the different colors. In total, there are six different fill colors used throughout the SVG, so we’ll remove the fill color from each path and give paths of the same color the same class name.

  • #695A69: color-1
  • #B2441D: color-2
  • #DFDOC6: color-3
  • #C8B2A8: color-4
  • #DE582A: color-5
  • #AO8A8A: color-6

After all the modifications, here’s what the SVG code looks like:

 <svg id="svg-castle" width="480" height="480" viewBox="0 0 480 480" fill="none" xmlns="http://www.w3.org/2000/svg">   <path d="M231.111 183.761V150.371C231.111 149.553 231.774 148.889 232.592 148.889H24  7.407C248.225 148.889 248.889 149.552 248.889 150.371V183.761L258.342 206.667H271.111  V135.556H240H208.889V206.667H221.658L231.111 183.761Z" stroke="#B2441D" stroke-width="2px" class="color-6" />   <path d="M311.111 420H288.889V455.556V468.889H311.111V455.556V420Z" stroke="#B2441D"   stroke-width="2px" class="color-1" />   <path d="M191.111 420H168.889V455.556V468.889H191.111V455.556V420Z" stroke="#B2441D" stroke-width="2px" class="color-1" />   <path d="M168.889 220V228.889V237.778H222.222V228.889H212.487L221.658 206.667H208.88   9H169.524L177.778 220H168.889Z" stroke="#B2441D" stroke-width="2px" class="color-2"/ >   <!-- etc. --> </svg>

That’s all the SVG preparation we need. Let’s look at how to achieve the desired animation with the different libraries.

Library 1: Vivus

Vivus is a lightweight JavaScript class (with no dependencies) that allows you to animate SVGs like they’re being drawn. The library is available using any of these options. To keep things simple, we’ll use a CDN link:

<script src="https://cdnjs.cloudflare.com/ajax/libs/vivus/0.4.5/vivus.min.js" integrity="sha512-NBLGIjYyAoYAr23l+dmAcUv7TvFj0XrqZoFa4i1o+F2VvF9SrERyMD8BHNnJn1SEGjl1AouBDcCv/q52L3ozBQ==" crossorigin="anonymous"></script>

Next, let’s create a new Vivus instance. It takes three arguments:

  1. The ID of the target element (the SVG)
  2. An options object with a dozen possible values
  3. A callback function that runs at the end of the animation

Looking back at our SVG code, the SVG ID is svg-castle.

new Vivus('svg-castle', {    duration: 200, type:'oneByOne' });

Now, let’s write a callback function that fills the paths with the different colors we’ve defined:

function fillPath(classname, color) {   const paths = document.querySelectorAll(`#svg-castle .$ {classname}`);   for (path of paths){     path.style.fill = `$ {color}`;   } }

The fillPath function selects all paths in the svg-castle element with the supplied classname, loops through and fills each path with the specified color. Remember in a previous step, we removed the fill from each path and gave each path a same fill class (color-1, color-2, etc.).

Next up, we call the fillPath function for the six different classnames and their corresponding colors:

function after() {   fillPath('color-1', '#695a69');   fillPath('color-2', '#b2441d');   fillPath('color-3', '#dfd0c6');   fillPath('color-4', '#c8b2a8');   fillPath('color-5', '#de582a');   fillPath('color-6', '#a08a8a') }

That’s the callback function passed to the Vivus instance. See Pen for full implementation.

Library 2: Walkway.js

Walkway is a light-weight SVG animation library for path, line and polygon elements. To start using it, we can either add the library using npm, yarn, or with a CDN link like we did with Vivus. We’ll go with the CDN link once again:

<script src="https://cdn.jsdelivr.net/npm/walkway.js/src/walkway.min.js"></script>

With Walkway, we create a new Walkway instance, passing an options object as an argument. Then, we call the draw method on the new instance and pass in an optional callback function which will be run at the end of the draw animation. Again, very much like Vivus.

We’ve already written the after callback function in the previous example, so the rest should be a piece of cake:

const svg = new Walkway({   selector: '#svg-castle',   duration: 3000, });  svg.draw(after);

Library 3: Lazy Line Painter

Lazy Line Painter is a modern JavaScript library for SVG path animation. It requires minimal code to setup. However, if a GUI is more of your thing, you can use the Lazy Line Composer which is a free online editor for SVG path animation from the same makers. The SVG will be exported as an animated SVG file that can be used directly anywhere.

The basic setup for Lazy Line Painter is similar to what we’ve already done in the other examples. First, get the library using either npm or a CDN link. Just like the previous examples, we’ll use a CDN link:

<script src="https://cdn.jsdelivr.net/npm/lazy-line-painter@1.9.4/lib/lazy-line-painter-1.9.4.min.js"></script>

Then, we initialize a new LazyLinePainter instance, which accepts two parameters — a selector (the ID of the target SVG element) and a config object. Let’s call the paint method on the new instance:

// select the svg by id let svg = document.querySelector('#svg-castle')  // define config options let options = {   strokeDash: '2, 2', } // initialize new LazyLinePainter instance let myAnimation = new LazyLinePainter(svg, options)  // call the paint method myAnimation.paint()

A full list of config options are available in the library docs. Unlike the previous libraries, we don’t pass a callback function to the paint method. Instead, we’ll listen for the complete:all event handler on the animation and then pass in the callback function.

myAnimation.on('complete:all', (event) => {after()});

We can also control when the paint method runs using event listeners like we’ve have done in the following codepen demo. Click on the castle to re-run the animation.

Library 4: Framer Motion

Framer Motion is a bit different from other libraries we’ve covered. It’s a production-ready open-source animation library for React components with tons of possible animation types. And, yes, this is from the same team behind the popular Framer prototyping tool.

First up, we’ll install the library with npm in the terminal:

npm install framer-motion

For SVG path drawing animations, Framer Motion provides a motion.path component that takes four props:

<motion.path   d={pathDefinition}   initial={{ pathLength: 1, pathOffset: 0 }}   animate={{ pathLength: 0, pathOffset: 1 }}   transition={{ duration: 2 }} />

To use it, we’ll simply convert our SVG paths to motion.path, like this:

import React from 'react'; import { motion } from "framer-motion"; const AnimatedCastle = () => {   return (     <svg id="svg-castle" width="480" height="480" viewBox="0 0 480 480" fill="non            e" xmlns="http://www.w3.org/2000/svg">       <motion.path d="M311.111 420H288.889V455.556V468.889H311.111V455.556V420Z"              stroke="#B2441D" stroke-width="2" className="color-1"        initial={{ pathLength: 1,fill:"none", opacity:0, }}        animate={{ pathLength: 0,fill:"695A69", opacity:1 }}        transition={{ duration: 2 }}       />       <motion.path d="M191.111 420H168.889V455.556V468.889H191.111V455.556V420Z"                stroke="#B2441D" stroke-width="2" className="color-2"         initial={{ pathLength: 1, fill:"none", opacity:0, }}         animate={{ pathLength: 0, fill:"#b2441d", opacity:1}}         transition={{ duration: 3 }}       />                 <!-- etc. -->     </svg>   ) }

This has to be done for each SVG path. See this demo for full implementation:

There’s a caveat though: the castle SVG has over 60 paths, which is a lot. Going through them was quite daunting for me, and I found the process to be repetitive and prone to errors. For that reason, I don’t recommend Framer Motion but I would say that it is well suited for SVGs within React components with no more than five paths. For anything more than that, go with any of the previous libraries we covered.

Conclusion

That’s a look at four JavaScript libraries we can use to get hand-drawn SVG effects.

Why didn’t we cover a CSS-only solution? While it’s possible to do, it involves a lot of code repetition. For example, it means finding the total length of each path using JavaScript or with this cool trick that sets each path length to 1, and then sets the stroke-dasharrray and stroke-dashoffset of each path to its path length.

After that, we still need to define keyframes to animate the stroke-dashoffset to zero. Then, those keyframe animations will be added to each path and with an animation-delay to offset things a bit. We also have to write six different keyframe rules to fill the paths with their respective colors. Considering that the castle has over 60 individual paths, that’s over 100 lines of CSS! Not exactly the most efficient or straightforward approach.


The post Libraries for SVG Drawing Animations appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,

How to Turn a Procreate Drawing into a Web Animation

I recently started drawing on my iPad using the Procreate app with Apple Pencil. I’m enjoying the flexibility of drawing this way. What usually keeps me from painting at home are basic things, like setup, cleaning brushes, proper ventilation, and other factors not really tied to the painting itself. Procreate does a pretty nice job of emulating painting and drawing processes, but adding digital features like undo/redo, layers, and layer effects.

Here’s a Procreate painting I made that I wound up exporting and animating on the web.

See the Pen
zebra page 2
by Sarah Drasner (@sdras)
on CodePen.

You can do this too! There are two basic animation effects we’ll cover here: the parallax effect that takes place on hover (with the ability to turn it off for those with vestibular disorders), and the small drawing effect when the page loads.

Parallax with drawing layers

I mentioned that part of the reason I enjoy drawing on the iPad is the ability to work in layers. When creating layers, I take care to keep certain “themes” on the same layer, for instance, the zebra stripes are on one layer and the dots are on own layer under beneath the stripes.

I’ll extend the drawing beyond the boundaries of where the line from the layer above ends, mainly because you’ll be able to peek around it a bit as we move the drawing around in the parallax effect. If the lines are sharp at any point, this will look unnatural.

Once I’m done creating my layers, I can export things as a Photoshop (PSD) file, thanks to Procreate’s exporting options.

The same drawing opened in Photoshop.

Then I’ll join together a few, so that I’m only working with about 8 layers at most. I use a photoshop plugin called tinyPNG to export each layer individually. I’ve heard there are better compression tools, but I’ve been pretty happy with this one.

Next, I’ll go into my code editor and create a div to house all the various images that are contained in the layers. I give that div relative positioning while all of the images inside it get absolute positioning. This places the images one on top the other.

<div id="zebra-ill" role="presentation">   <img class="zebraimg" src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/zebraexport6.png' />   <img class="zebraimg" src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/zebraexport5.png' />  … </div>
#zebra-ill {   position: relative;   min-height: 650px;   max-width: 500px; }  .zebraimg {   position: absolute;   top: 0;   left: 0;   perspective: 600px;   transform-style: preserve-3d;   transform: translateZ(0);   width: 100%;   }

The 100% width on the image will confines all of the images to the size of the parent div. I do this so that I’m controlling them all at once with the same restrictions, which works well for responsive conditions. The max-width and min-height on the parent allow me to confine the way that the div shrinks and grows, especially as when it gets dropped into a CSS Grid layout. It will need to be flexible, but have some constraints as well and CSS Grid is great for that.

Next, I add a mousemove event listener on the parent div with JavaScript. That lets me capture some information about the coordinates of the mouse using e.clientX and e.clientY.

const zebraIll = document.querySelector('#zebra-ill')  // Hover zebraIll.addEventListener('mousemove', e => {   let x = e.clientX;   let y = e.clientY; })

Then, I’ll go through each of the drawings and use those coordinates to move the images around. I’ll even apply transform styles connected to those coordinates.

const zebraIll = document.querySelector('#zebra-ill') const zebraIllImg = document.querySelectorAll('.zebraimg') const rate = 0.05  //hover zebraIll.addEventListener('mousemove', e => {   let x = e.clientX;   let y = e.clientY;      zebraIllImg.forEach((el, index) => {     el.style.transform =        `rotateX($ {x}deg) rotateY($ {y}deg)`   }) })

See the Pen
zebra page
by Sarah Drasner (@sdras)
on CodePen.

Woah, slow down there partner! That’s way too much movement, we want something a little more subtle. So I’ll need to slow it way down by multiplying it by a low rate, like 0.05. I also want to change it a just bit per layer, so I’ll use the layers index to speed up or slow down the movement.

const zebraIll = document.querySelector('#zebra-ill') const zebraIllImg = document.querySelectorAll('.zebraimg') const rate = 0.05  // Hover zebraIll.addEventListener('mousemove', e => {   let x = e.clientX;   let y = e.clientY;      zebraIllImg.forEach((el, index) => {     let speed = index += 1     let xPos = speed + rate * x     let yPos = speed + rate * y          el.style.transform =        `rotateX($ {xPos - 20}deg) rotateY($ {yPos - 20}deg) translateZ($ {index * 10}px)`   }) })

Finally, I can create a checkbox that asks the user if want to turn off this effect.

<p>   <input type="checkbox" name="motiona11y" id="motiona11y" />   <label for="motiona11y">If you have a vestibular disorder, check this to turn off some of the effects</label> </p>
const zebraIll = document.querySelector('#zebra-ill') const zebraIllImg = document.querySelectorAll('.zebraimg') const rate = 0.05 const motioncheck = document.getElementById('motiona11y') let isChecked = false  // Check to see if someone checked the vestibular disorder part motioncheck.addEventListener('change', e => {   isChecked = e.target.checked; })  // Hover zebraIll.addEventListener('mousemove', e => {   if (isChecked) return   let x = e.clientX;   let y = e.clientY;      // ... })

Now the user has the ability to look at the layered dimensionality of the drawing on hover, but can also turn the effect off if it is bothersome.

Drawing Effect

The ability to make something look like it’s been drawn on to the page has been around for a while and there are a lot of articles on how it’s done. I cover it as well in a course I made for Frontend Masters.

The premise goes like this:

  • Take an SVG path and make it dashed with dashoffset.
  • Make the dash the entire length of the shape.
  • Animate the dashoffset (the space between dashes).

What you get in the end is a kind of “drawn-on” effect.

But in this particular drawing you might have noticed that the parts I animated look like they were hand-drawn, which is a little more unique. You see, though that effect will work nicely for more mechanical drawings, the web doesn’t quite yet support the use of tapered lines (lines that vary in thickness, as is typical of a more hand-drawn feel).

For this approach, I brought the file into Illustrator, traced the lines from that part of my drawing, and made those lines tapered by going into the Stroke panel, where I selected “More options” and clicked the tapered option from the dropdown.

Screenshot of the Illustrator Stroke menu.

I duplicated those lines, and created fatter, uniform paths underneath. I then took those fat lines and animate them onto the page. Now my drawing shows through the shape:

Here’s what I did:

  • I traced with Pen tool and used tapered brush.
  • I duplicated the layer and changed the lines to be uniform and thicker.
  • I took the first layer and created a compound path.
  • I simplified path points.
  • I created clipping mask.

From there, I can animate everything with drawSVG and Greensock. Though you don’t need to, you could use CSS for this kind of animation. There’s a ton of path points so in this case, so it makes sense to use something more powerful. I wrote another post that goes into depth on how to start off creating these kinds of animations. I would recommend you start there if you’re fresh to it.

To use drawSVG, we need to do a few things:

  • Load the plugin script.
  • Register the plugin at the top of the JavaScript file.
  • Make sure that paths are being used, and that there are strokes on those paths.
  • Make sure that those paths are targeted rather than the groups that house them. The parent elements could be targeted instead.

Here’s a very basic example of drawSVG (courtesy of GreenSock):

See the Pen
DrawSVGPlugin Values
by GreenSock (@GreenSock)
on CodePen.

So, in the graphics editor, there is this clipping mask and a group that is clipped by them with fat uniform lines underneath:

From here, we’ll grab a hold of those paths and use the drawSVG plugin to animate them onto the page.

//register the plugin gsap.registerPlugin(DrawSVGPlugin);  const drawLines = () => {   gsap.set('.cls-15, #yellowunderline, .cls-13', {     visibility: 'visible'   })      const timeline = gsap.timeline({      defaults: {       delay: 1,       ease: 'circ',       duration: 2     }		     })   .add('start')   .fromTo('.cls-15 path', {     drawSVG: '0%'   }, {     drawSVG: '100%',     immediateRender: true   }, 'start')   .fromTo('#yellowunderline path', {     drawSVG: '50% 50%'   }, {     drawSVG: '100%',     immediateRender: true   }, 'start+=1')   .fromTo('.cls-13', {     drawSVG: '50% 50%'   }, {     drawSVG: '100%',     immediateRender: true   }, 'start+=1') }  window.onload = () => {   drawLines() };

And there we have it! An initial illustration for our site that’s created from a layered drawing in the Procreate iPad app. I hope this gets you going making your web projects unique with wonderful hand-drawn illustrations. If you make anything cool, let us know in the comments below!

The post How to Turn a Procreate Drawing into a Web Animation appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

A Trick That Makes Drawing SVG Lines Way Easier

When drawing lines with SVG, you often have a <path> element with a stroke. You set a stroke-dasharray that is as long as the path itself, as well as a stroke-offset that extends so far that you that it’s initially hidden. Then you animate the stroke-offset back to 0 so you can watch it “draw” the shape.

Figuring out the length of the path is the trick, which fortunately you can do in JavaScript by selecting the path and doing pathEl.getTotalLength(). It’ll probably be some weird decimal. A smidge unfortunate we can’t get that in CSS, but c’est la vie.

Here’s the trick!

You don’t have to measure the length of the path, because you can set it.

So you do like:

<path d="M66.039,133.545 ... " pathLength="1" />

That doesn’t do anything by itself (as far as I know). It’s not like that only draws part of the path — it still draws the whole thing like as if you did nothing, only now the “math” of the path length is based on a value of 1.

Now we can set the stroke-dasharray to 1, and animate the offset in CSS!

.path {   stroke-dasharray: 1;   stroke-dashoffset: 1;   animation: dash 5s linear alternate infinite; }  @keyframes dash {   from {     stroke-dashoffset: 1;   }   to {     stroke-dashoffset: 0;   } }

Which works:

See the Pen
Basic Example of SVG Line Drawing, Backward and Forward
by Chris Coyier (@chriscoyier)
on CodePen.

High five to Adam Haskell who emailed me about this a few months back.


Hey, speaking of SVG line drawing: Lemonade made a landing page for their 2019 charity that uses scroll-triggered SVG line drawing up and down the entire page. They did a behind-the-scenes look at it, which I always appreciate.

animated GIF of line drawing on Lemonade page - as page scrolls down a teddy bear shape is drawn

The post A Trick That Makes Drawing SVG Lines Way Easier appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Drawing Realistic Clouds with SVG and CSS

Greek mythology tells the story of Zeus creating the cloud nymph, Nephele. Like other Greek myths, this tale gets pretty bizarre and X-rated. Here’s a very abridged, polite version.

Nephele, we are told, was created by Zeus in the image of his own beautiful wife. A mortal meets Nephele, falls in love with her and, together, they take an adult nap™. Finally, in a strange twist, the cloud gives birth to half-human half-horse Centaur babies.

Weird, right? Personally, I can’t make heads or tails of it. Thankfully, the process for creating clouds in the browser is much more straightforward and far less risqué.

Yuan Chuan’s clouds detail. (Demo)

Recently, I discovered that developer Yuan Chuan has realized code-generated, photorealistic clouds. For me, this notion in the browser had long been the stuff of myth.

With one glance at the code in this pen we can imagine that convincing individual clouds are achievable through the use of CSS box-shadow with a <filter> element containing two SVG filters as its complement.

The photorealism we want is achieved with a delicate mix of feTurbulence and feDisplacementMap. These SVG filters are powerful, complex and offer very exciting features (including an Oscar winning algorithm)! However, under the hood, their complexity can be a bit intimidating.

While the physics of SVG filters is beyond the scope of this article, there is ample documentation available on MDN and w3.org. A very informative page on feTurbulence and feDisplacement is freely available (and offered as a chapter of this amazing book).

For this article, we will focus on learning to use these SVG filters to get spectacular results. We don’t need to delve too deeply into what is happening behind the scenes algorithmically, much in the way an artist isn’t required know the molecular structure of paint to render a stunning landscape.

Instead, let’s pay close attention to small handful of SVG attributes that are essential for drawing convincing clouds in the browser. Their use will enable us to bend these powerful filters to our will and learn how to customize them with precision in our own projects.

Let’s start with some basics

The CSS box-shadow property has five values that deserve close attention:

box-shadow: <offsetX> <offsetY> <blurRadius> <spreadRadius> <color>;

Let’s crank these values up (probably higher than what any sane developer would so that this shadow becomes a player on the stage in its own right.

(Demo)
#cloud-square {   background: turquoise;   box-shadow: 200px 200px 50px 0px #000;   width: 180px;   height: 180px; }  #cloud-circle {   background: coral;   border-radius: 50%;   box-shadow: 200px 200px 50px 0px #000;   width: 180px;   height: 180px; }

You’ve either made or seen shadow puppets, right?

Credit: Double-M

In the same way that a hand changes shape to alter the shadow, a “source shape” in the our HTML can move and morph to move and alter the shape of a shadow rendered in the browser. box-shadow duplicates the “morphing” features on the original size and border-radius. SVG filters get applied to both the element and its shadow.

<svg width="0" height="0">    <filter id="filter">     <feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="10" />     <feDisplacementMap in="SourceGraphic" scale="10" />   </filter> </svg>

This is the markup for our SVG so far. It won’t render because we haven’t defined anything visual (not to mention the zero width and height). It’s sole purpose is to hold a filter that we feed our SourceGraphic (aka our <div>). Our source <div> and its shadow are both being distorted independently by the filter.

We’ll add the essential CSS rule linking the HTML element (`#cloud-circle`) to the SVG filter using its ID:

#cloud-circle {   filter: url(#filter);   box-shadow: 200px 200px 50px 0px #fff; }

Et Voilà!

OK, so admittedly, adding the SVG filter is pretty underwhelming.

(Demo)

No worries! We have only just scratched the surface and have a lot more good stuff to look at.

Experimenting with the feDisplacementMap scale attribute

A few un-scientific experiments with this one attribute can yield dramatic results. For the moment, let’s keep the all values in feTurbulence constant and simply adjust the scale attribute of DisplacementMap.

As scale increases (by increments of 30) our source <div> becomes distorted and casts a shadow to mirror the stochastic form in which clouds appear in the sky.

<feDisplacementMap in="SourceGraphic" scale="180"/>
The scale attribute incremented by values of 30. (Demo)

OK, we’re getting somewhere! Let’s change the colors a bit to produce a more convincing cloud and to “sell” the effect.

body {   background: linear-gradient(165deg, #527785 0%, #7FB4C7 100%); }  #cloud-circle {     width: 180px;     height: 180px;     background: #000;     border-radius: 50%;     filter: url(#filter);     box-shadow: 200px 200px 50px 0px #fff; }

Now we’re getting closer to a realistic cloud effect!

Modifying the box-shadow blur value

The following suite of images shows the influence that the blur value has on box-shadow. Here, blur is increased by 10 pixels incrementally.

The cloud becomes “softer” as the blur value increases.

To give our cloud a bit of a cumulus-like effect, we can widen our source <div> a bit.

#cloud-circle {   width: 500px;   height: 275px;   background: #000;   border-radius: 50%;   filter: url(#filter);   box-shadow: 200px 200px 60px 0px #fff; }
Great, now the source element is getting in the way. 😫

Wait! We’ve widened the source element and now it’s in the way of our of the white shadow we’re calling a cloud. Let’s “re-cast” the shadow at a greater distance so that our cloud is no longer obscured by the source image. (Think of this as moving your hand away further from the wall so it doesn’t block the view of your shadow puppet.)

This is nicely achieved with a bit of CSS positioning. The <body> is the parent element for our cloud, which is statically positioned by default. Let’s “tuck” our source <div> up and out of the way with some absolute positioning. Initially, that will reposition our shadow as well, so we’ll also need to increase the distance of the shadow from the element and nudge the element a bit more.

#cloud-circle {   width: 500px;   height: 275px;   background: #000;   border-radius: 50%;   filter: url(#filter);   box-shadow: 400px 400px 60px 0px #fff; /* Increase shadow offset */   position: absolute; /* Take the parent out of the document flow */   top: -320px; /* Move a little down */   left: -320px; /* Move a little right */ }

Yes! We’ve arrived at a pretty persuasive cloud.

See the Pen
by Beau Haus (@beauhaus)
on CodePen.

What is painted to the browser is a pretty decent depiction of a cloud–But, I’m not sure…does this cloud really do justice the cloud nymph, Nephele? I’m sure we can do better!

Conveying depth with layers

Here’s what we want:

A photo of clouds against a blue sky. The clouds have shades of gray that provide depth.
Credit: pcdazero

From the look of the depth, texture and richness of the clouds in this photograph, one thing is clear: Zeus went to art school. At the very least, he must have read the The Universal Principles of Design which illustrates a powerful–yet, deceptively ordinary–concept:

[…] lighting bias plays a significant role in the interpretation of depth and naturalness, and can be manipulated in a variety of ways by designers…Use the level of contrast between light and dark areas to vary the appearance of depth.

This passage provides for us a hint as to how to we can vastly improve our own code-generated cloud. We can render our cloud with a good deal of fidelity to the clouds in our reference image by stacking layers of differing form, size and color on top of each other. All that takes is calling our filter as many times as we want layers.

<svg width="0" height="0">     <!-- Back Layer -->     <filter id="filter-back">       <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" />       <feDisplacementMap  in="SourceGraphic" scale="170" />     </filter>     <!-- Middle Layer -->     <filter id="filter-mid">       <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />       <feDisplacementMap  in="SourceGraphic" scale="150" />     </filter>     <!-- Front Layer -->     <filter id="filter-front">       <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />       <feDisplacementMap  in="SourceGraphic" scale="100" />     </filter> </svg>

Applying our layers will afford us an opportunity to explore feTurbulence and realize its versatility. We’ll choose the smoother type available to us: fractalNoise with numOctaves cranked up to 6.

<feTurbulence type="fractalNoise" baseFrequency="n" numOctaves="6"/>

What does all that mean? For now, let’s focus specifically on the baseFrequency attribute. Here’s what we get as we increase the value of n:

The lower the value, the rounder and fuzzier we get. The higher the value, the rounder and more rigid we get.

Words like turbulence, noise, frequency, and octave may seem odd and even confusing. But fear not! It’s actually perfectly accurate to analogize this filter’s effects to sound waves. We may equate a low frequency (baseFrequency=0.001) with a low, muffled noise and a rising frequency (baseFrequency=0.1) with a higher, crisper pitch.

We can see that our sweet spot for a cumulus-like effect may lie comfortably around the ~0.005 and ~0.01 range for the baseFrequency.

Adding detail with numOctaves

Incrementing numOctaves allows us to render our image in extremely granular detail. This requires a great deal of calculation, so be warned: high values are a significant performance hit. Try to resist the temptation to pump up this value unless your browser is wearing a helmet and knee-pads.

The higher the value we put into numOctaves the more granular detail give to our cloud.

The good news is that we don’t have to crank this value too high in order to produce detail and delicacy. As the array of images above shows, we can satisfy ourselves with a numOctavesvalue of 4 or 5.

Here’s the result

See the Pen
by Beau Haus (@beauhaus)
on CodePen.

Infinite variety with the seed attribute

There is much to say about the seed attribute as it offers a hint into the magic happening behind the scenes. But, for our purposes, the utility of seed can be reduced to four words: “different value, different shape.”

The Perlin Noise function (mentioned earlier) uses this value as the starting point for its random number generator. Choosing not to include this attribute will default seed to zero. When included, however, whatever value we give seed, we don’t need to worry about a performance hit.

Animation showing thr shape of a cloud changing as the seed value changes.
Different seed values produce different shapes.

The GIF above represents some of what seed has to offer. Keep in mind that each of those clouds is a layered, composite cloud. (While I have tweaked attributes for each layer, I have kept their respective seed values uniform.)

Credit: Brockenhexe

Here, with a close look at the reference image, I’ve layered 3 cloud-<div>s (of differing in opacity) onto a single base div. Through trial and error and punching in arbitrary seed values, I eventually arrived at a shape resembling the shape of the cloud in the photograph.

See the Pen
Nephele Reference Image study
by BEAU.HAUS (@beauhaus)
on CodePen.

Sky’s the limit

Of course, it would be hubris to think that the <div>s that we paint to the browser could be superior to Zeus’s, Nephele.

However, the more mystery we are able to tease out of CSS and SVG filters, the more we are empowered create something visually stunning with a high degree of fidelity to the Thunder God’s original creation. We can, then, can go on experiment further!

Reflecting Mist

Animated Reflecting mist

Alto-Cirrus Clouds

Alto-Cirrus clouds

In this article, we have just dipped our toe in an ocean of power and complexity. SVG filters can often seem overwhelming and unapproachable.

However, much like the examples found in the A Single Div project project or Diana Smith’s painting techniques, a playful and experimental approach will always rewarded with spectacular results!

I hope this gets you excited about creating a bit of photorealism on the web. I developed a little tool to help put them all to use and experiment a bit. Any questions, suggestions or advice? Ping me in the twitterverse drop a comment here.


Many thanks to Amelia Bellamy-Royds for her kind advice on this article.

The post Drawing Realistic Clouds with SVG and CSS appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]