Optimizing Animation Effects: Tips for Improving jQuery and CSS Transitions Performance

Wouldn't it be cool to have a magic line that follows your mouse as you navigate through the header menu?

Take a look at this example:

It works seamlessly and smoothly.

I tried implementing a similar jQuery script myself, but it's not as smooth as I'd like it to be.

Check out my HTML code below:

<nav>
    <ul>
        <li>
            <a href="" class="active"><p>Home</p></a>
        </li>
      
        // more list items...
        
        <li id="magic-line"></li>
    </ul>
</nav>

Here is the CSS for the magic line:

#magic-line{
    display: block;
    position: absolute;
    width: 100px;
    height: 3px;
    background-color: #7190C9;
    left: 0px;
    top:97px;
}

And here is the jQuery script I'm currently using:

var bar = $("#magic-line");
$("a").hover(function(){
    var link = $(this);
    switch(link.children("p").html()) {
    case "Home":
        var linkWidth = link.parent("li").width();
        var offset = link.offset().left;
        bar.css({left : offset, width : linkWidth, transition : "0.5s"});
        break;
    
    // additional cases...
    
}
});

Do you have any suggestions on how I can improve this script?

You can view my script in action on JsFiddle here:

https://jsfiddle.net/3jeu2L7v/

Although it runs smoother there than on my actual website. Any insights on why this might be happening?

Answer №1

One way to enhance animation performance is by utilizing CSS transforms. Instead of altering left and width, we can utilize transform: translateX() scaleX().

The downside is that CSS transforms may not be supported by older browsers, requiring handling of CSS prefixes in certain cases (e.g. -webkit-transfor, -moz, etc). However, CSS transforms will activate hardware acceleration.

I recommend trying an animation library like GSAP which handles this automatically. It will employ CSS transforms when possible and incorporate necessary CSS prefixes if required. Naturally, this adds an additional dependency to your project and increases the file size by a few kilobytes ;)

var items = $('li');
var bar = $('.bar');

items.on('mouseover', function(){
  var item = $(this);
  var w = item.width();
  var x = item.position().left + 15; // 15 is the margin form the CSS, adjust accordingly

  bar.css({
    transform: 'translateX(' + (x + w / 2 - 1) + 'px) scaleX(' + w + ')',
    //left: x,
    //width: w
  });
})
body {
  font-family: sans-serif;
  margin: 20px 50px;
}

nav{
  padding: 15px 15px 20px 15px;
  border: 1px solid #ccc;
  position: relative;
}

nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

nav li {
  list-style: none;
  display: inline;
  margin: 15px;
}

nav li a {
  text-decoration: none;
  color: #888;
}

nav .bar {
  position: absolute;
  width: 1px;
  height: 3px;
  left: 0;
  background: #0ae;
  bottom: 16px;
  transition: 0.5s all;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul>
    <li><a href="#">Home</a></li>
    <li><a href="#">About us</a></li>
    <li><a href="#">Work</a></li>
    <li><a href="#">Contact us</a></li>
  </ul>
  <div class="bar"></div>
</nav>

Check out this operational example: http://codepen.io/victmo/pen/KMMNVG

If you're interested, watch this video / article concerning animation performance through CSS transforms. Although somewhat dated, it remains relevant:

Best wishes!

Answer №2

It appears that the slow animation performance may be attributed to other aspects of your codebase. However, there is an opportunity for optimization within this specific piece of code. Specifically, rather than recalculating the width and offset of the "Barre" every time a user hovers, it would be more efficient to calculate these values once and update them only if necessary due to layout changes.

One potential approach could be:

var barre = $("#barre-du-futur"),
// caching additional selectors for future use
    menuLink = $("a");

// This variable will store precalculated widths and offsets   
var barreProps;

$(document).ready(function(){
    barre.css({left : $(".active").offset().left, width : $(".active").parent("li").width(), transition : "0.5s"});
  // Capture Barre properties
  captureBarreProps();
});

$(window).resize(function(){
    barre.css({left : $(".active").offset().left, width : $(".active").parent("li").width(), transition : "0.5s"});
  // Recalculate Barre properties
  captureBarreProps();
});

// Optimization function
function captureBarreProps() {
    // Reset the object
    barreProps = {};

  // Iterate through the links to precalculate properties
  menuLink.each(function() {

        var curMenuLink = $(this);
    var curItemName = curMenuLink.children("p").html();
    if ( curItemName.toString() !== "" ) {
      barreProps[curItemName] = {
        width: curMenuLink.parent("li").width(),
        offset: curMenuLink.offset().left
      };    
    } else {
        // error: An issue with one of the Menu Items
    }

  });
}

// Eliminating the need for switch statements and hardcoded names
menuLink.hover(function(){
    var thisA = $(this),
      curItemName = thisA.children("p").html(),
      curBarreProps = barreProps[curItemName];

    barre.css({left : curBarreProps.offset, width : curBarreProps.width, transition : "0.5s"});
});

Check out the updated fiddle here

Answer №3

To achieve the desired effect of filling the whole line from left to right without space between them, you must trigger the hover on the li elements which are divided into four equal parts. The key is to set the width of nav ul li to 25% in your CSS.

Here is a more concise way to write your function:

$("li").each(function() {
  $(this).hover(function (){
    var trigger = $(this).find('a'),
        left        = trigger.offset().left,
        width       = trigger.outerWidth();
    barre.css({'left': left, 'width': width})
  })
})

I have rewritten your fiddle for clarity: Check out the updated fiddle here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Cloud function for Firestore to recursively update a subcollection or collection group

I've been working on this cloud function: import pLimit from "p-limit"; const syncNotificationsAvatar = async ( userId: string, change: Change<DocumentSnapshot> ) => { if (!change.before.get("published") || !change.after.exists) { ...

Exploring the Fusion of Strings and Arrays of Javascript Objects using jQuery and JSON

Trying to achieve a simple task, but not very proficient in jQuery so struggling to figure it out. Want to send JSON data to an ASP.NET Controller. Data includes strings and a list of objects. The code snippet would appear like this: View: $(document). ...

Constantly positioning the text cursor over the Textbox

I am currently in the process of developing a webpage that will feature only one text box for displaying information based on the input data provided. Our strategy involves utilizing either a Barcode Scanner or Magnetic Swipe as well as a Touch Screen Moni ...

"Encountering issue with componentDidMount() not functioning as expected, despite debugger being

Hello everyone! This is my first time sharing something here, so I'm eager to hear your feedback on both how I've posted and the content itself. I've only been programming for 8 weeks now, so I'm sure there are plenty of mistakes in wha ...

Optimized layout for screen resolutions exceeding 1000 pixels wide

As I work on making my website responsive using Dreamweaver CC, I'm encountering a problem with screen sizes larger than 1000px. In Dreamweaver, there are three options for responsive web design at the bottom: desktop 1000px and less, tablet 768px an ...

What advantages do interfaces as data types offer in Angular compared to using classes?

After watching a tutorial from my teacher, he showed us this code snippet: He mentioned that the products array, defined as type any [], is not taking advantage of TypeScript's strongly typing. He suggested using an INTERFACE instead. I have a questi ...

Encountering numerous TypeScript errors due to a JavaScript file in Visual Studio 2017

Kindly review the update below I utilized the following package as a foundation for my VS Project -> https://github.com/AngularClass/angular2-webpack-starter Everything ran smoothly in Visual Studio Code, but when I attempted to convert it into a Visu ...

Creating a 404 Error Response in Express.js

Inside app.js, I currently have the following code: // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); This code executes when a non-existent URL ...

Is it more advantageous to create two distinct projects for the frontend and backend along with an API, or should I consolidate them into a

Asking for some guidance on a few queries I have. I've developed a backend node server with express & mongo to run specific tasks and store them in the database, along with creating an admin page using express & bootstrap. Everything is functioning s ...

Issue with accessing Scope value in AngularJS directive Scope

FIDDLE I've recently developed a directive that looks like this: return { restrict: 'EAC', scope: { statesActive: '=' }, link: function (scope, element, attrs) { var ...

Move Picture with Click (like the action on Google Maps)

Currently on the lookout for some code that will enable me to manipulate a floor plan in a manner reminiscent of the one showcased at whitehouse.gov I am in the process of coding a website that would benefit from integrating a similar function with their ...

Collapse the active panel on a jQuery accordion with a click event

I'm currently utilizing the Simple jQuery Accordion created by CSS-Tricks, and I am seeking guidance on how to enable the active panel to close when clicking on the link within the dt element. The scenario where I am implementing this is as a menu in ...

Error encountered: A syntax error occurred due to an unexpected token ":" found in the file path D:practise odejs odejs-demoviewsindex.html

Attempting to switch the engine view from .ejs to .html resulted in an error. After searching online, I was unable to find a solution for the following problems: Express 500 SyntaxError: Unexpected token : in D:\practise\nodejs\nodejs-demo& ...

Has a newly created element been styled or are all values set to default since it is outside of the DOM?

First, I begin by creating an element: let link = document.createElement('a'); After my document is fully loaded and all styles and scripts are applied. The styles may look something like this: a { background-color: salmon; } Therefore, it w ...

Text encoded in UTF-8 emerges from the surrounding frame

As someone who is relatively new to frontend development, I recently created a blog using a template that utilizes Next.js, Tailwind, and React. However, when I input UTF-8 text in Korean, the text overflows outside of the parent frame on mobile devices. ...

Guide to creating a ReactJS higher-order component using modern ES6 syntax

I´m attempting to create a ReactJS higher-order component using ES6 syntax. This is what I have so far: export const withContext = Component => class AppContextComponent extends React.Component { render() { return ( ...

Enable or disable dropdown based on selected radio button status

I need the dropdown to be disabled unless the radio button labeled prov is selected. It should only be enabled when prov is chosen. I attempted to create a demo, but it's not functioning correctly and I can't figure out why. HTML: <td colsp ...

Conceal a specific entry in Django

I have a table displaying records from a model using a for loop. Each row has a button that, when clicked, removes the row. However, upon refreshing the page, the removed rows reappear. I want to prevent this. How can I achieve this? <tabl ...

Is there a way to eliminate currency within an Angularjs directive?

Currently, I am utilizing a directive to properly display currency format. You can find the directive at this URL: http://jsfiddle.net/ExpertSystem/xKrYp/ Is there a way to remove the dollar symbol from the output value and only retain the converted amoun ...

Is it not possible to include Infinity as a number in JSON?

Recently, I encountered a frustrating issue that took an incredibly long time to troubleshoot. Through the REPL (NodeJS), I managed to replicate this problem: > o = {}; {} > JSON.stringify(o) '{}' > o.n = 10 10 > JSON.stringify(o) ...