Search code examples
jquerycssjquery-animatecss-transformstimeline

move right/left each time button is clicked


I am trying to make a "timeline". I am doing it to learn more about jquery, so i am trying not to use any "timeline js plugins".

I used a jQuery function to center the Event which the user clicks. I do this by using a transform:

$('.tl-container').css('transform', 'translateX(' + movingDistance + 'px)');

Then i added a couple of arrows so the user could go left and right and see other events which might be out of sight.

How can i make it that that buttons move the timeline on each click by 100px?

I first tried also using the translate idea, but that didn't work. It would translate once, but the second click wouldn't work anymore (because the timeline was already at the "end" of the translate.

Then i tried with .animate(). This works, but with one major problem. It "offsets" the whole timeline, so when i then click on an Event it won't center (it will move to the new "offset center".

Any ideas?

Here is a CodePen showing what i have now: https://codepen.io/anon/pen/bVJQbB

I also have the project on GitHug if you prefer: https://github.com/boguz/chronos


Solution

  • I have forked your pen : https://codepen.io/sodawillow/pen/MaRZLa

    In HTML I have added two buttons with next and prev ID's (just for test purposes, because your arrows didn't display on Firefox when I first worked on your pen) :

    <button id="prev">prev</button>
    <button id="next">next</button>
    

    In JS I have added a global variable (activeEventNumber) to hold the current active event, and used it with your code to mode the cursor one step forwards, or backwards. Of course, this could be improved a little :

    $("#prev").on("click", prevEvent);
    $("#next").on("click", nextEvent);
    
    function prevEvent() {
      var eventSize = $('.tl-event').width(),
        windowSize = $(window).width(),
        clickedPosition = eventSpacing * --activeEventNumber + eventSize * activeEventNumber,
        movingDistance = windowSize / 2 - clickedPosition - eventSpacing / 2 - eventSpacing * 1.7;
      $('.tl-container').css('transform', 'translateX(' + movingDistance + 'px)');
      activeEvent($(".tl-event").eq(activeEventNumber - 1));
    }
    
    function nextEvent() {
      var eventSize = $('.tl-event').width(),
        windowSize = $(window).width(),
        clickedPosition = eventSpacing * ++activeEventNumber + eventSize * activeEventNumber,
        movingDistance = windowSize / 2 - clickedPosition - eventSpacing / 2 - eventSpacing * 1.7;
      $('.tl-container').css('transform', 'translateX(' + movingDistance + 'px)');
      activeEvent($(".tl-event").eq(activeEventNumber - 1));
    }
    

    PS : I will try to improve the JS in my pen

    EDIT : my pen has both your arrows and mine working, and I tried to DRY the JS as much as possible (less lines than your JS :)). I have added keyboard arrows, too. If you don't understand some/anything, please tell me.

    // Use Strict Mode
    "use strict";
    
    // GLOBAL variables
    var eventSpacing = 50, activeEventNumber = 0, clickedEventNumber, eventSize, windowSize;
    
    // DOCUMENT READY FUNCTION
    $(function () {
      
      //set useful values
      clickedEventNumber = $(this).index();
      eventSize = $('.tl-event').width();
      windowSize = $(window).width();
    
      createTimeline();
      initEvents();
    
    }); // end of document.ready
    
    function createTimeline() {
      var numberOfEvents = $('.tl-event').length;
      
      $('.tl-line').each(function() {
        var lineLength = numberOfEvents * (eventSpacing + eventSize);
        $('.tl-container').css('width', lineLength + (eventSpacing * 2) +'px');
        $('.tl-line').css('left', eventSpacing + 'px');
        $('.tl-line').css('width', lineLength + 'px');
      });
      
      $('.tl-event').each(function() {
        var eventNumber = $(this).index();
        var eventHorPosition = eventNumber * eventSpacing + eventSpacing / 2;
        $(this).css('left', eventHorPosition + 'px');
      });
    }
    
    function initEvents() {
      
      //click
      $('.tl-event').on('click', function() {
        activeEventNumber = $(this).index();
        moveCursor(activeEventNumber);    
      });
      
      //buttons
      $('#prev, .arrow-before').on("click", moveToPrevEvent);
      $('#next, .arrow-after').on("click", moveToNextEvent);
      
      //keyboard arrows
      $(window).on("keydown", function(e) {
        switch(e.which) {
          case 37: moveToPrevEvent(); break; //left arrow key
          case 39: moveToNextEvent(); break; //right arrow key
          default: return; // exit this handler for other keys
        }
        e.preventDefault(); // prevent the default action (scroll / move caret)
      });
      
      //next and previous functions
      function moveToPrevEvent() { if(activeEventNumber > 1) moveCursor(--activeEventNumber); } 
      function moveToNextEvent() { if(activeEventNumber < $('.tl-event').length) moveCursor(++activeEventNumber); }
    
      //main function to select an event
      function moveCursor(eventNumber) {
        var clickedPosition, movingDistance;  
        //move
        clickedPosition = (eventSpacing + eventSize) * eventNumber;
        movingDistance = windowSize / 2 - clickedPosition - eventSpacing * 2.2; //magic number ! :) 1.7 + .5
        $('.tl-container').css('transform', 'translateX(' + movingDistance + 'px)');
        //style
        $('.tl-event').css('background-color', 'white');
        $($(".tl-event").eq(activeEventNumber - 1)).css('background-color', 'red');
      }
    }
    * {
      margin: 0;
      padding: 0;
    }
    
    body {
      background-color: #444;
      color: #fff;
      max-width: 100%;
      overflow: hidden;
      text-align: center;
    }
    
    
    .tl-mask {
      border: 1px solid red;
      height: 200px;
      width: 86vw;
      margin: 10vh auto 0;
      overflow: hidden;
      position: relative;
    }
    .arrow {
      height: 40px;
      position: absolute;
      top: 188px;
      opacity: .2;
    }
    .arrow:hover {
      opacity: .75;
      cursor: pointer;
    }
    .arrow-before {
      left: 50px;
    }
    .arrow-after {
      right: 50px;
    }
    .tl-container {
      height: 200px;
      background-color: #666;
      position: relative;
      transition: all 2s ease;
      min-width: 100%;
    }
    .tl-line {
      background-color: white;
      height: 2px;
      display: block;
      position: relative;
      top: 100px;
    }
    .tl-event {
      background-color: white;
      height: 10px;
      width: 10px;
      position: relative;
      top: 94px;
      border-radius: 50%;
      float: left;
    }
    .tl-event:hover {
      cursor: pointer;
    }
    
    .center-check {
      background-color: white;
      width: 3px;
      height: 20px;
      margin: 10px auto;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <h1>chronos</h1>
    <img src="./img/arrow_before.png" alt="" class="arrow arrow-before">
    
    <div class="tl-mask">
      <div class="tl-container">
        <div class="tl-line"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
        <div class="tl-event"></div>
      </div> <!-- end .tl-container -->
    </div> <!-- end -tl-mask -->
    
    <img src="./img/arrow_after.png" alt="" class="arrow arrow-after">
    <div class="center-check"></div>
    
    <button id="prev">prev</button>
    <button id="next">next</button>