Search code examples
javascripthtmldraggablegsap

Video timeline dragging and click


I am new to GSAP and I am trying to enable dragging and click on custom html5 video timeline with GSAP. I have read a couple of posts on the GSAP forum but there is something I obviously can't understand...

I've reproduced a simplified example on the following jsfiddle: https://jsfiddle.net/epigeyre/oLmk6b0d/2/

So I create my draggable element from an element stored in a variable, bound it to it's container (which is the timeline container), and then add my function onDrag (I guess click will be the same). The timeline progress is shown by a div inside the timeline container that is scaling on an X axis from 0 to 1. I think linking to the current video time is ok but the animation is not (I don't understand why a translate is applied...).

Here is that specific snippet:

Draggable.create( timelineProgress, {
  type:'x',
  bounds: timeline, // My timeline container
  onDrag: function() {
    video.currentTime = this.x / this.maxX * video.duration;
    TweenLite.set( timelineProgress, { scaleX: this.x / this.maxX } ) ;
  },
  onClick: function() {
    video.currentTime = this.x / this.maxX * video.duration;
    TweenLite.set( timelineProgress, { scaleX: this.x / this.maxX } ) ;
  }
});

Could you help me understand what's going? Thanks a lot for your help!


Solution

  • Alright so here the solution for those in need of building a custom video player. Using GSAP you have a really interesting trigger property within the Draggable plugin.

    Here is how I made it work for an HTML5 video timeline.

    var video = document.getElementsByTagName('video')[0],
        play = document.getElementsByClassName('video__play')[0],
        timeline = document.getElementsByClassName('timeline')[0],
        timelineProgress = document.getElementsByClassName('timeline__progress')[0],
        drag = document.getElementsByClassName('timeline__drag')[0];
    
    // Toggle Play / Pause
    play.addEventListener('click', togglePlay, false);
    
    function togglePlay() {
      if (video.paused) {
        video.play();
      } else {
        video.pause();
      }
    }
    
    // on interaction with video controls
    video.onplay = function() {
      TweenMax.ticker.addEventListener('tick', vidUpdate);
    };
    video.onpause = function() {
    	TweenMax.ticker.removeEventListener('tick', vidUpdate);
    };
    video.onended = function() {
    	TweenMax.ticker.removeEventListener('tick', vidUpdate);
    };
    
    // Sync the timeline with the video duration
    function vidUpdate() {
      TweenMax.set(timelineProgress, {
        scaleX: (video.currentTime / video.duration).toFixed(5)
      });
      TweenMax.set(drag, {
        x: (video.currentTime / video.duration * timeline.offsetWidth).toFixed(4)
      });
    }
    
    // Make the timeline draggable
    Draggable.create(drag, {
      type: 'x',
      trigger: timeline,
      bounds: timeline,
      onPress: function(e) {
        video.currentTime = this.x / this.maxX * video.duration;
        TweenMax.set(this.target, {
          x: this.pointerX - timeline.getBoundingClientRect().left
        });
        this.update();
        var progress = this.x / timeline.offsetWidth;
        TweenMax.set(timelineProgress, {
          scaleX: progress
        });
      },
      onDrag: function() {
        video.currentTime = this.x / this.maxX * video.duration;
        var progress = this.x / timeline.offsetWidth;
        TweenMax.set(timelineProgress, {
          scaleX: progress
        });
      },
      onRelease: function(e) {
        e.preventDefault();
      }
    });
    video {
      display: block;
      width: 100%;
      height: auto;
    }
    
    .timeline {
      width: 100%;
      height: 10px;
      background-color: black;
      cursor: pointer;
      position: relative;
    }
    
    /* Here is the dragger that I will use to move the video 
    * current time forward or backward.
    * I have added a background color for you to see it
    * but just remove it in production.
    */
    
    .timeline__drag {
      width: 1px;
      height: 20px;
      top: -10px;
      background-color: yellow;
      position: absolute;
      z-index: 2;
      transform-origin: 0 0;
    }
    
    .timeline__progress {
      display: block;
      width: 100%;
      height: 100%;
      background-color: green;
      transform: scaleX(0);
      transform-origin: 0 0;
      position: relative;
      z-index: 1;
    }
    
    button {
      margin-top: 2em;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.0/utils/Draggable.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.0/TweenMax.min.js"></script>
    
    <video>
      <source src="http://clips.vorwaerts-gmbh.de/VfE_html5.mp4" type="video/mp4">
    </video>
    <div class="timeline">
      <div class="timeline__drag"></div>
      <span class="timeline__progress"></span>
    </div>
    <button class="video__play">Play / Pause video</button>

    I have to thanks Carl of the GSAP forum for his wonderful help!