Skip to content

Instantly share code, notes, and snippets.

@mattkelley
Created July 23, 2015 19:47
Show Gist options
  • Save mattkelley/4eee7bc61b645ef0f56b to your computer and use it in GitHub Desktop.
Save mattkelley/4eee7bc61b645ef0f56b to your computer and use it in GitHub Desktop.

Revisions

  1. Matt Kelley created this gist Jul 23, 2015.
    138 changes: 138 additions & 0 deletions range.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,138 @@

    var on = function(items, scope) {
    items.forEach(function(item) {
    return item.e.map(function(e) {
    item.context.addEventListener(e,item.fn,false)
    })
    })
    }

    var off = function(items) {
    items.forEach(function(item) {
    return item.e.map(function(e) {
    item.context.removeEventListener(e,item.fn,false)
    })
    })
    }

    // move the element to a percentage point
    var to = function(el, percent) {
    // This is for sanity but also for visual. The playhead shouldn't leave the progress bar.
    var clamp = Math.min(Math.max(percent, 0.2), 99.8);
    // Convert this into matrices stuff later
    ['webkitTransform', 'MozTransform', 'msTransform', 'transform'].forEach(function(v) {
    el.style[v] = 'translateX(' + clamp + '%)';
    });
    }

    // calculate the percentage point
    var percentage = function(el, event) {
    var x = (event.touches) ? event.touches[0].clientX : event.clientX;
    var position = x - el.getBoundingClientRect().left;
    var percentage = 100 * position / el.offsetWidth;
    return Math.min(Math.max(percentage, 0), 100);
    }

    /**
    * Range slider
    * Creates a simple UI slider for scrubbing video or volume
    * @param {Element} el
    */
    function Range(el) {
    // store passed element
    this._el = el;
    // current percentage of the range element
    this._position = 0;
    // amount of move events in the current range adjustment
    this._count = 0;
    // create elements, and the event configuration
    this.create();
    };
    // Enable the range slider events
    Range.prototype.enable = function() {
    on([this._e.start]);
    };
    // Disable the range slider events
    Range.prototype.disable = function() {
    off([this._e.start]);
    };
    // Get the range current position
    Range.prototype.get = function() {
    return this._position;
    };
    // Set the range position
    Range.prototype.set = function() {
    this._position = percent;
    to(this._time, this._position);
    };
    // Handle touchstart and mousedown events
    Range.prototype._start = function() {
    event.stopPropagation();
    on([this._e.move, this._e.stop]);
    this.trigger('begin', {type: 'begin', srcEvent: event, percent: this._position});
    if (this._position !== percentage(this._bar, event)) {
    this.move(event);
    }
    };
    // Handle touchmove and mousemove events
    Range.prototype._move = function() {
    event.stopPropagation();
    this._position = percentage(this._bar, event);
    to(this._time, this._position);
    this.trigger('move', {type: 'move', srcEvent: event, count: ++this._count, percent: this._position});
    };
    // Handle touchend and mouseup events
    Range.prototype._stop = function() {
    event.stopPropagation();
    off([this._e.move, this._e.stop]);
    this.trigger('end', {type: 'end', srcEvent: event, percent: this._position });
    this._count = 0;
    };
    // Destroy the Range slider elements and remove event listeners
    Range.prototype.destroy = function() {
    off([this._e.start, this._e.move, this._e.stop]);
    this._bar.removeChild(this._time);
    this._el.removeChild(this._bar);
    this._el.classList.remove('ivp-control-progress');
    };
    // Create the Range markup and event configuration
    Range.prototype.create = function() {
    // Bar is the outer element always visibile
    this._bar = document.createElement('div');
    this._bar.className = 'ivp-progress-bar';
    // Time is the inner element which slides
    this._time = document.createElement('div');
    this._time.className = 'ivp-progress-time';
    this._bar.appendChild(this._time);
    // @NOTE modifying passed element here - not sure if I like this or not
    this._el.classList.add('ivp-control-progress');
    this._el.appendChild(this._bar);

    var _this = this;
    this._e = {
    start: {
    context: this._bar,
    e: ['mousedown', 'touchstart'],
    fn: function() {
    _this._start.apply(_this, arguments);
    }
    },
    move: {
    context: document,
    e: ['mousemove', 'touchmove'],
    fn: function() {
    _this._move.apply(_this, arguments);
    }
    },
    stop: {
    context: document,
    e: ['mouseup', 'touchend'],
    fn: function() {
    _this._stop.apply(_this, arguments)
    }
    }
    }
    };

    // Export the Range class as default
    export default Range;