11

Painting SVG Paths with Masks

 2 years ago
source link: https://frontend.horse/articles/painting-svg-paths-with-masks/?ref=sidebar
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Painting SVG Paths with Masks

Featuring Tom Miller

Written by Alex Trost

SVG graphics have an incredible amount of benefits over their raster counterparts. They’re tiny files, scale infinitely, can be animated and edited with code, and a whole lot more.

However, you can’t get the same textured feel that raster graphics can provide.

When we combine the strengths of vector and raster, we can create some charming effects. That’s exactly what Tom Miller did for his Silkscreen Squiggles CodePen.

Click anywhere on the CodePen to run it again.

In short:

  • 73 paths that get a thin stroke drawn, each with a random color.
  • 73 identical paths get a thick stroke drawn with random colors.
  • Both groups of paths are masked by the same raster image.

Let’s dig into the details!

Raster Mask

The key here is using a great mask with an alpha layer. Here’s the PNG Tom is using, set on a dark background.

Painted squiggles in white

You could achieve the basic shapes with just SVG paths, but it’s the paintbrush texture that makes this mask great.

Both groups have image as the mask, so they apply the alpha (transparency) in the same way you see above.

Here’s the pen with the mask removed.

So we see the difference a good raster masks makes, now let’s get into how Tom is drawing these paths!

Drawing with JavaScript

Beyond the elegant mask, most of the magic happens in just a few lines of JavaScript.

Let’s take a look and break it down.

var colors = ["#80475e", "#cc5a71", "#c89b7b", "#f0f757"]

window.onload =
  window.onclick =
  window.ontouchend =
    () => {
      gsap
        .timeline({
          defaults: {
            duration: 1,
            overwrite: true,
            attr: {
              stroke: () => colors[gsap.utils.random(0, colors.length - 1, 1)],
            },
          },
        })
        .fromTo(
          "#g1 path",
          { drawSVG: () => (Math.random > 0.5 ? "100% 100%" : 0) },
          { drawSVG: "0% 100%", stagger: { amount: 5, from: "random" } },
          0
        )
        .fromTo(
          "#g2 path",
          { drawSVG: () => (Math.random > 0.5 ? "100% 100%" : 0) },
          { drawSVG: "0% 100%", stagger: { amount: 4, from: "random" } },
          "-=3"
        )
    }

The first line sets the color palette.

Then the next line is a terse way to set the same anonymous function on three different events. onload makes it animate after the DOM is ready, onclick and ontouchend handle user inputs.

Now let’s dig into the GSAP.

GSAP Animations

First he creates a GSAP timeline and cuts back on duplicating code by setting some defaults.

gsap.timeline({
  defaults: {
    duration: 1, // 1 second
    overwrite: true,
    attr: {
      stroke: () => colors[gsap.utils.random(0, colors.length - 1, 1)],
    },
  },
})

Overwrite

GSAP’s overwrite setting is interesting, and the GSAP docs explain it well.

Overwriting refers to how GSAP handles conflicts between multiple tweens on the same properties of the same targets at the same time.

GSAP’s 3 Overwrite Modes

Usually you don’t need to change the default, but here Tom chooses true which makes sure everything gets wiped out if new tweens are created.

In this version I removed overwrite: true. Try clicking 5 times in quick succession and see why it’s needed.

The new tweens don’t overwrite the old ones, so it’s chaos.

Stroke color

Tom is setting a default attribute for stroke as a function instead of a value.

attr: {
    stroke: () => colors[gsap.utils.random(0, colors.length - 1, 1)],
  },

This function will get called on every path as it gets animated, returning a random color each time.

DrawSVG

Now let’s take a look at the fromTo() tween.

.fromTo(
  "#g1 path",
        { drawSVG: () => (Math.random > 0.5 ? "100% 100%" : 0) },
        { drawSVG: "0% 100%", stagger: { amount: 5, from: "random" } },
        0
      )

Because there are 73 paths that fulfill the "#g1 path" query, GSAP will be animating an array of all those elements.

The first object is the state we’re animating from. The drawSVG property sets how much of the path is filled, with a beginning and end value. A fully drawn path is "0%, 100%".

By giving a value of 0, we set the start and stop both to the very beginning. With 100%, 100% we also get no path drawn, because the start and stop are at the same place.

The difference is we’re now at the end of the line, rather than the beginning.

Stagger

Staggering a group of animations means they won’t happen at the same time, but one after another.

{
  stagger: { amount: 5, from: "random" }
}

The amount property is the total amount of seconds that get split among the staggers. This animation will take 5 seconds to start all of the tweens.

GSAP has a great feature to set which position in the array the animations will begin. Our options are "start", "center", "edges", "random", or "end".

It’s important to note that random doesn’t choose a random position to begin but instead randomizes the entire array and order of the animations.

Second group of paths

There’s not much different about the second group of paths. They’re the exact same path values, just given a larger stroke-width and start about 3 seconds into the animation.

Mostly they throw another coat of paint onto the piece and fill in any gaps the thinner paths might have left.

Wrap Up

We touched on some great GSAP techniques, plus took a closer look at the mask technique Tom used.

I love the combination of vector and raster graphics, and hope you see how much a single PNG can add to an SVG animation.

If you enjoyed this, check out when Tom taught us some great scroll animations on stream, or check out the rest of his CodePen.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK