Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
270
gaia/apps/music/elements/music-seek-bar.js
Executable file
270
gaia/apps/music/elements/music-seek-bar.js
Executable file
|
|
@ -0,0 +1,270 @@
|
|||
/* global IntlHelper */
|
||||
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
var isTouch = 'ontouchstart' in window;
|
||||
|
||||
proto.createdCallback = function() {
|
||||
var shadowRoot = this.createShadowRoot();
|
||||
shadowRoot.innerHTML =
|
||||
`<style>
|
||||
#container {
|
||||
background-color: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 4.2rem;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
#container > span {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
#elapsed-time,
|
||||
#remaining-time {
|
||||
color: #e7e7e7;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
line-height: 4.2rem;
|
||||
direction: ltr;
|
||||
vertical-align: top;
|
||||
flex: 0 0 auto;
|
||||
width: 5.3rem;
|
||||
}
|
||||
#elapsed-time {
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
#remaining-time {
|
||||
padding-right: 1.5rem;
|
||||
text-align: right;
|
||||
}
|
||||
#seek-bar {
|
||||
flex: 1 0 auto;
|
||||
z-index: 1;
|
||||
}
|
||||
#seek-bar-progress {
|
||||
background-color: #a6b4b7;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
position: absolute;
|
||||
top: calc(50% - 0.1rem);
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 0.1rem;
|
||||
pointer-events: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
#seek-bar-progress::-moz-progress-bar {
|
||||
background-color: #01c5ed;
|
||||
}
|
||||
#seek-bar-indicator {
|
||||
background-color: transparent;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: calc(50% - 3rem);
|
||||
left: -3rem;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
pointer-events: none;
|
||||
transition: transform 20ms linear;
|
||||
will-change: transform;
|
||||
}
|
||||
#seek-bar-indicator:after {
|
||||
content: '';
|
||||
background-color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 1.85rem;
|
||||
left: 1.85rem;
|
||||
width: 2.1rem;
|
||||
height: 2.1rem;
|
||||
}
|
||||
#seek-bar-indicator.highlight {
|
||||
background-color: #00caf2;
|
||||
}
|
||||
</style>
|
||||
<div id="container">
|
||||
<span id="elapsed-time"></span>
|
||||
<span id="seek-bar">
|
||||
<progress id="seek-bar-progress"></progress>
|
||||
<div id="seek-bar-indicator"></div>
|
||||
</span>
|
||||
<span id="remaining-time"></span>
|
||||
</div>`;
|
||||
|
||||
var $id = shadowRoot.getElementById.bind(shadowRoot);
|
||||
|
||||
this.els = {
|
||||
container: $id('container'),
|
||||
elapsedTime: $id('elapsed-time'),
|
||||
seekBar: $id('seek-bar'),
|
||||
seekBarProgress: $id('seek-bar-progress'),
|
||||
seekBarIndicator: $id('seek-bar-indicator'),
|
||||
remainingTime: $id('remaining-time')
|
||||
};
|
||||
|
||||
var container = this.els.container;
|
||||
var seekBar = this.els.seekBar;
|
||||
|
||||
var seekTimeout = null;
|
||||
|
||||
container.addEventListener(isTouch ? 'touchstart' : 'mousedown', (evt) => {
|
||||
clearTimeout(seekTimeout);
|
||||
|
||||
container.addEventListener(isTouch ? 'touchmove' : 'mousemove',
|
||||
pointerMoveHandler);
|
||||
container.addEventListener(isTouch ? 'touchend' : 'mouseup',
|
||||
pointerEndHandler);
|
||||
|
||||
this.els.seekBarIndicator.classList.add('highlight');
|
||||
|
||||
pointerMoveHandler(evt);
|
||||
});
|
||||
|
||||
var pointerMoveHandler = (evt) => {
|
||||
var pointer = isTouch ? evt.targetTouches[0] : evt;
|
||||
var percent = clamp(0, 1,
|
||||
(pointer.clientX - seekBar.offsetLeft) / seekBar.offsetWidth);
|
||||
|
||||
if (document.documentElement.dir === 'rtl') {
|
||||
this.remainingTime = this._overrideRemainingTime =
|
||||
percent * this.duration;
|
||||
}
|
||||
|
||||
else {
|
||||
this.elapsedTime = this._overrideElapsedTime =
|
||||
percent * this.duration;
|
||||
}
|
||||
};
|
||||
|
||||
var pointerEndHandler = (evt) => {
|
||||
this.dispatchEvent(new CustomEvent('seek', {
|
||||
detail: { elapsedTime: this.elapsedTime }
|
||||
}));
|
||||
|
||||
container.removeEventListener(isTouch ? 'touchmove' : 'mousemove',
|
||||
pointerMoveHandler);
|
||||
container.removeEventListener(isTouch ? 'touchend' : 'mouseup',
|
||||
pointerEndHandler);
|
||||
|
||||
seekTimeout = setTimeout(() => {
|
||||
this._overrideRemainingTime = null;
|
||||
this._overrideElapsedTime = null;
|
||||
|
||||
this.els.seekBarIndicator.classList.remove('highlight');
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this._overrideRemainingTime = null;
|
||||
this._overrideElapsedTime = null;
|
||||
|
||||
this.duration = null;
|
||||
this.elapsedTime = null;
|
||||
this.remainingTime = null;
|
||||
};
|
||||
|
||||
Object.defineProperty(proto, 'duration', {
|
||||
get: function() {
|
||||
return this._duration;
|
||||
},
|
||||
|
||||
set: function(value) {
|
||||
if (isNaN(value)) {
|
||||
this._duration = null;
|
||||
this.remainingTime = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._duration = value;
|
||||
this.remainingTime = this._duration - this._elapsedTime;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'elapsedTime', {
|
||||
get: function() {
|
||||
return this._elapsedTime;
|
||||
},
|
||||
|
||||
set: function(value) {
|
||||
if (isNaN(value)) {
|
||||
this._elapsedTime = null;
|
||||
this.remainingTime = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._elapsedTime = this._overrideElapsedTime !== null ?
|
||||
this._overrideElapsedTime : value;
|
||||
this.remainingTime = this._duration - this._elapsedTime;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(proto, 'remainingTime', {
|
||||
get: function() {
|
||||
return this._remainingTime;
|
||||
},
|
||||
|
||||
set: function(value) {
|
||||
var indeterminate = isNaN(value) || isNaN(this._duration);
|
||||
if (indeterminate) {
|
||||
this._remainingTime = null;
|
||||
this._elapsedTime = null;
|
||||
}
|
||||
|
||||
else {
|
||||
this._remainingTime = this._overrideRemainingTime !== null ?
|
||||
this._overrideRemainingTime : value;
|
||||
this._elapsedTime = this._duration - this._remainingTime;
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
if (indeterminate) {
|
||||
this.els.remainingTime.textContent = '---:--';
|
||||
this.els.elapsedTime.textContent = '--:--';
|
||||
return;
|
||||
}
|
||||
|
||||
var percent = this._duration ? this._elapsedTime / this._duration : 0;
|
||||
var x = this.els.seekBar.offsetWidth * percent;
|
||||
|
||||
if (document.documentElement.dir === 'rtl') {
|
||||
x = this.els.seekBar.offsetWidth - x;
|
||||
}
|
||||
|
||||
this.els.seekBarIndicator.style.transform = 'translateX(' + x + 'px)';
|
||||
|
||||
IntlHelper.get('duration').then((duration) => {
|
||||
this.els.remainingTime.textContent =
|
||||
duration.format(-this._remainingTime * 1000);
|
||||
this.els.elapsedTime.textContent =
|
||||
duration.format(this._elapsedTime * 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function clamp(min, max, value) {
|
||||
return Math.min(Math.max(min, value), max);
|
||||
}
|
||||
|
||||
IntlHelper.define('duration', 'mozduration', {
|
||||
minUnit: 'second',
|
||||
maxUnit: 'minute'
|
||||
});
|
||||
|
||||
try {
|
||||
window.MusicSeekBar = document.registerElement('music-seek-bar', {
|
||||
prototype: proto
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.name !== 'NotSupportedError') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
})(window);
|
||||
Loading…
Reference in a new issue