170 lines
4.9 KiB
JavaScript
170 lines
4.9 KiB
JavaScript
/**
|
|
* uiProgressButton.js v1.0.0
|
|
* http://www.codrops.com
|
|
*
|
|
* Licensed under the MIT license.
|
|
* http://www.opensource.org/licenses/mit-license.php
|
|
*
|
|
* Copyright 2014, Codrops
|
|
* http://www.codrops.com
|
|
*/
|
|
;( function( window ) {
|
|
|
|
'use strict';
|
|
|
|
var transEndEventNames = {
|
|
'WebkitTransition': 'webkitTransitionEnd',
|
|
'MozTransition': 'transitionend',
|
|
'OTransition': 'oTransitionEnd',
|
|
'msTransition': 'MSTransitionEnd',
|
|
'transition': 'transitionend'
|
|
},
|
|
transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
|
|
support = { transitions : Modernizr.csstransitions };
|
|
|
|
function extend( a, b ) {
|
|
for( var key in b ) {
|
|
if( b.hasOwnProperty( key ) ) {
|
|
a[key] = b[key];
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
function SVGEl( el ) {
|
|
this.el = el;
|
|
// the path elements
|
|
this.paths = [].slice.call( this.el.querySelectorAll( 'path' ) );
|
|
// we will save both paths and its lengths in arrays
|
|
this.pathsArr = new Array();
|
|
this.lengthsArr = new Array();
|
|
this._init();
|
|
}
|
|
|
|
SVGEl.prototype._init = function() {
|
|
var self = this;
|
|
this.paths.forEach( function( path, i ) {
|
|
self.pathsArr[i] = path;
|
|
path.style.strokeDasharray = self.lengthsArr[i] = path.getTotalLength();
|
|
} );
|
|
// undraw stroke
|
|
this.draw(0);
|
|
}
|
|
|
|
// val in [0,1] : 0 - no stroke is visible, 1 - stroke is visible
|
|
SVGEl.prototype.draw = function( val ) {
|
|
for( var i = 0, len = this.pathsArr.length; i < len; ++i ){
|
|
this.pathsArr[ i ].style.strokeDashoffset = this.lengthsArr[ i ] * ( 1 - val );
|
|
}
|
|
}
|
|
|
|
function UIProgressButton( el, options ) {
|
|
this.el = el;
|
|
this.options = extend( {}, this.options );
|
|
extend( this.options, options );
|
|
this._init();
|
|
}
|
|
|
|
UIProgressButton.prototype.options = {
|
|
// time in ms that the status (success or error will be displayed) - should be at least higher than the transition-duration value defined for the stroke-dashoffset transition of both checkmark and cross strokes
|
|
statusTime : 1500
|
|
}
|
|
|
|
UIProgressButton.prototype._init = function() {
|
|
// the button
|
|
this.button = this.el.querySelector( 'button' );
|
|
// progress el
|
|
this.progressEl = new SVGEl( this.el.querySelector( 'svg.progress-circle' ) );
|
|
// the success/error elems
|
|
this.successEl = new SVGEl( this.el.querySelector( 'svg.checkmark' ) );
|
|
this.errorEl = new SVGEl( this.el.querySelector( 'svg.cross' ) );
|
|
// init events
|
|
this._initEvents();
|
|
// enable button
|
|
this._enable();
|
|
}
|
|
|
|
UIProgressButton.prototype._initEvents = function() {
|
|
var self = this;
|
|
this.button.addEventListener( 'click', function() { self._submit(); } );
|
|
}
|
|
|
|
UIProgressButton.prototype._submit = function() {
|
|
// by adding the loading class the button will transition to a "circle"
|
|
classie.addClass( this.el, 'loading' );
|
|
|
|
var self = this,
|
|
onEndBtnTransitionFn = function( ev ) {
|
|
if( support.transitions ) {
|
|
if( ev.propertyName !== 'width' ) return false;
|
|
this.removeEventListener( transEndEventName, onEndBtnTransitionFn );
|
|
}
|
|
|
|
// disable the button - this should have been the first thing to do when clicking the button.
|
|
// however if we do so Firefox does not seem to fire the transitionend event.
|
|
this.setAttribute( 'disabled', '' );
|
|
|
|
if( typeof self.options.callback === 'function' ) {
|
|
self.options.callback( self );
|
|
}
|
|
else {
|
|
// fill it (time will be the one defined in the CSS transition-duration property)
|
|
self.setProgress(1);
|
|
self.stop();
|
|
}
|
|
};
|
|
|
|
if( support.transitions ) {
|
|
this.button.addEventListener( transEndEventName, onEndBtnTransitionFn );
|
|
}
|
|
else {
|
|
onEndBtnTransitionFn();
|
|
}
|
|
}
|
|
|
|
// runs after the progress reaches 100%
|
|
UIProgressButton.prototype.stop = function( status ) {
|
|
var self = this,
|
|
endLoading = function() {
|
|
// first undraw progress stroke.
|
|
self.progressEl.draw(0);
|
|
|
|
if( typeof status === 'number' ) {
|
|
var statusClass = status >= 0 ? 'success' : 'error',
|
|
statusEl = status >=0 ? self.successEl : self.errorEl;
|
|
|
|
// draw stroke of success (checkmark) or error (cross).
|
|
statusEl.draw( 1 );
|
|
// add respective class to the element
|
|
classie.addClass( self.el, statusClass );
|
|
// after options.statusTime remove status and undraw the respective stroke. Also enable the button.
|
|
setTimeout( function() {
|
|
classie.remove( self.el, statusClass );
|
|
statusEl.draw(0);
|
|
self._enable();
|
|
}, self.options.statusTime );
|
|
}
|
|
else {
|
|
self._enable();
|
|
}
|
|
// finally remove class loading.
|
|
classie.removeClass( self.el, 'loading' );
|
|
};
|
|
|
|
// give it a time (ideally the same like the transition time) so that the last progress increment animation is still visible.
|
|
setTimeout( endLoading, 300 );
|
|
}
|
|
|
|
UIProgressButton.prototype.setProgress = function( val ) {
|
|
this.progressEl.draw( val );
|
|
}
|
|
|
|
// enable button
|
|
UIProgressButton.prototype._enable = function() {
|
|
this.button.removeAttribute( 'disabled' );
|
|
}
|
|
|
|
// add to global namespace
|
|
window.UIProgressButton = UIProgressButton;
|
|
|
|
})( window ); |