Hook
Hook

Reputation: 460

Looping through previous and next LIs starting from the current one hovered

I am currently playing around with a menu animation. I achieved the desired result but it's not optimised and I can't manage to create a function to loop through the elements and apply the correct effect.

What I am basically doing is going through all LI after and before the LI hovered and reducing/fading the elements based on their positions. So the further the LI is from the hovered one the smaller and transparent it gets.

My solution is obviously not flexible as I need to run through all the values again if I add/remove a LI.

I would be interested to know how you would simplify that.

The code is quite simple.

$(document).ready(function(){
  $("ul li").on("mouseover", function(){
    $("ul li").removeClass("current");
    var liLength = $("ul li").length;
    $(this).css({
        "width": "42",
        "opacity": "1"
      }).prevAll().eq(0).css({
        "width": "35",
        "opacity": "0.8333333334"
      }).prevAll().eq(0).css({
        "width": "28",
        "opacity": "0.6666666667"           
      }).prevAll().eq(0).css({
        "width": "21",
        "opacity": "0.5"
     }).prevAll().eq(0).css({
        "width": "14",
        "opacity": "0.3333333333"
      }).prevAll().eq(0).css({
        "width": "7",
        "opacity": "0.16666666667"
      });

    $(this).css({
        "width": "42",
        "opacity": "1"
      }).nextAll().eq(0).css({
        "width": "35",
        "opacity": "0.8333333334"
      }).nextAll().eq(0).css({
        "width": "28",
        "opacity": "0.6666666667"           
      }).nextAll().eq(0).css({
        "width": "21",
        "opacity": "0.5"
     }).nextAll().eq(0).css({
        "width": "14",
        "opacity": "0.3333333333"
      }).nextAll().eq(0).css({
        "width": "7",
        "opacity": "0.16666666667"
      });

  $("ul li").on("mouseout", function(){
    $("ul li").removeClass("current").css({
      "width": "21",
      "opacity": "1"
    });
  });
  });
});
html, body, ul{
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}

.container{
  height: 100%;
  display: flex;
  -webkit-align-items: center;
  -webkit-box-align: center;
  align-items: center; 
}

ul{
 display: block;
 position: relative;
 height: auto;
}

ul li{
 list-style:none;
 background: #232323;
 border-top: 10px solid #fff;
 border-bottom: 10px solid #fff;
 border-right: 10px solid #fff;
 height: 10px;
 width: 21px;
 translate3d(0,0,0);
 -webkit-transition: all 200ms linear;
 transition:all 200ms linear;
}

ul li.current{
 background: blue;
}

.exp{
 position: fixed;
 top: 0;
 right: 0;
 background: grey;
 width: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>

The key elements :

The bigger element should be 42px wide and opacity : 1 The smaller element should be 7px wide and opacity : 0.2

The size should decrease as follow : (42 - 7) / x-elements * index of the element compared to its position from the hovered element.

The opacity should decrease as follow : (1 - 0.2) / x-elements * index of the element compared to its position from the hovered element.

The Prototype : Codepen

Thanks a lot!

Upvotes: 2

Views: 99

Answers (2)

Frits
Frits

Reputation: 7614

I've done a similar thing as DaniP. Although his code is much more optimised. I'm posting here more due to the fact that I spent a lot of time putting this together :)

Like I said, have a look at Dani's codepen, it's a lot less code, however if you need to spread out the functions with additional variables, have a look at my fiddle below:

https://jsfiddle.net/uyazymmu/2/

The code is really simple and goes through a few basic steps:

  1. Find the position of the hovered element
  2. Count the amount of li's
  3. Set the initial CSS of the hovered elements
  4. Loop through all of the other elements and adjust their CSS based on the relative distance to the initial element.

Here's the code:

$(document).ready(function() {

  $('li').mouseenter(function() {
    var li_index = $(this).index() + 1; // mark the position of the li
    var li_total = $('li').length; //total li's

    $(this).css({ //initial CSS
      'width': '42px',
      'opacity': '1'
    });

    $(this).prevAll().each(function() { //loop through all previous li's
      var prev_index = $(this).index() + 1;
      var distance = li_index - prev_index;
      var opacity = 1 - (0.2 * distance);
      var width = 42 - (7 * distance);
      $(this).css({
        'width': width + 'px',
        'opacity': opacity
      });
    });

    $(this).nextAll().each(function() { // loop through all the next li's
      var next_index = $(this).index() + 1;
      var distance = next_index - li_index;
      var opacity = 1 - (0.2 * distance);
      var width = 42 - (7 * distance);
      $(this).css({
        'width': width + 'px',
        'opacity': opacity
      });
    });
  });

  $('li').mouseleave(function() {
    $('li').each(function() {
      $(this).css({ //reset everything
        'width': '21px',
        'opacity': '1'
      });
    });
  });

});
html,
body,
ul {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}

.container {
  height: 100%;
  display: flex;
  -webkit-align-items: center;
  -webkit-box-align: center;
  align-items: center;
}

ul {
  display: block;
  position: relative;
  height: auto;
}

ul li {
  list-style: none;
  background: #232323;
  border-top: 20px solid #fff;
  border-bottom: 20px solid #fff;
  border-right: 40px solid #fff;
  height: 2px;
  width: 21px;
  translate3d(0, 0, 0);
  -webkit-transition: all 200ms linear;
  transition: all 200ms linear;
}

ul li.current {
  background: blue;
}

.exp {
  position: fixed;
  top: 0;
  right: 0;
  background: grey;
  width: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>

Upvotes: 2

DaniP
DaniP

Reputation: 38252

You can iterate through each collection of elements and use the index as indicator; try something like this:

$(this).css({"width": "42","opacity":"1"})
       .prevAll().each(function(index){
           $(this).css({
              "width": ((42-7)/(index+1))+"px",
               "opacity": ((1-0.2)/(index+1))
            })
       })

Codepen Demo

Upvotes: 2

Related Questions