fixed player

This commit is contained in:
DIYgod 2018-03-22 01:34:24 +08:00
parent 20e2bd3e3d
commit a002a4b180
No known key found for this signature in database
GPG Key ID: EC0B76A252D3EF67
16 changed files with 383 additions and 60 deletions

View File

@ -186,4 +186,20 @@ const ap7 = new APlayer({
}
}
}
});
let ap8;
$.ajax({
url: 'https://api.i-meto.com/meting/api?server=netease&type=playlist&id=35798529',
success: function (list) {
ap8 = new APlayer({
element: document.getElementById('player8'),
mutex: true,
theme: '#ad7a86',
order: 'random',
lrcType: 3,
fixed: true,
audio: JSON.parse(list)
});
}
});

View File

@ -12,7 +12,7 @@
max-width: 32rem;
margin-left: auto;
margin-right: auto;
margin-bottom: 50px;
margin-bottom: 150px;
}
h1 {
font-size: 54px;
@ -96,10 +96,14 @@
<h3>HLS</h3>
<div id="player6" class="aplayer"></div>
<div id="player7" class="aplayer"></div>
<div id="player8" class="aplayer"></div>
</div>
<script>
var vConsole = new VConsole();
if (/mobile/i.test(window.navigator.userAgent)) {
new VConsole();
}
</script>
<script src="https://cdn.jsdelivr.net/npm/jquery"></script>
<script src="demo.js"></script>
</body>
</html>

View File

@ -54,6 +54,7 @@
},
"dependencies": {
"balloon-css": "^0.5.0",
"promise-polyfill": "7.1.0"
"promise-polyfill": "7.1.0",
"smoothscroll": "0.4.0"
}
}

3
src/assets/right.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32">
<path d="M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z"></path>
</svg>

After

Width:  |  Height:  |  Size: 184 B

3
src/assets/skip.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32">
<path d="M25.468 6.947c-0.326-0.172-0.724-0.151-1.030 0.057l-6.438 4.38v-3.553c0-0.371-0.205-0.71-0.532-0.884-0.326-0.172-0.724-0.151-1.030 0.057l-12 8.164c-0.274 0.186-0.438 0.496-0.438 0.827s0.164 0.641 0.438 0.827l12 8.168c0.169 0.115 0.365 0.174 0.562 0.174 0.16 0 0.321-0.038 0.468-0.116 0.327-0.173 0.532-0.514 0.532-0.884v-3.556l6.438 4.382c0.169 0.115 0.365 0.174 0.562 0.174 0.16 0 0.321-0.038 0.468-0.116 0.327-0.173 0.532-0.514 0.532-0.884v-16.333c0-0.371-0.205-0.71-0.532-0.884z"></path>
</svg>

After

Width:  |  Height:  |  Size: 585 B

View File

@ -73,6 +73,114 @@ $aplayer-height-lrc: $aplayer-height + $lrc-height - 6;
}
}
&.aplayer-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: 0;
z-index: 99;
overflow: visible;
max-width: 400px;
box-shadow: none;
.aplayer-list {
margin-bottom: 66px;
}
.aplayer-body {
position: fixed;
bottom: 0;
left: 0;
right: 0;
margin: 0;
z-index: 99;
background: #fff;
padding-right: 18px;
transition: all 0.3s ease;
max-width: 400px;
}
.aplayer-lrc {
display: block;
position: fixed;
bottom: 10px;
left: 0;
right: 0;
margin: 0;
z-index: 98;
pointer-events: none;
text-shadow: 0 0 1px #fff, 0 0 10px #fff;
&:before,
&:after {
display: none;
}
}
.aplayer-info {
transform: scaleX(1);
transform-origin: 0 0;
transition: all 0.3s ease;
border-bottom: none;
border-top: 1px solid #e9e9e9;
.aplayer-music {
width: calc(100% - 105px);
}
}
.aplayer-miniswitcher {
display: block;
}
&.aplayer-narrow {
.aplayer-info {
display: block;
transform: scaleX(0);
}
.aplayer-body {
width: $aplayer-height !important;
}
.aplayer-miniswitcher .aplayer-icon {
transform: rotateY(0);
}
}
.aplayer-icon-back,
.aplayer-icon-play,
.aplayer-icon-forward {
display: inline-block;
}
.aplayer-icon-back,
.aplayer-icon-play,
.aplayer-icon-forward,
.aplayer-icon-menu {
position: absolute;
bottom: 27px;
width: 20px;
height: 20px;
}
.aplayer-icon-back {
right: 75px;
}
.aplayer-icon-play {
right: 50px;
}
.aplayer-icon-forward {
right: 25px;
}
.aplayer-icon-menu {
right: 0;
}
}
&.aplayer-mobile {
.aplayer-icon-volume-down {
display: none;
@ -112,17 +220,24 @@ $aplayer-height-lrc: $aplayer-height + $lrc-height - 6;
padding: 0;
font-size: 12px;
margin: 0;
display: inline;
display: inline-block;
path {
transition: all .2s ease-in-out;
}
}
.aplayer-icon-order {
.aplayer-icon-order,
.aplayer-icon-back,
.aplayer-icon-play,
.aplayer-icon-forward {
display: none;
}
.aplayer-icon-forward {
transform: rotate(180deg);
}
.aplayer-lrc-content {
display: none;
}
@ -428,6 +543,7 @@ $aplayer-height-lrc: $aplayer-height + $lrc-height - 6;
opacity: 1;
overflow: visible;
height: initial !important;
min-height: 16px;
}
}
@ -447,7 +563,7 @@ $aplayer-height-lrc: $aplayer-height + $lrc-height - 6;
overflow: hidden;
&.aplayer-list-hide {
height: 0 !important;
max-height: 0 !important;
}
ol {
@ -460,10 +576,6 @@ $aplayer-height-lrc: $aplayer-height + $lrc-height - 6;
width: 5px;
}
&::-webkit-scrollbar-track {
background-color: #f9f9f9;
}
&::-webkit-scrollbar-thumb {
border-radius: 3px;
background-color: #eee;
@ -540,6 +652,35 @@ $aplayer-height-lrc: $aplayer-height + $lrc-height - 6;
background-color: #f4f4f5;
color: #909399;
}
.aplayer-miniswitcher {
display: none;
position: absolute;
top: 0;
right: 0;
bottom: 0;
height: 100%;
background: #e6e6e6;
width: 18px;
border-radius: 0 2px 2px 0;
.aplayer-icon {
height: 100%;
width: 100%;
transform: rotateY(180deg);
transition: all 0.3s ease;
path {
fill: #666;
}
&:hover {
path {
fill: #000;
}
}
}
}
}
@keyframes aplayer-roll {

View File

@ -13,6 +13,8 @@ class Controller {
if (!utils.isMobile) {
this.initVolumeButton();
}
this.initMiniSwitcher();
this.initSkipButton();
}
initPlayButton () {
@ -64,7 +66,7 @@ class Controller {
});
const thumbMove = (e) => {
let percentage = 1 - ((e.clientY || e.changedTouches[0].clientY) - utils.getElementViewTop(this.player.template.volumeBar)) / this.player.template.volumeBar.clientHeight;
let percentage = 1 - ((e.clientY || e.changedTouches[0].clientY) - utils.getElementViewTop(this.player.template.volumeBar, this.player.options.fixed)) / this.player.template.volumeBar.clientHeight;
percentage = Math.max(percentage, 0);
percentage = Math.min(percentage, 1);
this.player.volume(percentage);
@ -74,7 +76,7 @@ class Controller {
this.player.template.volumeBarWrap.classList.remove('aplayer-volume-bar-wrap-active');
document.removeEventListener(utils.nameMap.dragEnd, thumbUp);
document.removeEventListener(utils.nameMap.dragMove, thumbMove);
let percentage = 1 - ((e.clientY || e.changedTouches[0].clientY) - utils.getElementViewTop(this.player.template.volumeBar)) / this.player.template.volumeBar.clientHeight;
let percentage = 1 - ((e.clientY || e.changedTouches[0].clientY) - utils.getElementViewTop(this.player.template.volumeBar, this.player.options.fixed)) / this.player.template.volumeBar.clientHeight;
percentage = Math.max(percentage, 0);
percentage = Math.min(percentage, 1);
this.player.volume(percentage);
@ -130,12 +132,28 @@ class Controller {
}
initMenuButton () {
this.player.template.list.style.height = 33 * this.player.options.audio.length - 1 + 'px';
this.player.template.listOl.style.height = 33 * this.player.options.audio.length - 1 + 'px';
this.player.template.menu.addEventListener('click', () => {
this.player.list.toggle();
});
}
initMiniSwitcher () {
this.player.template.miniSwitcher.addEventListener('click', () => {
this.player.setMode(this.player.mode === 'mini' ? 'normal' : 'mini');
});
}
initSkipButton () {
this.player.template.skipBackButton.addEventListener('click', () => {
this.player.skipBack();
});
this.player.template.skipForwardButton.addEventListener('click', () => {
this.player.skipForward();
});
this.player.template.skipPlayButton.addEventListener('click', () => {
this.player.toggle();
});
}
}
export default Controller;

View File

@ -10,6 +10,8 @@ import loopAll from '../assets/loop-all.svg';
import loopOne from '../assets/loop-one.svg';
import loopNone from '../assets/loop-none.svg';
import loading from '../assets/loading.svg';
import right from '../assets/right.svg';
import skip from '../assets/skip.svg';
const Icons = {
play: play,
@ -24,6 +26,8 @@ const Icons = {
loopOne: loopOne,
loopNone: loopNone,
loading: loading,
right: right,
skip: skip,
};
export default Icons;

View File

@ -1,5 +1,6 @@
import tplListItem from '../template/list-item.art';
import utils from './utils';
import smoothScroll from 'smoothscroll';
class List {
constructor (player) {
@ -33,6 +34,7 @@ class List {
show () {
this.player.events.trigger('listshow');
this.player.template.list.classList.remove('aplayer-list-hide');
this.player.template.listOl.scrollTop = this.index * 33;
}
hide () {
@ -65,8 +67,6 @@ class List {
if (wasSingle && this.audios.length > 1) {
this.player.container.classList.add('aplayer-withlist');
}
this.player.template.list.style.height = this.audios.length * 33 - 1 + 'px';
this.player.template.listOl.style.height = this.audios.length * 33 - 1 + 'px';
this.player.randomOrder = utils.randomOrder(this.audios.length);
this.player.template.listCurs = this.player.container.querySelectorAll('.aplayer-list-cur');
@ -110,8 +110,6 @@ class List {
if (this.audios.length === 1) {
this.player.container.classList.remove('aplayer-withlist');
}
this.player.template.list.style.height = this.audios.length * 33 - 1 + 'px';
this.player.template.listOl.style.height = this.audios.length * 33 - 1 + 'px';
this.player.template.listCurs = this.player.container.querySelectorAll('.aplayer-list-cur');
}
@ -141,11 +139,12 @@ class List {
}
this.player.container.querySelectorAll('.aplayer-list li')[this.index].classList.add('aplayer-list-light');
this.player.template.list.scrollTop = this.index * 33;
smoothScroll(this.index * 33, 500, null, this.player.template.listOl);
this.player.setAudio(audio);
this.player.lrc && this.player.lrc.switch(this.index);
this.player.lrc && this.player.lrc.update(0);
// set duration time
if (this.player.duration !== 1) { // compatibility: Android browsers will output 1 at first

View File

@ -83,7 +83,7 @@ class Lrc {
if (lrc_s) {
lrc_s = lrc_s.replace(/([^\]^\n])\[/g, (match, p1) => p1 + '\n[');
const lyric = lrc_s.split('\n');
const lrc = [];
let lrc = [];
const lyricLen = lyric.length;
for (let i = 0; i < lyricLen; i++) {
// match lrc time
@ -105,6 +105,7 @@ class Lrc {
}
}
// sort by time
lrc = lrc.filter((item) => item[1]);
lrc.sort((a, b) => a[0] - b[0]);
return lrc;
}

View File

@ -3,7 +3,8 @@ export default (options) => {
// default options
const defaultOption = {
container: options.element || document.getElementsByClassName('aplayer')[0],
mini: options.narrow || false,
mini: options.narrow || options.fixed || false,
fixed: false,
autoplay: false,
mutex: true,
lrcType: options.showlrc || options.lrc || 0,
@ -12,8 +13,8 @@ export default (options) => {
loop: 'all',
order: 'list',
volume: 0.7,
listFolded: false,
listMaxHeight: options.listmaxheight,
listFolded: options.fixed,
listMaxHeight: options.listmaxheight || '250px',
audio: options.music || [],
storageName: 'aplayer-setting'
};

View File

@ -32,7 +32,7 @@ class APlayer {
this.randomOrder = utils.randomOrder(this.options.audio.length);
this.container.classList.add('aplayer');
if (this.options.lrcType) {
if (this.options.lrcType && !this.options.fixed) {
this.container.classList.add('aplayer-withlrc');
}
if (this.options.audio.length > 1) {
@ -45,9 +45,6 @@ class APlayer {
if (this.arrow) {
this.container.classList.add('aplayer-arrow');
}
if (this.options.mini) {
this.setMode('mini');
}
// save lrc
this.container = this.options.container;
@ -66,6 +63,14 @@ class APlayer {
randomOrder: this.randomOrder,
});
if (this.options.fixed) {
this.container.classList.add('aplayer-fixed');
this.template.body.style.width = this.template.body.offsetWidth - 18 + 'px';
}
if (this.options.mini) {
this.setMode('mini');
this.template.info.style.display = 'block';
}
if (this.template.info.offsetWidth < 200) {
this.template.time.classList.add('aplayer-time-narrow');
}
@ -150,6 +155,15 @@ class APlayer {
// audio download error: an error occurs
this.on('error', () => {
if (this.list.audios.length) {
this.notice('An audio error has occurred, player will skip forward in 2 seconds.');
setTimeout(() => {
this.skipForward();
if (!this.paused) {
this.play();
}
}, 2000);
}
else {
this.notice('An audio error has occurred.');
}
});
@ -169,11 +183,11 @@ class APlayer {
}
else if (this.options.order === 'random') {
if (this.randomOrder.indexOf(this.list.index) < this.randomOrder.length - 1) {
this.list.switch(this.nextRandomNum());
this.list.switch(this.nextIndex());
this.play();
}
else {
this.list.switch(this.nextRandomNum());
this.list.switch(this.nextIndex());
this.pause();
}
}
@ -183,12 +197,7 @@ class APlayer {
this.play();
}
else if (this.options.loop === 'all') {
if (this.options.order === 'list') {
this.list.switch((this.list.index + 1) % this.list.audios.length);
}
else if (this.options.order === 'random') {
this.list.switch(this.nextRandomNum());
}
this.skipForward();
this.play();
}
});
@ -277,6 +286,7 @@ class APlayer {
setTimeout(() => {
this.template.button.innerHTML = Icons.pause;
}, 100);
this.template.skipPlayButton.innerHTML = Icons.pause;
}
this.timer.enable('loading');
@ -297,8 +307,7 @@ class APlayer {
if (playPromise) {
playPromise.catch((e) => {
console.warn(e);
if (e.name === 'NotAllowedError' ||
e.name === 'NotSupportedError') {
if (e.name === 'NotAllowedError') {
this.setUIPaused();
}
});
@ -315,6 +324,7 @@ class APlayer {
setTimeout(() => {
this.template.button.innerHTML = Icons.play;
}, 100);
this.template.skipPlayButton.innerHTML = Icons.play;
}
this.container.classList.remove('aplayer-loading');
@ -381,24 +391,6 @@ class APlayer {
}
}
/**
* get next random number
*/
nextRandomNum () {
if (this.list.audios.length > 1) {
const index = this.randomOrder.indexOf(this.list.index);
if (index === this.randomOrder.length - 1) {
return this.randomOrder[0];
}
else {
return this.randomOrder[index + 1];
}
}
else {
return 0;
}
}
// abandoned
switchAudio (index) {
this.list.switch(index);
@ -442,15 +434,63 @@ class APlayer {
if (this.noticeTime) {
clearTimeout(this.noticeTime);
}
this.events.trigger('notice_show', text);
this.events.trigger('noticeshow', text);
if (time) {
this.noticeTime = setTimeout(() => {
this.template.notice.style.opacity = 0;
this.events.trigger('notice_hide');
this.events.trigger('noticehide');
}, time);
}
}
prevIndex () {
if (this.list.audios.length > 1) {
if (this.options.order === 'list') {
return this.list.index - 1 < 0 ? this.list.audios.length - 1 : this.list.index - 1;
}
else if (this.options.order === 'random') {
const index = this.randomOrder.indexOf(this.list.index);
if (index === 0) {
return this.randomOrder[this.randomOrder.length - 1];
}
else {
return this.randomOrder[index - 1];
}
}
}
else {
return 0;
}
}
nextIndex () {
if (this.list.audios.length > 1) {
if (this.options.order === 'list') {
return (this.list.index + 1) % this.list.audios.length;
}
else if (this.options.order === 'random') {
const index = this.randomOrder.indexOf(this.list.index);
if (index === this.randomOrder.length - 1) {
return this.randomOrder[0];
}
else {
return this.randomOrder[index + 1];
}
}
}
else {
return 0;
}
}
skipBack () {
this.list.switch(this.prevIndex());
}
skipForward () {
this.list.switch(this.nextIndex());
}
static get version () {
/* global APLAYER_VERSION */
return APLAYER_VERSION;

View File

@ -33,6 +33,7 @@ class Template {
this.time = this.container.querySelector('.aplayer-time');
this.barWrap = this.container.querySelector('.aplayer-bar-wrap');
this.button = this.container.querySelector('.aplayer-button');
this.body = this.container.querySelector('.aplayer-body');
this.list = this.container.querySelector('.aplayer-list');
this.listOl = this.container.querySelector('.aplayer-list ol');
this.listCurs = this.container.querySelectorAll('.aplayer-list-cur');
@ -51,6 +52,10 @@ class Template {
this.author = this.container.querySelector('.aplayer-author');
this.dtime = this.container.querySelector('.aplayer-dtime');
this.notice = this.container.querySelector('.aplayer-notice');
this.miniSwitcher = this.container.querySelector('.aplayer-miniswitcher');
this.skipBackButton = this.container.querySelector('.aplayer-icon-back');
this.skipForwardButton = this.container.querySelector('.aplayer-icon-forward');
this.skipPlayButton = this.container.querySelector('.aplayer-icon-play');
}
}

View File

@ -39,7 +39,7 @@ const utils = {
return actualLeft - elementScrollLeft;
},
getElementViewTop: (element) => {
getElementViewTop: (element, noScrollTop) => {
let actualTop = element.offsetTop;
let current = element.offsetParent;
let elementScrollTop = 0;
@ -48,7 +48,7 @@ const utils = {
current = current.offsetParent;
}
elementScrollTop = document.body.scrollTop + document.documentElement.scrollTop;
return actualTop - elementScrollTop;
return noScrollTop ? actualTop : actualTop - elementScrollTop;
},
isMobile: isMobile,

View File

@ -1,3 +1,4 @@
{{ if !options.fixed }}
<div class="aplayer-body">
<div class="aplayer-pic" style="{{ if cover }}background-image: url(&quot;{{ cover }}&quot;);{{ /if }}background-color: {{ options.theme }};">
<div class="aplayer-button aplayer-play">{{@ icons.play }}</div>
@ -25,6 +26,15 @@
<span class="aplayer-time-inner">
<span class="aplayer-ptime">00:00</span> / <span class="aplayer-dtime">00:00</span>
</span>
<span class="aplayer-icon aplayer-icon-back">
{{@ icons.skip }}
</span>
<span class="aplayer-icon aplayer-icon-play">
{{@ icons.play }}
</span>
<span class="aplayer-icon aplayer-icon-forward">
{{@ icons.skip }}
</span>
<div class="aplayer-volume-wrap">
<button type="button" class="aplayer-icon aplayer-icon-volume-down">
{{@ icons.volumeDown }}
@ -48,6 +58,7 @@
</div>
</div>
<div class="aplayer-notice"></div>
<div class="aplayer-miniswitcher"><button class="aplayer-icon">{{@ icons.right }}</button></div>
</div>
<div class="aplayer-list{{ if options.listFolded }} aplayer-list-hide{{ /if }}"{{ if options.listMaxHeight }} style="max-height: {{ options.listMaxHeight }}"{{ /if }}>
<ol{{ if options.listMaxHeight }} style="max-height: {{ options.listMaxHeight }}"{{ /if }}>
@ -57,4 +68,76 @@
index: 1
}) }}
</ol>
</div>
</div>
{{ else }}
<div class="aplayer-list{{ if options.listFolded }} aplayer-list-hide{{ /if }}"{{ if options.listMaxHeight }} style="max-height: {{ options.listMaxHeight }}"{{ /if }}>
<ol{{ if options.listMaxHeight }} style="max-height: {{ options.listMaxHeight }}"{{ /if }}>
{{ include './list-item.art' getObject({
theme: options.theme,
audio: options.audio,
index: 1
}) }}
</ol>
</div>
<div class="aplayer-body">
<div class="aplayer-pic" style="{{ if cover }}background-image: url(&quot;{{ cover }}&quot;);{{ /if }}background-color: {{ options.theme }};">
<div class="aplayer-button aplayer-play">{{@ icons.play }}</div>
</div>
<div class="aplayer-info" style="display: none;">
<div class="aplayer-music">
<span class="aplayer-title">No audio</span>
<span class="aplayer-author"></span>
</div>
<div class="aplayer-controller">
<div class="aplayer-bar-wrap">
<div class="aplayer-bar">
<div class="aplayer-loaded" style="width: 0"></div>
<div class="aplayer-played" style="width: 0; background: {{ options.theme }};">
<span class="aplayer-thumb" style="background: {{ options.theme }};">
<span class="aplayer-loading-icon">{{@ icons.loading }}</span>
</span>
</div>
</div>
</div>
<div class="aplayer-time">
<span class="aplayer-time-inner">
<span class="aplayer-ptime">00:00</span> / <span class="aplayer-dtime">00:00</span>
</span>
<span class="aplayer-icon aplayer-icon-back">
{{@ icons.skip }}
</span>
<span class="aplayer-icon aplayer-icon-play">
{{@ icons.play }}
</span>
<span class="aplayer-icon aplayer-icon-forward">
{{@ icons.skip }}
</span>
<div class="aplayer-volume-wrap">
<button type="button" class="aplayer-icon aplayer-icon-volume-down">
{{@ icons.volumeDown }}
</button>
<div class="aplayer-volume-bar-wrap">
<div class="aplayer-volume-bar">
<div class="aplayer-volume" style="height: 80%; background: {{ options.theme }};"></div>
</div>
</div>
</div>
<button type="button" class="aplayer-icon aplayer-icon-order">
{{ if options.order === 'list' }}{{@ icons.orderList }}{{ else if options.order === 'random' }}{{@ icons.orderRandom }}{{ /if }}
</button>
<button type="button" class="aplayer-icon aplayer-icon-loop">
{{ if options.loop === 'one' }}{{@ icons.loopOne }}{{ else if options.loop === 'all' }}{{@ icons.loopAll }}{{ else if options.loop === 'none' }}{{@ icons.loopNone }}{{ /if }}
</button>
<button type="button" class="aplayer-icon aplayer-icon-menu">
{{@ icons.menu }}
</button>
</div>
</div>
</div>
<div class="aplayer-notice"></div>
<div class="aplayer-miniswitcher"><button class="aplayer-icon">{{@ icons.right }}</button></div>
</div>
<div class="aplayer-lrc">
<div class="aplayer-lrc-contents" style="transform: translateY(0); -webkit-transform: translateY(0);"></div>
</div>
{{/if}}

View File

@ -6136,6 +6136,10 @@ slide@^1.1.5:
version "1.1.6"
resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
smoothscroll@0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/smoothscroll/-/smoothscroll-0.4.0.tgz#40e507b46461408ba1b787d0081e1e883c4124a5"
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "http://registry.npm.taobao.org/snapdragon-node/download/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"