Search code examples
javascripthtmljquerycsstransition

Stuck on trying to replicate a certain CSS-Transition (CTA-Button that moves to the bottom corner of the page when scrolling down and gets fixed)


So here is a simple fiddle (http://jsfiddle.net/t1xywroc/2/) I created to show you the animation I'm trying to replicate (from this website: https://paperpillar.com/).

I'm still fairly new to Javascript/Jquery and have only been doing HTML and CSS for a couple months.

The problem about my animation is that (as far I know) there is no transition from an absolute position to a fixed position, which I believe causes that small jump, right after triggering the animation (or transition if you will). The second problem is, that the content of the ::before element can't be transitioned either. How can I fix these things using jQuery?

I tried to get it work by using mostly CSS but I keep coming across new problems. I guess it's inevitable to use JavaScript, which is what I need help with. I'd really appreciate it.

Note: not a native speaker.

HTML

<div class="section">
  <div class="button"></div>
</div>

CSS

.section {
  height: 2000px;
  width: auto;
}

.button {
  position: absolute;
  transform: translateX(50%);
  right: 50%;
  display: inline-block;
  color: white;
  line-height: 60px;
  height: 60px;
  width: auto;
  padding-left: 25px;
  padding-right: 25px;
  background-color: blue;
  border-radius: 25px;
  vertical-align: middle;
  top: 15rem;
}
.button::before{
  content: 'Button Text';
}

.floating {
    padding-left: 0px;
    padding-right: 0px;
    position: fixed;
    right: 15px;
    top: calc(100vh - 120px);
    transform: none;
    height: 80px;
    width: 80px;
    transition: all 1.5s ease-in-out;
    background-color: red !important;
    border: none;
    border-radius: 50%;
    justify-content: center;
    text-align: center;
}
.floating::before{
  content:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24px' height='24px' fill='white'><path d='M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z' /></svg>");
}

JS

$(document).ready(function() {
$(window).on('scroll', function() {
   if ($(window).width() <= 768) {
      var scrollTop = $(this).scrollTop();

      $('.button').each(function() {
          var topDistance = $(this).offset().top;

          if ((topDistance - 30) < scrollTop) {
            $(this).addClass('floating');

          // Haven't put much thought into this part yet
          } else if ((topDistance - 30) >= scrollTop){
          }
        });
    }
});
});

Solution

  • A couple of problems have been highlighted in the question: the 'jump' when the transition moves between absolute and fixed and the fact that pseudo elements' content can not be transitioned.

    To get round the absolute to fixed jump problem we can set the button to fixed as soon as the transition is to start and then transition. This is possible by introducing CSS animations rather than transitions.

    To appear to transition between content we use before pseudo element to hold the initial text (as in the code given) and introduce an after pseudo element that holds the svg. To give the appearance of transitioning between the two we animate opacity.

    Note: in the website which is to be emulated the button initially has a white background over the page's white background. This means the change in shape as the initial button fades away is less obvious. With a contrasting blue background the change in shape is much more obvious. That may or may not be the effect required.

    Here's a snippet with animations instead of transitions and moving to fixed immediately the animation starts.

    $(document).ready(function() {
    $(window).on('scroll', function() {
       if ($(window).width() <= 2500) {
          var scrollTop = $(this).scrollTop();
    
          $('.button').each(function() {
              var topDistance = $(this).offset().top;
    
              if ((topDistance - 30) < scrollTop) {
                $(this).addClass('floating');
              } else if ((topDistance - 100) >= scrollTop){
              }
    });
    }
    });
    });
    .section {
      height: 2000px;
      width: auto;
      position: relative;
    }
    .button, .button::before, .button::after {  
      animation-duration: 1.5s;
      animation-iteration-count: 1;
      animation-fill-mode: forwards;
      animation-timing-function: ease-in-out;
      position: absolute;
    }
    .button {
      transform: translateX(50%);
      right: 50%;
      line-height: 60px;
      height: 60px;
      width: auto;
      color: transparent; /* do this to ensure the button has dimensions so it can be clicked */
      display: inline-block;
      vertical-align: middle;
      top: 15rem;
    }
    .button.floating {
      position: fixed;
      top: 30px;
      animation-name: floatdown;
    }
    .button::before {
      content: 'Button\00a0 Text';
      opacity: 1;
      color: white;
      line-height: 60px;
      height: 60px;
      width: auto;
      padding-left: 25px;
      padding-right: 25px;
      background-color: blue;
      border-radius: 25px;
    }
    
    .button::after {
        content: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24px' height='24px' fill='white'><path d='M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z' /></svg>");
        opacity: 0;
        padding-left: 0px;
        padding-right: 0px;
        height: 80px;
        width: 80px;
        margin-left: -50%;
        background-color: red;
        border: none;
        border-radius: 50%;
        justify-content: center;
        text-align: center;
    }
    
    div.button.floating::before {
        animation-name: fadeout;
    }
    div.button.floating::after {
        animation-name: fadein;
    }
    @keyframes fadeout {
        100% {
          opacity: 0;      
          }
    }
    @keyframes fadein {
        100% {
          opacity: 1;
          }
    }
    @keyframes floatdown {
        100% {
          top: calc(100vh - 120px);
          right: 95px; /* 80+15px */
        }
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="section">
      <div class="button">Button text</div>
    </div>

    Note also that if you want the downarrow to fill the circle more you could put it as a background-image with size contain rather than as content.