DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Programmatic Brand Extraction: Pulling Logos, Colors, and Assets from Any URL
  • The Death of the CSS Selector: Architecting Resilient, AI-Powered Web Scrapers
  • A Guide to Parallax and Scroll-Based Animations
  • Building a Card Layout Using CSS Subgrid

Trending

  • Native SQL in Java Without JDBC Boilerplate — Meet Ujorm3
  • Operationalizing Enterprise AI at Scale: Architecture, Governance, and Adoption
  • Encryption Won't Survive Quantum Computing: What to Do?
  • Stop Loading Everything into Redshift: A Spectrum + Iceberg Pattern for Hybrid Analytics
  1. DZone
  2. Coding
  3. Languages
  4. Creating 3D CSS Buttons That Move as You Mouse Over

Creating 3D CSS Buttons That Move as You Mouse Over

Let's take a look at how to make 3D Javascript/CSS buttons with shadows, edges, and animations that move as you hover your mouse over them.

By 
Johnny Simpson user avatar
Johnny Simpson
·
Oct. 13, 21 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
6.9K Views

Join the DZone community and get the full member experience.

Join For Free
3D buttons

Recently I was toying with the idea of a 3D button that moves as the user moves their mouse around it. To further this effect, I added some 3D shadows which move in tandem to give the illusion of a 3D button that is sitting off the page and moves with the user’s mouse movements.

Demo

How Does It Work?

The fundamental concept behind these buttons is that we need to track when the user mouses over the button, moves, and mouses out. On mouseover, we will move the button so it appears 3D. On mouse out, we will reset it.

Before we get to the Javascript, let’s make our button look good. Our HTML will look like this:

HTML
 
<button class="button"><span>Hover!</span></button>


And our CSS looks like this:

CSS
 
button {
    box-shadow: none;
    background: transparent;
    transform-style: preserve-3d;
    padding: 0;
    height: auto;
    float: none;
}

button span {
    background: linear-gradient(180deg, #ff7147, #e0417f);
    font-size: 2rem;
    padding: 1rem 2rem;
    line-height: 3rem;
    will-change: transform, filter;
    float: none;
    margin: 0;
    transition: all 0.15s ease-out;
    height: auto;
    border-radius: 100px;
    overflow: hidden;
    display: block;
    margin: 0px auto;
    display: block;
    transform: rotateX(0deg) rotateY(0deg) scale(1);
    filter: drop-shadow(0 15px 15px rgba(0,0,0,0.3));
    font-weight: 600;
    perspective-origin: 0 0;
    letter-spacing: 0;
}


Minor Background Animations

You may have noticed that the third button has a background animation. If you’re interested in how I did that, I used a pseudo-element that is moving via animation. The pseudo-element has a simple gradient, and the overflow is hidden. You can test this out yourself by removing overflow: hidden from the span and button elements.

How the Javascript Works

Let’s take a look at our Javascript now. You may have noted we have two elements for our button:  the button itself and a span inside of it. There is a good reason for this  —  this lets us apply 3D perspective on the parent, which is required for the effect to work. It also allows us to target the parent for the hover effect. If we use hover on the child, the effect will bug out as the child will rotate, and we will miss the hitbox.

I am using a function that uses the event variable (e) and references both the span (noted here as item) and the button (referenced as parent).

JavaScript
 
let calculateAngle = function(e, item, parent) {
    let dropShadowColor = `rgba(0, 0, 0, 0.3)`
    // If the button has a data-filter-color attribute, then use this for the shadow's color
    if(parent.getAttribute('data-filter-color') !== null) {
        dropShadowColor = parent.getAttribute('data-filter-color');
    }

    // If the button has a data-custom-perspective attribute, then use this as the perspective.
    if(parent.getAttribute('data-custom-perspective') !== null) {
        parent.style.perspective = `${parent.getAttribute('data-custom-perspective')}`
    }

    // Get the x position of the users mouse, relative to the button itself
    let x = Math.abs(item.getBoundingClientRect().x - e.clientX);
    // Get the y position relative to the button
    let y = Math.abs(item.getBoundingClientRect().y - e.clientY);

    // Calculate half the width and height
    let halfWidth  = item.getBoundingClientRect().width / 2;
    let halfHeight = item.getBoundingClientRect().height / 2;

    // Use this to create an angle. I have divided by 6 and 4 respectively so the effect looks good.
    // Changing these numbers will change the depth of the effect.
    let calcAngleX = (x - halfWidth) / 6;
    let calcAngleY = (y - halfHeight) / 4;

    // Set the items transform CSS property
    item.style.transform = `rotateY(${calcAngleX}deg) rotateX(${calcAngleY}deg) scale(1.15)`;
        
    // And set its container's perspective.
    parent.style.perspective = `${halfWidth * 2}px`
    item.style.perspective = `${halfWidth * 3}px`

    // Reapply this to the shadow, with different dividers
    let calcShadowX = (x - halfWidth) / 3;
    let calcShadowY = (y - halfHeight) / 3;
        
    // Add a filter shadow - this is more performant to animate than a regular box shadow.
    item.style.filter = `drop-shadow(${-calcShadowX}px ${calcShadowY}px 15px ${dropShadowColor})`;
}


Effectively this splits the button into four quadrants. The midpoint represents an angle of change on the X and Y axis of 0, while a movement to the left results in a more negative Y angle, and a more positive one to the right. The same applies for X, where moving the cursor up turns the X angle more positive, and down, more negative.

Some things worth noting:

  • We are using filter box-shadows  because they transition better with CSS’ transition property.
  • I’ve added the ability to add custom perspective and box-shadow colors   to give more flexibility without having to change the code.
  • The effect is modulated by dividing the calcAngle* variables. If you change how much you divide them by, or even change the perspective, the effect will become more or less pronounced.

Applying Our Function to Each Button

To apply our function to each button, we simply iterate through them all with forEach. 

JavaScript
 
document.querySelectorAll('.button').forEach(function(item) {
    // Add on mouseenter
    item.addEventListener('mouseenter', function(e) {
        calculateAngle(e, this.querySelector('span'), this);
    });
    // Add on mousemove
    item.addEventListener('mousemove', function(e) {
        calculateAngle(e, this.querySelector('span'), this);
    });

    // Reset everything on mouse leave
    item.addEventListener('mouseleave', function(e) {
        let dropShadowColor = `rgba(0, 0, 0, 0.3)`
        if(item.getAttribute('data-filter-color') !== null) {
            dropShadowColor = item.getAttribute('data-filter-color')
        }
        item.querySelector('span').style.transform = `rotateY(0deg) rotateX(0deg) scale(1)`;
        item.querySelector('span').style.filter = `drop-shadow(0 10px 15px ${dropShadowColor})`;
    });
})


We’re Done

With that, we have recreated the effect shown at the start of the article. We hope you’ve enjoyed this guide.

CSS

Published at DZone with permission of Johnny Simpson. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Programmatic Brand Extraction: Pulling Logos, Colors, and Assets from Any URL
  • The Death of the CSS Selector: Architecting Resilient, AI-Powered Web Scrapers
  • A Guide to Parallax and Scroll-Based Animations
  • Building a Card Layout Using CSS Subgrid

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook