JavaScript
语言:
JaveScriptBabelCoffeeScript
确定
function Playback(container, progressCheck) {
this.diameter = 100;
this.diameterOnHover = 120;
this.thickness = 5;
this.thicknessOnHover = 3;
this.iconSize = 24;
this.diameterStart = this.diameter;
this.thicknessStart = this.thickness;
// quick intro
this.thickness = 1;
this.diameter = 40;
this.isOver = false;
this.playing = false;
this.progress = 0;
this.progressOffset = 1;
this.container = container;
this.progressCheck = progressCheck;
this.canvas = document.createElement('canvas');
this.canvas.className = 'playback';
this.canvas.width = this.diameterOnHover;
this.canvas.height = this.diameterOnHover;
this.context = this.canvas.getContext('2d');
this.container.appendChild(this.canvas);
this.animate();
this.on('mouseover', function() {
this.isOver = true;
}.bind(this));
this.on('mouseout', function() {
this.isOver = false;
}.bind(this));
}
Playback.prototype.setPlaying = function(value) {
var wasPlaying = this.playing;
this.playing = value;
};
Playback.prototype.isPlaying = function() {
return this.playing;
};
Playback.prototype.animate = function() {
var progressBefore = this.progress;
if (this.playing) {
this.progress = this.progressCheck();
} else {
this.progress += (1 - this.progress) * 0.1;
this.progress = this.progress > 0.99999 ? 0.99999 : this.progress;
}
if (progressBefore > 0.8 && this.progress < 0.2) {
this.progressOffset = this.progress;
}
this.diameter += ((this.isOver ? this.diameterOnHover : this.diameterStart) - this.diameter) * 0.1;
this.thickness += ((this.isOver ? this.thicknessOnHover : this.thicknessStart) - this.thickness) * 0.1;
this.render();
requestAnimationFrame(this.animate.bind(this));
};
Playback.prototype.render = function() {
var progress = this.progress,
radius = (this.diameter / 2) - this.thickness,
x = this.diameter / 2,
y = this.diameter / 2,
iconSize = this.iconSize;
this.progressOffset += (1 - this.progressOffset) * 0.1;
var endAngle = (-Math.PI / 2) + (progress * (Math.PI * 2));
var startAngle = (-Math.PI / 2) + (this.progressOffset * (Math.PI * 2));
this.context.save();
this.context.clearRect(0, 0, this.diameterOnHover, this.diameterOnHover);
this.context.translate((this.diameterOnHover - this.diameter) / 2, (this.diameterOnHover - this.diameter) / 2);
// Solid background color
this.context.beginPath();
this.context.arc(x, y, radius + 2, 0, Math.PI * 2, false);
this.context.fillStyle = 'rgba( 0, 0, 0, 0.3 )';
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.3)';
this.context.stroke();
// 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(2, 0);
this.context.moveTo(0, 0);
this.context.lineTo(iconSize - 2, iconSize / 2);
this.context.lineTo(0, iconSize);
this.context.fillStyle = '#fff';
this.context.fill();
}
this.context.restore();
};
Playback.prototype.on = function(type, listener) {
this.canvas.addEventListener(type, listener, false);
};
Playback.prototype.off = function(type, listener) {
this.canvas.removeEventListener(type, listener, false);
};
var demoElement = document.querySelector('.playback-demo');
var t = Date.now();
var p = new Playback(demoElement, function() {
return (Date.now() - t) * 0.04 % 100 / 100;
});
p.setPlaying(true);
p.on('click', function() {
t = Date.now();
p.render();
p.setPlaying(!p.isPlaying());
});