Home Web Front-end CSS Tutorial The Two Lines of CSS That Tanked Performance (fps to ps)

The Two Lines of CSS That Tanked Performance (fps to ps)

Sep 03, 2024 am 11:09 AM

I recently released Learn WCs and If you’ve seen it, you’ve likely noticed the animation in the background, where the coloured circles move diagonally across the screen. It looks like this:

It works nicely on Chrome and Safari, but I noticed a severe drop in performance on Firefox.

The performance was so bad, that I straight up disabled this animation in Firefox.

How does the animation work?

The animation is built using two nested divs. The outer div is the first child of the site’s body tag.

<body>
    <div class="background-mask">
        <div class="background-gradient"></div>
    </div>

    <!-- Rest of content -->
</body>
Copy after login

The .background-gradient element is responsible for creating a gradient that spans the entire width and height of its parent container. Like so:

The Two Lines of CSS That Tanked Performance (fps to ps)

The outer .background-mask is responsible for two things:

  1. It sets the position to fixed, and makes the container fill the entire dimensions of the viewport.
  2. Creates a dotted mask over the gradient

This ensures that the colour of the dots is the colour of the gradient directly underneath it:

The Two Lines of CSS That Tanked Performance (fps to ps)

Here’s the CSS for everything I described above:

.background-mask {
    --mask-size: 24px;

    /* Position Styles */
    position: fixed;
    width: 100%;
    height: 100%;
    z-index: -1;

    /* Mask Styles */
    mask-image: radial-gradient(black 2px, transparent 2px);
    mask-size: var(--mask-size) var(--mask-size);
    mask-position: 0px 0px;
    animation: mask-move 3s infinite linear;
}

.background-gradient {
    background: var(--red);
    background-image: var(--gradient);
    width: 100%;
    height: 100%;
}

@keyframes mask-move {
    0% {
        mask-position: 0px 0px;
    }

    100% {
        mask-position: var(--mask-size) var(--mask-size);
    }
}

@media (prefers-reduced-motion: reduce) {
    .hero-background-mask {
        animation: none;
    }
}
Copy after login

If you’re interested in learning more about masks in CSS, then I can recommend this comprehensive post by Ahmad Shadeed

What’s causing this drop in performance?

Not all CSS properties animate equally. Without going too much into how the browser renders HTML to the page (though I’ve outlined it here), there are a handful of stages it goes through. The three stages that we’re interested in are:

  • Layout - When the browser calculates the size and positions of the elements on the page
  • Paint - Draws all the visual aspects of the page, like images, colors, shadows, etc
  • Composite - Layering the elements on top of one another in the correct order

The order of the pipeline looks like this:

Layout → Paint → Composite

The layout and paint processes can be CPU-intensive, so it’s important to try and reduce the amount of times your CSS triggers the stages in the pipeline*.* The browser helps in some part by optimising performance for certain properties, some skip entire stages of the rendering pipeline and others can leverage hardware acceleration to move computation from the CPU to the GPU.

Animating certain properties, like translate and opacity , both avoids triggering a layout and uses hardware acceleration.

Sadly, this is not the case when animating mask-position. I took a look at Chrome and saw that the paint count for the background div was increasing on every frame. After a few seconds it had already triggered a paint over 1,000 times.

The Two Lines of CSS That Tanked Performance (fps to ps)

Even with this high paint count, the animation on Chrome feels smooth. However, it feels super janky on Firefox. Annoyingly, I couldn‘t find a way to measure the paint count on Firefox, so any assumptions I make about Firefox’s poor performance is purely conjecture.

What I did notice is that the animation is fine for small devices, but gets worse as the size of the screen increases. My working theory is that Firefox doesn’t batch the layout triggers for each the 24x24 masks, which causes the FPS to tank when more 24x24 masks are present. Again, I might be completely wrong here.

How did I fix this?

Instead of animating badly optimised CSS properties like mask-position , I needed to lean on the more performant properties, like translate.

The solution wasn’t to move the masks by 24px, but to instead move the entire background element using the translate property.

From an abstract standpoint, this is how the animation looks:

Here’s the two line change in the CSS:

/* --mask-size = 24px */

@keyframes mask-move {
    0% {
        transform: translate(calc(var(--mask-size) * -1), calc(var(--mask-size) * -1));
    }

    100% {
        transform: translate(0px, 0px);
    }
}
Copy after login

The browser no longer animates the mask-position, which triggered a layout on each frame. Even though the background moves on each frame, through translate it doesn’t trigger a layout or a paint. You can see that the only paints twice, down from 1,000+ every minute.

The Two Lines of CSS That Tanked Performance (fps to ps)

Eagle-eyed viewers will have spotted a problem. If you remember, the height and width of the background fills the viewport. Shifting the background left and up by 24px leaves us with this empty space in the viewport.

The Two Lines of CSS That Tanked Performance (fps to ps)

Solving it is as simple as adding the mask size to the width and height of the container:

.background-mask {
    --mask-size: 24px;

    width: calc(100% + var(--mask-size));
    height: calc(100% + var(--mask-size));
}
Copy after login

Let’s take a look again in Firefox:

It may not be a perfect solution, but it’s always a little satisfying pulling off a fun smoke and mirrors CSS trick.

The above is the detailed content of The Two Lines of CSS That Tanked Performance (fps to ps). For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Vue 3 Vue 3 Apr 02, 2025 pm 06:32 PM

It&#039;s out! Congrats to the Vue team for getting it done, I know it was a massive effort and a long time coming. All new docs, as well.

Can you get valid CSS property values from the browser? Can you get valid CSS property values from the browser? Apr 02, 2025 pm 06:17 PM

I had someone write in with this very legit question. Lea just blogged about how you can get valid CSS properties themselves from the browser. That&#039;s like this.

Stacked Cards with Sticky Positioning and a Dash of Sass Stacked Cards with Sticky Positioning and a Dash of Sass Apr 03, 2025 am 10:30 AM

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

A bit on ci/cd A bit on ci/cd Apr 02, 2025 pm 06:21 PM

I&#039;d say "website" fits better than "mobile app" but I like this framing from Max Lynch:

Using Markdown and Localization in the WordPress Block Editor Using Markdown and Localization in the WordPress Block Editor Apr 02, 2025 am 04:27 AM

If we need to show documentation to the user directly in the WordPress editor, what is the best way to do it?

Comparing Browsers for Responsive Design Comparing Browsers for Responsive Design Apr 02, 2025 pm 06:25 PM

There are a number of these desktop apps where the goal is showing your site at different dimensions all at the same time. So you can, for example, be writing

Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Why are the purple slashed areas in the Flex layout mistakenly considered 'overflow space'? Apr 05, 2025 pm 05:51 PM

Questions about purple slash areas in Flex layouts When using Flex layouts, you may encounter some confusing phenomena, such as in the developer tools (d...

How to select a child element with the first class name item through CSS? How to select a child element with the first class name item through CSS? Apr 05, 2025 pm 11:24 PM

When the number of elements is not fixed, how to select the first child element of the specified class name through CSS. When processing HTML structure, you often encounter different elements...

See all articles