| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 | 
							- /**
 -  * UI component that lets the use control auto-slide
 -  * playback via play/pause.
 -  */
 - export default class Playback {
 - 
 - 	/**
 - 	 * @param {HTMLElement} container The component will append
 - 	 * itself to this
 - 	 * @param {function} progressCheck A method which will be
 - 	 * called frequently to get the current playback progress on
 - 	 * a range of 0-1
 - 	 */
 - 	constructor( container, progressCheck ) {
 - 
 - 		// Cosmetics
 - 		this.diameter = 100;
 - 		this.diameter2 = this.diameter/2;
 - 		this.thickness = 6;
 - 
 - 		// Flags if we are currently playing
 - 		this.playing = false;
 - 
 - 		// Current progress on a 0-1 range
 - 		this.progress = 0;
 - 
 - 		// Used to loop the animation smoothly
 - 		this.progressOffset = 1;
 - 
 - 		this.container = container;
 - 		this.progressCheck = progressCheck;
 - 
 - 		this.canvas = document.createElement( 'canvas' );
 - 		this.canvas.className = 'playback';
 - 		this.canvas.width = this.diameter;
 - 		this.canvas.height = this.diameter;
 - 		this.canvas.style.width = this.diameter2 + 'px';
 - 		this.canvas.style.height = this.diameter2 + 'px';
 - 		this.context = this.canvas.getContext( '2d' );
 - 
 - 		this.container.appendChild( this.canvas );
 - 
 - 		this.render();
 - 
 - 	}
 - 
 - 	setPlaying( value ) {
 - 
 - 		const wasPlaying = this.playing;
 - 
 - 		this.playing = value;
 - 
 - 		// Start repainting if we weren't already
 - 		if( !wasPlaying && this.playing ) {
 - 			this.animate();
 - 		}
 - 		else {
 - 			this.render();
 - 		}
 - 
 - 	}
 - 
 - 	animate() {
 - 
 - 		const progressBefore = this.progress;
 - 
 - 		this.progress = this.progressCheck();
 - 
 - 		// When we loop, offset the progress so that it eases
 - 		// smoothly rather than immediately resetting
 - 		if( progressBefore > 0.8 && this.progress < 0.2 ) {
 - 			this.progressOffset = this.progress;
 - 		}
 - 
 - 		this.render();
 - 
 - 		if( this.playing ) {
 - 			requestAnimationFrame( this.animate.bind( this ) );
 - 		}
 - 
 - 	}
 - 
 - 	/**
 - 	 * Renders the current progress and playback state.
 - 	 */
 - 	render() {
 - 
 - 		let progress = this.playing ? this.progress : 0,
 - 			radius = ( this.diameter2 ) - this.thickness,
 - 			x = this.diameter2,
 - 			y = this.diameter2,
 - 			iconSize = 28;
 - 
 - 		// Ease towards 1
 - 		this.progressOffset += ( 1 - this.progressOffset ) * 0.1;
 - 
 - 		const endAngle = ( - Math.PI / 2 ) + ( progress * ( Math.PI * 2 ) );
 - 		const startAngle = ( - Math.PI / 2 ) + ( this.progressOffset * ( Math.PI * 2 ) );
 - 
 - 		this.context.save();
 - 		this.context.clearRect( 0, 0, this.diameter, this.diameter );
 - 
 - 		// Solid background color
 - 		this.context.beginPath();
 - 		this.context.arc( x, y, radius + 4, 0, Math.PI * 2, false );
 - 		this.context.fillStyle = 'rgba( 0, 0, 0, 0.4 )';
 - 		this.context.fill();
 - 
 - 		// Draw progress track
 - 		this.context.beginPath();
 - 		this.context.arc( x, y, radius, 0, Math.PI * 2, false );
 - 		this.context.lineWidth = this.thickness;
 - 		this.context.strokeStyle = 'rgba( 255, 255, 255, 0.2 )';
 - 		this.context.stroke();
 - 
 - 		if( this.playing ) {
 - 			// Draw progress on top of track
 - 			this.context.beginPath();
 - 			this.context.arc( x, y, radius, startAngle, endAngle, false );
 - 			this.context.lineWidth = this.thickness;
 - 			this.context.strokeStyle = '#fff';
 - 			this.context.stroke();
 - 		}
 - 
 - 		this.context.translate( x - ( iconSize / 2 ), y - ( iconSize / 2 ) );
 - 
 - 		// Draw play/pause icons
 - 		if( this.playing ) {
 - 			this.context.fillStyle = '#fff';
 - 			this.context.fillRect( 0, 0, iconSize / 2 - 4, iconSize );
 - 			this.context.fillRect( iconSize / 2 + 4, 0, iconSize / 2 - 4, iconSize );
 - 		}
 - 		else {
 - 			this.context.beginPath();
 - 			this.context.translate( 4, 0 );
 - 			this.context.moveTo( 0, 0 );
 - 			this.context.lineTo( iconSize - 4, iconSize / 2 );
 - 			this.context.lineTo( 0, iconSize );
 - 			this.context.fillStyle = '#fff';
 - 			this.context.fill();
 - 		}
 - 
 - 		this.context.restore();
 - 
 - 	}
 - 
 - 	on( type, listener ) {
 - 		this.canvas.addEventListener( type, listener, false );
 - 	}
 - 
 - 	off( type, listener ) {
 - 		this.canvas.removeEventListener( type, listener, false );
 - 	}
 - 
 - 	destroy() {
 - 
 - 		this.playing = false;
 - 
 - 		if( this.canvas.parentNode ) {
 - 			this.container.removeChild( this.canvas );
 - 		}
 - 
 - 	}
 - 
 - }
 
 
  |