Move from miccall theme to kaze
3
aplayer/src/assets/loading.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32">
|
||||
<path d="M4 16c0-6.6 5.4-12 12-12s12 5.4 12 12c0 1.2-0.8 2-2 2s-2-0.8-2-2c0-4.4-3.6-8-8-8s-8 3.6-8 8 3.6 8 8 8c1.2 0 2 0.8 2 2s-0.8 2-2 2c-6.6 0-12-5.4-12-12z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 253 B |
3
aplayer/src/assets/loop-all.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 29 32">
|
||||
<path d="M9.333 9.333h13.333v4l5.333-5.333-5.333-5.333v4h-16v8h2.667v-5.333zM22.667 22.667h-13.333v-4l-5.333 5.333 5.333 5.333v-4h16v-8h-2.667v5.333z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 244 B |
3
aplayer/src/assets/loop-none.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 29 32">
|
||||
<path d="M2.667 7.027l1.707-1.693 22.293 22.293-1.693 1.707-4-4h-11.64v4l-5.333-5.333 5.333-5.333v4h8.973l-8.973-8.973v0.973h-2.667v-3.64l-4-4zM22.667 17.333h2.667v5.573l-2.667-2.667v-2.907zM22.667 6.667v-4l5.333 5.333-5.333 5.333v-4h-10.907l-2.667-2.667h13.573z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 357 B |
3
aplayer/src/assets/loop-one.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 33 32">
|
||||
<path d="M9.333 9.333h13.333v4l5.333-5.333-5.333-5.333v4h-16v8h2.667v-5.333zM22.667 22.667h-13.333v-4l-5.333 5.333 5.333 5.333v-4h16v-8h-2.667v5.333zM17.333 20v-8h-1.333l-2.667 1.333v1.333h2v5.333h2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 294 B |
3
aplayer/src/assets/lrc.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32">
|
||||
<path d="M26.667 5.333h-21.333c-0 0-0.001 0-0.001 0-1.472 0-2.666 1.194-2.666 2.666 0 0 0 0.001 0 0.001v-0 16c0 0 0 0.001 0 0.001 0 1.472 1.194 2.666 2.666 2.666 0 0 0.001 0 0.001 0h21.333c0 0 0.001 0 0.001 0 1.472 0 2.666-1.194 2.666-2.666 0-0 0-0.001 0-0.001v0-16c0-0 0-0.001 0-0.001 0-1.472-1.194-2.666-2.666-2.666-0 0-0.001 0-0.001 0h0zM5.333 16h5.333v2.667h-5.333v-2.667zM18.667 24h-13.333v-2.667h13.333v2.667zM26.667 24h-5.333v-2.667h5.333v2.667zM26.667 18.667h-13.333v-2.667h13.333v2.667z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 590 B |
3
aplayer/src/assets/menu.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 22 32">
|
||||
<path d="M20.8 14.4q0.704 0 1.152 0.48t0.448 1.12-0.48 1.12-1.12 0.48h-19.2q-0.64 0-1.12-0.48t-0.48-1.12 0.448-1.12 1.152-0.48h19.2zM1.6 11.2q-0.64 0-1.12-0.48t-0.48-1.12 0.448-1.12 1.152-0.48h19.2q0.704 0 1.152 0.48t0.448 1.12-0.48 1.12-1.12 0.48h-19.2zM20.8 20.8q0.704 0 1.152 0.48t0.448 1.12-0.48 1.12-1.12 0.48h-19.2q-0.64 0-1.12-0.48t-0.48-1.12 0.448-1.12 1.152-0.48h19.2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 472 B |
3
aplayer/src/assets/order-list.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32">
|
||||
<path d="M0.622 18.334h19.54v7.55l11.052-9.412-11.052-9.413v7.549h-19.54v3.725z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 174 B |
3
aplayer/src/assets/order-random.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32">
|
||||
<path d="M22.667 4l7 6-7 6 7 6-7 6v-4h-3.653l-3.76-3.76 2.827-2.827 2.587 2.587h2v-8h-2l-12 12h-6v-4h4.347l12-12h3.653v-4zM2.667 8h6l3.76 3.76-2.827 2.827-2.587-2.587h-4.347v-4z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 272 B |
3
aplayer/src/assets/pause.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 17 32">
|
||||
<path d="M14.080 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048zM2.88 4.8q2.88 0 2.88 2.048v18.24q0 2.112-2.88 2.112t-2.88-2.112v-18.24q0-2.048 2.88-2.048z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 288 B |
3
aplayer/src/assets/play.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 16 31">
|
||||
<path d="M15.552 15.168q0.448 0.32 0.448 0.832 0 0.448-0.448 0.768l-13.696 8.512q-0.768 0.512-1.312 0.192t-0.544-1.28v-16.448q0-0.96 0.544-1.28t1.312 0.192z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 251 B |
3
aplayer/src/assets/right.svg
Normal 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
aplayer/src/assets/skip.svg
Normal 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 |
3
aplayer/src/assets/volume-down.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 28 32">
|
||||
<path d="M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 604 B |
3
aplayer/src/assets/volume-off.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 28 32">
|
||||
<path d="M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
3
aplayer/src/assets/volume-up.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 28 32">
|
||||
<path d="M13.728 6.272v19.456q0 0.448-0.352 0.8t-0.8 0.32-0.8-0.32l-5.952-5.952h-4.672q-0.48 0-0.8-0.352t-0.352-0.8v-6.848q0-0.48 0.352-0.8t0.8-0.352h4.672l5.952-5.952q0.32-0.32 0.8-0.32t0.8 0.32 0.352 0.8zM20.576 16q0 1.344-0.768 2.528t-2.016 1.664q-0.16 0.096-0.448 0.096-0.448 0-0.8-0.32t-0.32-0.832q0-0.384 0.192-0.64t0.544-0.448 0.608-0.384 0.512-0.64 0.192-1.024-0.192-1.024-0.512-0.64-0.608-0.384-0.544-0.448-0.192-0.64q0-0.48 0.32-0.832t0.8-0.32q0.288 0 0.448 0.096 1.248 0.48 2.016 1.664t0.768 2.528zM25.152 16q0 2.72-1.536 5.056t-4 3.36q-0.256 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.704 0.672-1.056 1.024-0.512 1.376-0.8 1.312-0.96 2.048-2.4t0.736-3.104-0.736-3.104-2.048-2.4q-0.352-0.288-1.376-0.8-0.672-0.352-0.672-1.056 0-0.448 0.32-0.8t0.8-0.352q0.224 0 0.48 0.096 2.496 1.056 4 3.36t1.536 5.056zM29.728 16q0 4.096-2.272 7.552t-6.048 5.056q-0.224 0.096-0.448 0.096-0.48 0-0.832-0.352t-0.32-0.8q0-0.64 0.704-1.056 0.128-0.064 0.384-0.192t0.416-0.192q0.8-0.448 1.44-0.896 2.208-1.632 3.456-4.064t1.216-5.152-1.216-5.152-3.456-4.064q-0.64-0.448-1.44-0.896-0.128-0.096-0.416-0.192t-0.384-0.192q-0.704-0.416-0.704-1.056 0-0.448 0.32-0.8t0.832-0.352q0.224 0 0.448 0.096 3.776 1.632 6.048 5.056t2.272 7.552z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
706
aplayer/src/css/index.scss
Normal file
@@ -0,0 +1,706 @@
|
||||
$aplayer-height: 66px;
|
||||
$lrc-height: 30px;
|
||||
$aplayer-height-lrc: $aplayer-height + $lrc-height - 6;
|
||||
|
||||
.aplayer {
|
||||
background: #fff;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
margin: 5px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.07), 0 1px 5px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
line-height: initial;
|
||||
position: relative;
|
||||
|
||||
* {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
path,
|
||||
circle {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-withlist {
|
||||
.aplayer-info {
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
}
|
||||
.aplayer-list {
|
||||
display: block;
|
||||
}
|
||||
.aplayer-info .aplayer-controller .aplayer-time .aplayer-icon.aplayer-icon-menu {
|
||||
display: inline;
|
||||
}
|
||||
.aplayer-icon-order {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-withlrc {
|
||||
.aplayer-pic {
|
||||
height: $aplayer-height-lrc;
|
||||
width: $aplayer-height-lrc;
|
||||
}
|
||||
.aplayer-info {
|
||||
margin-left: $aplayer-height-lrc;
|
||||
height: $aplayer-height-lrc;
|
||||
padding: 10px 7px 0 7px;
|
||||
}
|
||||
.aplayer-lrc {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-narrow {
|
||||
width: $aplayer-height;
|
||||
|
||||
.aplayer-info {
|
||||
display: none;
|
||||
}
|
||||
.aplayer-list {
|
||||
display: none;
|
||||
}
|
||||
.aplayer-pic,
|
||||
.aplayer-body {
|
||||
height: $aplayer-height;
|
||||
width: $aplayer-height;
|
||||
}
|
||||
}
|
||||
|
||||
&.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: 65px;
|
||||
border: 1px solid #eee;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.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: -1px -1px 0 #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,
|
||||
.aplayer-icon-lrc {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-arrow {
|
||||
.aplayer-icon-order,
|
||||
.aplayer-icon-loop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-loading {
|
||||
.aplayer-info .aplayer-controller .aplayer-loading-icon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played .aplayer-thumb {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aplayer-icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
opacity: .8;
|
||||
vertical-align: middle;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
|
||||
path {
|
||||
transition: all .2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-icon-order,
|
||||
.aplayer-icon-back,
|
||||
.aplayer-icon-play,
|
||||
.aplayer-icon-forward,
|
||||
.aplayer-icon-lrc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aplayer-icon-lrc-inactivity {
|
||||
svg {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-icon-forward {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.aplayer-lrc-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aplayer-pic {
|
||||
position: relative;
|
||||
float: left;
|
||||
height: $aplayer-height;
|
||||
width: $aplayer-height;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover .aplayer-button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.aplayer-button {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
opacity: 0.8;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
transition: all 0.1s ease;
|
||||
|
||||
path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aplayer-play {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border: 2px solid #fff;
|
||||
bottom: 50%;
|
||||
right: 50%;
|
||||
margin: 0 -15px -15px 0;
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 4px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-pause {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #fff;
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-info {
|
||||
margin-left: $aplayer-height;
|
||||
padding: 14px 7px 0 10px;
|
||||
height: $aplayer-height;
|
||||
box-sizing: border-box;
|
||||
|
||||
.aplayer-music {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 0 13px 5px;
|
||||
user-select: text;
|
||||
cursor: default;
|
||||
padding-bottom: 2px;
|
||||
height: 20px;
|
||||
|
||||
.aplayer-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.aplayer-author {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-controller {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
.aplayer-bar-wrap {
|
||||
margin: 0 0 0 5px;
|
||||
padding: 4px 0;
|
||||
cursor: pointer !important;
|
||||
flex: 1;
|
||||
|
||||
&:hover {
|
||||
.aplayer-bar .aplayer-played .aplayer-thumb {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-bar {
|
||||
position: relative;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background: #cdcdcd;
|
||||
|
||||
.aplayer-loaded {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: #aaa;
|
||||
height: 2px;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.aplayer-played {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: 2px;
|
||||
|
||||
.aplayer-thumb {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 5px;
|
||||
margin-top: -4px;
|
||||
margin-right: -10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
transition: all .3s ease-in-out;
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-time {
|
||||
position: relative;
|
||||
right: 0;
|
||||
bottom: 4px;
|
||||
height: 17px;
|
||||
color: #999;
|
||||
font-size: 11px;
|
||||
padding-left: 7px;
|
||||
|
||||
.aplayer-time-inner {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.aplayer-icon {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
path {
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
&.aplayer-icon-loop {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
path {
|
||||
fill: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-icon-menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-time-narrow {
|
||||
.aplayer-icon-mode {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aplayer-icon-menu {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-volume-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-left: 3px;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover .aplayer-volume-bar-wrap {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.aplayer-volume-bar-wrap {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
right: -3px;
|
||||
width: 25px;
|
||||
height: 0;
|
||||
z-index: 99;
|
||||
overflow: hidden;
|
||||
transition: all .2s ease-in-out;
|
||||
|
||||
&.aplayer-volume-bar-wrap-active {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.aplayer-volume-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 10px;
|
||||
width: 5px;
|
||||
height: 35px;
|
||||
background: #aaa;
|
||||
border-radius: 2.5px;
|
||||
overflow: hidden;
|
||||
|
||||
.aplayer-volume {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 5px;
|
||||
transition: all 0.1s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-loading-icon {
|
||||
display: none;
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-lrc {
|
||||
display: none;
|
||||
position: relative;
|
||||
height: $lrc-height;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
margin: -10px 0 7px;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 10%;
|
||||
content: ' ';
|
||||
background: -moz-linear-gradient(top, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
|
||||
background: -webkit-linear-gradient(top, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
|
||||
background: linear-gradient(to bottom, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=0 );
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 33%;
|
||||
content: ' ';
|
||||
background: -moz-linear-gradient(top, rgba(255,255,255,0) 0%, rgba(255,255,255,0.8) 100%);
|
||||
background: -webkit-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%);
|
||||
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ccffffff',GradientType=0 );
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 16px !important;
|
||||
height: 16px !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
transition: all 0.5s ease-out;
|
||||
opacity: 0.4;
|
||||
overflow: hidden;
|
||||
|
||||
&.aplayer-lrc-current {
|
||||
opacity: 1;
|
||||
overflow: visible;
|
||||
height: initial !important;
|
||||
min-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&.aplayer-lrc-hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.aplayer-lrc-contents {
|
||||
width: 100%;
|
||||
transition: all 0.5s ease-out;
|
||||
user-select: text;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-list {
|
||||
overflow: auto;
|
||||
transition: all 0.5s ease;
|
||||
will-change: height;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
border-radius: 3px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 15px;
|
||||
font-size: 12px;
|
||||
border-top: 1px solid #e9e9e9;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
&.aplayer-list-light {
|
||||
background: #e9e9e9;
|
||||
|
||||
.aplayer-list-cur {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-list-cur {
|
||||
display: none;
|
||||
width: 3px;
|
||||
height: 22px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aplayer-list-index {
|
||||
color: #666;
|
||||
margin-right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aplayer-list-author {
|
||||
color: #666;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aplayer-notice {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
transition: all .3s ease-in-out;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
pointer-events: none;
|
||||
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 {
|
||||
0%{left:0}
|
||||
100%{left: -100%}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0)
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
27
aplayer/src/js/bar.js
Normal file
@@ -0,0 +1,27 @@
|
||||
class Bar {
|
||||
constructor(template) {
|
||||
this.elements = {};
|
||||
this.elements.volume = template.volume;
|
||||
this.elements.played = template.played;
|
||||
this.elements.loaded = template.loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update progress
|
||||
*
|
||||
* @param {String} type - Point out which bar it is
|
||||
* @param {Number} percentage
|
||||
* @param {String} direction - Point out the direction of this bar, Should be height or width
|
||||
*/
|
||||
set(type, percentage, direction) {
|
||||
percentage = Math.max(percentage, 0);
|
||||
percentage = Math.min(percentage, 1);
|
||||
this.elements[type].style[direction] = percentage * 100 + '%';
|
||||
}
|
||||
|
||||
get(type, direction) {
|
||||
return parseFloat(this.elements[type].style[direction]) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
export default Bar;
|
||||
164
aplayer/src/js/controller.js
Normal file
@@ -0,0 +1,164 @@
|
||||
import utils from './utils';
|
||||
import Icons from './icons';
|
||||
|
||||
class Controller {
|
||||
constructor(player) {
|
||||
this.player = player;
|
||||
|
||||
this.initPlayButton();
|
||||
this.initPlayBar();
|
||||
this.initOrderButton();
|
||||
this.initLoopButton();
|
||||
this.initMenuButton();
|
||||
if (!utils.isMobile) {
|
||||
this.initVolumeButton();
|
||||
}
|
||||
this.initMiniSwitcher();
|
||||
this.initSkipButton();
|
||||
this.initLrcButton();
|
||||
}
|
||||
|
||||
initPlayButton() {
|
||||
this.player.template.pic.addEventListener('click', () => {
|
||||
this.player.toggle();
|
||||
});
|
||||
}
|
||||
|
||||
initPlayBar() {
|
||||
const thumbMove = (e) => {
|
||||
let percentage = ((e.clientX || e.changedTouches[0].clientX) - this.player.template.barWrap.getBoundingClientRect().left) / this.player.template.barWrap.clientWidth;
|
||||
percentage = Math.max(percentage, 0);
|
||||
percentage = Math.min(percentage, 1);
|
||||
this.player.bar.set('played', percentage, 'width');
|
||||
this.player.lrc && this.player.lrc.update(percentage * this.player.duration);
|
||||
this.player.template.ptime.innerHTML = utils.secondToTime(percentage * this.player.duration);
|
||||
};
|
||||
|
||||
const thumbUp = (e) => {
|
||||
document.removeEventListener(utils.nameMap.dragEnd, thumbUp);
|
||||
document.removeEventListener(utils.nameMap.dragMove, thumbMove);
|
||||
let percentage = ((e.clientX || e.changedTouches[0].clientX) - this.player.template.barWrap.getBoundingClientRect().left) / this.player.template.barWrap.clientWidth;
|
||||
percentage = Math.max(percentage, 0);
|
||||
percentage = Math.min(percentage, 1);
|
||||
this.player.bar.set('played', percentage, 'width');
|
||||
this.player.seek(percentage * this.player.duration);
|
||||
this.player.disableTimeupdate = false;
|
||||
};
|
||||
|
||||
this.player.template.barWrap.addEventListener(utils.nameMap.dragStart, () => {
|
||||
this.player.disableTimeupdate = true;
|
||||
document.addEventListener(utils.nameMap.dragMove, thumbMove);
|
||||
document.addEventListener(utils.nameMap.dragEnd, thumbUp);
|
||||
});
|
||||
}
|
||||
|
||||
initVolumeButton() {
|
||||
this.player.template.volumeButton.addEventListener('click', () => {
|
||||
if (this.player.audio.muted) {
|
||||
this.player.volume(this.player.audio.volume, true);
|
||||
} else {
|
||||
this.player.audio.muted = true;
|
||||
this.player.switchVolumeIcon();
|
||||
this.player.bar.set('volume', 0, 'height');
|
||||
}
|
||||
});
|
||||
|
||||
const thumbMove = (e) => {
|
||||
let percentage = 1 - ((e.clientY || e.changedTouches[0].clientY) - this.player.template.volumeBar.getBoundingClientRect().top) / this.player.template.volumeBar.clientHeight;
|
||||
percentage = Math.max(percentage, 0);
|
||||
percentage = Math.min(percentage, 1);
|
||||
this.player.volume(percentage);
|
||||
};
|
||||
|
||||
const thumbUp = (e) => {
|
||||
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) - this.player.template.volumeBar.getBoundingClientRect().top) / this.player.template.volumeBar.clientHeight;
|
||||
percentage = Math.max(percentage, 0);
|
||||
percentage = Math.min(percentage, 1);
|
||||
this.player.volume(percentage);
|
||||
};
|
||||
|
||||
this.player.template.volumeBarWrap.addEventListener(utils.nameMap.dragStart, () => {
|
||||
this.player.template.volumeBarWrap.classList.add('aplayer-volume-bar-wrap-active');
|
||||
document.addEventListener(utils.nameMap.dragMove, thumbMove);
|
||||
document.addEventListener(utils.nameMap.dragEnd, thumbUp);
|
||||
});
|
||||
}
|
||||
|
||||
initOrderButton() {
|
||||
this.player.template.order.addEventListener('click', () => {
|
||||
if (this.player.options.order === 'list') {
|
||||
this.player.options.order = 'random';
|
||||
this.player.template.order.innerHTML = Icons.orderRandom;
|
||||
} else if (this.player.options.order === 'random') {
|
||||
this.player.options.order = 'list';
|
||||
this.player.template.order.innerHTML = Icons.orderList;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initLoopButton() {
|
||||
this.player.template.loop.addEventListener('click', () => {
|
||||
if (this.player.list.audios.length > 1) {
|
||||
if (this.player.options.loop === 'one') {
|
||||
this.player.options.loop = 'none';
|
||||
this.player.template.loop.innerHTML = Icons.loopNone;
|
||||
} else if (this.player.options.loop === 'none') {
|
||||
this.player.options.loop = 'all';
|
||||
this.player.template.loop.innerHTML = Icons.loopAll;
|
||||
} else if (this.player.options.loop === 'all') {
|
||||
this.player.options.loop = 'one';
|
||||
this.player.template.loop.innerHTML = Icons.loopOne;
|
||||
}
|
||||
} else {
|
||||
if (this.player.options.loop === 'one' || this.player.options.loop === 'all') {
|
||||
this.player.options.loop = 'none';
|
||||
this.player.template.loop.innerHTML = Icons.loopNone;
|
||||
} else if (this.player.options.loop === 'none') {
|
||||
this.player.options.loop = 'all';
|
||||
this.player.template.loop.innerHTML = Icons.loopAll;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initMenuButton() {
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
initLrcButton() {
|
||||
this.player.template.lrcButton.addEventListener('click', () => {
|
||||
if (this.player.template.lrcButton.classList.contains('aplayer-icon-lrc-inactivity')) {
|
||||
this.player.template.lrcButton.classList.remove('aplayer-icon-lrc-inactivity');
|
||||
this.player.lrc && this.player.lrc.show();
|
||||
} else {
|
||||
this.player.template.lrcButton.classList.add('aplayer-icon-lrc-inactivity');
|
||||
this.player.lrc && this.player.lrc.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Controller;
|
||||
62
aplayer/src/js/events.js
Normal file
@@ -0,0 +1,62 @@
|
||||
class Events {
|
||||
constructor() {
|
||||
this.events = {};
|
||||
|
||||
this.audioEvents = [
|
||||
'abort',
|
||||
'canplay',
|
||||
'canplaythrough',
|
||||
'durationchange',
|
||||
'emptied',
|
||||
'ended',
|
||||
'error',
|
||||
'loadeddata',
|
||||
'loadedmetadata',
|
||||
'loadstart',
|
||||
'mozaudioavailable',
|
||||
'pause',
|
||||
'play',
|
||||
'playing',
|
||||
'progress',
|
||||
'ratechange',
|
||||
'seeked',
|
||||
'seeking',
|
||||
'stalled',
|
||||
'suspend',
|
||||
'timeupdate',
|
||||
'volumechange',
|
||||
'waiting',
|
||||
];
|
||||
this.playerEvents = ['destroy', 'listshow', 'listhide', 'listadd', 'listremove', 'listswitch', 'listclear', 'noticeshow', 'noticehide', 'lrcshow', 'lrchide'];
|
||||
}
|
||||
|
||||
on(name, callback) {
|
||||
if (this.type(name) && typeof callback === 'function') {
|
||||
if (!this.events[name]) {
|
||||
this.events[name] = [];
|
||||
}
|
||||
this.events[name].push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
trigger(name, data) {
|
||||
if (this.events[name] && this.events[name].length) {
|
||||
for (let i = 0; i < this.events[name].length; i++) {
|
||||
this.events[name][i](data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type(name) {
|
||||
if (this.playerEvents.indexOf(name) !== -1) {
|
||||
return 'player';
|
||||
} else if (this.audioEvents.indexOf(name) !== -1) {
|
||||
return 'audio';
|
||||
}
|
||||
|
||||
console.error(`Unknown event name: ${name}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Events;
|
||||
35
aplayer/src/js/icons.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import play from '../assets/play.svg';
|
||||
import pause from '../assets/pause.svg';
|
||||
import volumeUp from '../assets/volume-up.svg';
|
||||
import volumeDown from '../assets/volume-down.svg';
|
||||
import volumeOff from '../assets/volume-off.svg';
|
||||
import orderRandom from '../assets/order-random.svg';
|
||||
import orderList from '../assets/order-list.svg';
|
||||
import menu from '../assets/menu.svg';
|
||||
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';
|
||||
import lrc from '../assets/lrc.svg';
|
||||
|
||||
const Icons = {
|
||||
play: play,
|
||||
pause: pause,
|
||||
volumeUp: volumeUp,
|
||||
volumeDown: volumeDown,
|
||||
volumeOff: volumeOff,
|
||||
orderRandom: orderRandom,
|
||||
orderList: orderList,
|
||||
menu: menu,
|
||||
loopAll: loopAll,
|
||||
loopOne: loopOne,
|
||||
loopNone: loopNone,
|
||||
loading: loading,
|
||||
right: right,
|
||||
skip: skip,
|
||||
lrc: lrc,
|
||||
};
|
||||
|
||||
export default Icons;
|
||||
7
aplayer/src/js/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import '../css/index.scss';
|
||||
import APlayer from './player';
|
||||
|
||||
/* global APLAYER_VERSION GIT_HASH */
|
||||
console.log(`${'\n'} %c APlayer v${APLAYER_VERSION} ${GIT_HASH} %c http://aplayer.js.org ${'\n'}`, 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
|
||||
|
||||
export default APlayer;
|
||||
195
aplayer/src/js/list.js
Normal file
@@ -0,0 +1,195 @@
|
||||
import tplListItem from '../template/list-item.art';
|
||||
import utils from './utils';
|
||||
import smoothScroll from 'smoothscroll';
|
||||
|
||||
class List {
|
||||
constructor(player) {
|
||||
this.player = player;
|
||||
this.index = 0;
|
||||
this.audios = this.player.options.audio;
|
||||
this.showing = true;
|
||||
this.player.template.list.style.height = `${Math.min(this.player.template.list.scrollHeight, this.player.options.listMaxHeight)}px`;
|
||||
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.player.template.list.addEventListener('click', (e) => {
|
||||
let target;
|
||||
if (e.target.tagName.toUpperCase() === 'LI') {
|
||||
target = e.target;
|
||||
} else {
|
||||
target = e.target.parentElement;
|
||||
}
|
||||
const audioIndex = parseInt(target.getElementsByClassName('aplayer-list-index')[0].innerHTML) - 1;
|
||||
if (audioIndex !== this.index) {
|
||||
this.switch(audioIndex);
|
||||
this.player.play();
|
||||
} else {
|
||||
this.player.toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
this.showing = true;
|
||||
this.player.template.list.scrollTop = this.index * 33;
|
||||
this.player.template.list.style.height = `${Math.min(this.player.template.list.scrollHeight, this.player.options.listMaxHeight)}px`;
|
||||
this.player.events.trigger('listshow');
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.showing = false;
|
||||
this.player.template.list.style.height = `${Math.min(this.player.template.list.scrollHeight, this.player.options.listMaxHeight)}px`;
|
||||
setTimeout(() => {
|
||||
this.player.template.list.style.height = '0px';
|
||||
this.player.events.trigger('listhide');
|
||||
}, 0);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this.showing) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
add(audios) {
|
||||
this.player.events.trigger('listadd', {
|
||||
audios: audios,
|
||||
});
|
||||
|
||||
if (Object.prototype.toString.call(audios) !== '[object Array]') {
|
||||
audios = [audios];
|
||||
}
|
||||
audios.map((item) => {
|
||||
item.name = item.name || item.title || 'Audio name';
|
||||
item.artist = item.artist || item.author || 'Audio artist';
|
||||
item.cover = item.cover || item.pic;
|
||||
item.type = item.type || 'normal';
|
||||
return item;
|
||||
});
|
||||
|
||||
const wasSingle = !(this.audios.length > 1);
|
||||
const wasEmpty = this.audios.length === 0;
|
||||
|
||||
this.player.template.list.innerHTML += tplListItem({
|
||||
theme: this.player.options.theme,
|
||||
audio: audios,
|
||||
index: this.audios.length + 1,
|
||||
});
|
||||
|
||||
this.audios = this.audios.concat(audios);
|
||||
|
||||
if (wasSingle && this.audios.length > 1) {
|
||||
this.player.container.classList.add('aplayer-withlist');
|
||||
}
|
||||
|
||||
this.player.randomOrder = utils.randomOrder(this.audios.length);
|
||||
this.player.template.listCurs = this.player.container.querySelectorAll('.aplayer-list-cur');
|
||||
|
||||
this.player.template.listCurs[this.audios.length - 1].style.backgroundColor = audios.theme || this.player.options.theme;
|
||||
|
||||
if (wasEmpty) {
|
||||
if (this.player.options.order === 'random') {
|
||||
this.switch(this.player.randomOrder[0]);
|
||||
} else {
|
||||
this.switch(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove(index) {
|
||||
this.player.events.trigger('listremove', {
|
||||
index: index,
|
||||
});
|
||||
if (this.audios[index]) {
|
||||
if (this.audios.length > 1) {
|
||||
const list = this.player.container.querySelectorAll('.aplayer-list li');
|
||||
list[index].remove();
|
||||
|
||||
this.audios.splice(index, 1);
|
||||
this.player.lrc && this.player.lrc.remove(index);
|
||||
|
||||
if (index === this.index) {
|
||||
if (this.audios[index]) {
|
||||
this.switch(index);
|
||||
} else {
|
||||
this.switch(index - 1);
|
||||
}
|
||||
}
|
||||
if (this.index > index) {
|
||||
this.index--;
|
||||
}
|
||||
|
||||
for (let i = index; i < list.length; i++) {
|
||||
list[i].getElementsByClassName('aplayer-list-index')[0].textContent = i;
|
||||
}
|
||||
if (this.audios.length === 1) {
|
||||
this.player.container.classList.remove('aplayer-withlist');
|
||||
}
|
||||
|
||||
this.player.template.listCurs = this.player.container.querySelectorAll('.aplayer-list-cur');
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(index) {
|
||||
this.player.events.trigger('listswitch', {
|
||||
index: index,
|
||||
});
|
||||
|
||||
if (typeof index !== 'undefined' && this.audios[index]) {
|
||||
this.index = index;
|
||||
|
||||
const audio = this.audios[this.index];
|
||||
|
||||
// set html
|
||||
this.player.template.pic.style.backgroundImage = audio.cover ? `url('${audio.cover}')` : '';
|
||||
this.player.theme(this.audios[this.index].theme || this.player.options.theme, this.index, false);
|
||||
this.player.template.title.innerHTML = audio.name;
|
||||
this.player.template.author.innerHTML = audio.artist ? ' - ' + audio.artist : '';
|
||||
|
||||
const light = this.player.container.getElementsByClassName('aplayer-list-light')[0];
|
||||
if (light) {
|
||||
light.classList.remove('aplayer-list-light');
|
||||
}
|
||||
this.player.container.querySelectorAll('.aplayer-list li')[this.index].classList.add('aplayer-list-light');
|
||||
|
||||
smoothScroll(this.index * 33, 500, null, this.player.template.list);
|
||||
|
||||
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
|
||||
this.player.template.dtime.innerHTML = utils.secondToTime(this.player.duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.player.events.trigger('listclear');
|
||||
this.index = 0;
|
||||
this.player.container.classList.remove('aplayer-withlist');
|
||||
this.player.pause();
|
||||
this.audios = [];
|
||||
this.player.lrc && this.player.lrc.clear();
|
||||
this.player.audio.src = '';
|
||||
this.player.template.list.innerHTML = '';
|
||||
this.player.template.pic.style.backgroundImage = '';
|
||||
this.player.theme(this.player.options.theme, this.index, false);
|
||||
this.player.template.title.innerHTML = 'No audio';
|
||||
this.player.template.author.innerHTML = '';
|
||||
this.player.bar.set('loaded', 0, 'width');
|
||||
this.player.template.dtime.innerHTML = utils.secondToTime(0);
|
||||
}
|
||||
}
|
||||
|
||||
export default List;
|
||||
143
aplayer/src/js/lrc.js
Normal file
@@ -0,0 +1,143 @@
|
||||
import tplLrc from '../template/lrc.art';
|
||||
|
||||
class Lrc {
|
||||
constructor(options) {
|
||||
this.container = options.container;
|
||||
this.async = options.async;
|
||||
this.player = options.player;
|
||||
this.parsed = [];
|
||||
this.index = 0;
|
||||
this.current = [];
|
||||
}
|
||||
|
||||
show() {
|
||||
this.player.events.trigger('lrcshow');
|
||||
this.player.template.lrcWrap.classList.remove('aplayer-lrc-hide');
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.player.events.trigger('lrchide');
|
||||
this.player.template.lrcWrap.classList.add('aplayer-lrc-hide');
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this.player.template.lrcWrap.classList.contains('aplayer-lrc-hide')) {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
update(currentTime = this.player.audio.currentTime) {
|
||||
if (this.index > this.current.length - 1 || currentTime < this.current[this.index][0] || (!this.current[this.index + 1] || currentTime >= this.current[this.index + 1][0])) {
|
||||
for (let i = 0; i < this.current.length; i++) {
|
||||
if (currentTime >= this.current[i][0] && (!this.current[i + 1] || currentTime < this.current[i + 1][0])) {
|
||||
this.index = i;
|
||||
this.container.style.transform = `translateY(${-this.index * 16}px)`;
|
||||
this.container.style.webkitTransform = `translateY(${-this.index * 16}px)`;
|
||||
this.container.getElementsByClassName('aplayer-lrc-current')[0].classList.remove('aplayer-lrc-current');
|
||||
this.container.getElementsByTagName('p')[i].classList.add('aplayer-lrc-current');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(index) {
|
||||
if (!this.parsed[index]) {
|
||||
if (!this.async) {
|
||||
if (this.player.list.audios[index].lrc) {
|
||||
this.parsed[index] = this.parse(this.player.list.audios[index].lrc);
|
||||
} else {
|
||||
this.parsed[index] = [['00:00', 'Not available']];
|
||||
}
|
||||
} else {
|
||||
this.parsed[index] = [['00:00', 'Loading']];
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = () => {
|
||||
if (index === this.player.list.index && xhr.readyState === 4) {
|
||||
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
|
||||
this.parsed[index] = this.parse(xhr.responseText);
|
||||
} else {
|
||||
this.player.notice(`LRC file request fails: status ${xhr.status}`);
|
||||
this.parsed[index] = [['00:00', 'Not available']];
|
||||
}
|
||||
this.container.innerHTML = tplLrc({
|
||||
lyrics: this.parsed[index],
|
||||
});
|
||||
this.update(0);
|
||||
this.current = this.parsed[index];
|
||||
}
|
||||
};
|
||||
const apiurl = this.player.list.audios[index].lrc;
|
||||
xhr.open('get', apiurl, true);
|
||||
xhr.send(null);
|
||||
}
|
||||
}
|
||||
|
||||
this.container.innerHTML = tplLrc({
|
||||
lyrics: this.parsed[index],
|
||||
});
|
||||
this.current = this.parsed[index];
|
||||
this.update(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse lrc, suppose multiple time tag
|
||||
*
|
||||
* @param {String} lrc_s - Format:
|
||||
* [mm:ss]lyric
|
||||
* [mm:ss.xx]lyric
|
||||
* [mm:ss.xxx]lyric
|
||||
* [mm:ss.xx][mm:ss.xx][mm:ss.xx]lyric
|
||||
* [mm:ss.xx]<mm:ss.xx>lyric
|
||||
*
|
||||
* @return {String} [[time, text], [time, text], [time, text], ...]
|
||||
*/
|
||||
parse(lrc_s) {
|
||||
if (lrc_s) {
|
||||
lrc_s = lrc_s.replace(/([^\]^\n])\[/g, (match, p1) => p1 + '\n[');
|
||||
const lyric = lrc_s.split('\n');
|
||||
let lrc = [];
|
||||
const lyricLen = lyric.length;
|
||||
for (let i = 0; i < lyricLen; i++) {
|
||||
// match lrc time
|
||||
const lrcTimes = lyric[i].match(/\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g);
|
||||
// match lrc text
|
||||
const lrcText = lyric[i]
|
||||
.replace(/.*\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/g, '')
|
||||
.replace(/<(\d{2}):(\d{2})(\.(\d{2,3}))?>/g, '')
|
||||
.replace(/^\s+|\s+$/g, '');
|
||||
|
||||
if (lrcTimes) {
|
||||
// handle multiple time tag
|
||||
const timeLen = lrcTimes.length;
|
||||
for (let j = 0; j < timeLen; j++) {
|
||||
const oneTime = /\[(\d{2}):(\d{2})(\.(\d{2,3}))?]/.exec(lrcTimes[j]);
|
||||
const min2sec = oneTime[1] * 60;
|
||||
const sec2sec = parseInt(oneTime[2]);
|
||||
const msec2sec = oneTime[4] ? parseInt(oneTime[4]) / ((oneTime[4] + '').length === 2 ? 100 : 1000) : 0;
|
||||
const lrcTime = min2sec + sec2sec + msec2sec;
|
||||
lrc.push([lrcTime, lrcText]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort by time
|
||||
lrc = lrc.filter((item) => item[1]);
|
||||
lrc.sort((a, b) => a[0] - b[0]);
|
||||
return lrc;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
remove(index) {
|
||||
this.parsed.splice(index, 1);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.parsed = [];
|
||||
this.container.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
export default Lrc;
|
||||
45
aplayer/src/js/options.js
Normal file
@@ -0,0 +1,45 @@
|
||||
export default (options) => {
|
||||
// default options
|
||||
const defaultOption = {
|
||||
container: options.element || document.getElementsByClassName('aplayer')[0],
|
||||
mini: options.narrow || options.fixed || false,
|
||||
fixed: false,
|
||||
autoplay: false,
|
||||
mutex: true,
|
||||
lrcType: options.showlrc || options.lrc || 0,
|
||||
preload: 'metadata',
|
||||
theme: '#b7daff',
|
||||
loop: 'all',
|
||||
order: 'list',
|
||||
volume: 0.7,
|
||||
listFolded: options.fixed,
|
||||
listMaxHeight: options.listmaxheight || 250,
|
||||
audio: options.music || [],
|
||||
storageName: 'aplayer-setting',
|
||||
};
|
||||
for (const defaultKey in defaultOption) {
|
||||
if (defaultOption.hasOwnProperty(defaultKey) && !options.hasOwnProperty(defaultKey)) {
|
||||
options[defaultKey] = defaultOption[defaultKey];
|
||||
}
|
||||
}
|
||||
|
||||
options.listMaxHeight = parseFloat(options.listMaxHeight);
|
||||
|
||||
if (Object.prototype.toString.call(options.audio) !== '[object Array]') {
|
||||
options.audio = [options.audio];
|
||||
}
|
||||
|
||||
options.audio.map((item) => {
|
||||
item.name = item.name || item.title || 'Audio name';
|
||||
item.artist = item.artist || item.author || 'Audio artist';
|
||||
item.cover = item.cover || item.pic;
|
||||
item.type = item.type || 'normal';
|
||||
return item;
|
||||
});
|
||||
|
||||
if (options.audio.length <= 1 && options.loop === 'one') {
|
||||
options.loop = 'all';
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
491
aplayer/src/js/player.js
Normal file
@@ -0,0 +1,491 @@
|
||||
import Promise from 'promise-polyfill';
|
||||
|
||||
import utils from './utils';
|
||||
import Icons from './icons';
|
||||
import handleOption from './options';
|
||||
import Template from './template';
|
||||
import Bar from './bar';
|
||||
import Storage from './storage';
|
||||
import Lrc from './lrc';
|
||||
import Controller from './controller';
|
||||
import Timer from './timer';
|
||||
import Events from './events';
|
||||
import List from './list';
|
||||
|
||||
const instances = [];
|
||||
|
||||
class APlayer {
|
||||
/**
|
||||
* APlayer constructor function
|
||||
*
|
||||
* @param {Object} options - See README
|
||||
* @constructor
|
||||
*/
|
||||
constructor(options) {
|
||||
this.options = handleOption(options);
|
||||
this.container = this.options.container;
|
||||
this.paused = true;
|
||||
this.playedPromise = Promise.resolve();
|
||||
this.mode = 'normal';
|
||||
|
||||
this.randomOrder = utils.randomOrder(this.options.audio.length);
|
||||
|
||||
this.container.classList.add('aplayer');
|
||||
if (this.options.lrcType && !this.options.fixed) {
|
||||
this.container.classList.add('aplayer-withlrc');
|
||||
}
|
||||
if (this.options.audio.length > 1) {
|
||||
this.container.classList.add('aplayer-withlist');
|
||||
}
|
||||
if (utils.isMobile) {
|
||||
this.container.classList.add('aplayer-mobile');
|
||||
}
|
||||
this.arrow = this.container.offsetWidth <= 300;
|
||||
if (this.arrow) {
|
||||
this.container.classList.add('aplayer-arrow');
|
||||
}
|
||||
|
||||
// save lrc
|
||||
if (this.options.lrcType === 2 || this.options.lrcType === true) {
|
||||
const lrcEle = this.container.getElementsByClassName('aplayer-lrc-content');
|
||||
for (let i = 0; i < lrcEle.length; i++) {
|
||||
if (this.options.audio[i]) {
|
||||
this.options.audio[i].lrc = lrcEle[i].innerHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.template = new Template({
|
||||
container: this.container,
|
||||
options: this.options,
|
||||
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');
|
||||
}
|
||||
|
||||
if (this.options.lrcType) {
|
||||
this.lrc = new Lrc({
|
||||
container: this.template.lrc,
|
||||
async: this.options.lrcType === 3,
|
||||
player: this,
|
||||
});
|
||||
}
|
||||
this.events = new Events();
|
||||
this.storage = new Storage(this);
|
||||
this.bar = new Bar(this.template);
|
||||
this.controller = new Controller(this);
|
||||
this.timer = new Timer(this);
|
||||
this.list = new List(this);
|
||||
|
||||
this.initAudio();
|
||||
this.bindEvents();
|
||||
if (this.options.order === 'random') {
|
||||
this.list.switch(this.randomOrder[0]);
|
||||
} else {
|
||||
this.list.switch(0);
|
||||
}
|
||||
|
||||
// autoplay
|
||||
if (this.options.autoplay) {
|
||||
this.play();
|
||||
}
|
||||
|
||||
instances.push(this);
|
||||
}
|
||||
|
||||
initAudio() {
|
||||
this.audio = document.createElement('audio');
|
||||
this.audio.preload = this.options.preload;
|
||||
|
||||
for (let i = 0; i < this.events.audioEvents.length; i++) {
|
||||
this.audio.addEventListener(this.events.audioEvents[i], (e) => {
|
||||
this.events.trigger(this.events.audioEvents[i], e);
|
||||
});
|
||||
}
|
||||
|
||||
this.volume(this.storage.get('volume'), true);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.on('play', () => {
|
||||
if (this.paused) {
|
||||
this.setUIPlaying();
|
||||
}
|
||||
});
|
||||
|
||||
this.on('pause', () => {
|
||||
if (!this.paused) {
|
||||
this.setUIPaused();
|
||||
}
|
||||
});
|
||||
|
||||
this.on('timeupdate', () => {
|
||||
if (!this.disableTimeupdate) {
|
||||
this.bar.set('played', this.audio.currentTime / this.duration, 'width');
|
||||
this.lrc && this.lrc.update();
|
||||
const currentTime = utils.secondToTime(this.audio.currentTime);
|
||||
if (this.template.ptime.innerHTML !== currentTime) {
|
||||
this.template.ptime.innerHTML = currentTime;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// show audio time: the metadata has loaded or changed
|
||||
this.on('durationchange', () => {
|
||||
if (this.duration !== 1) {
|
||||
// compatibility: Android browsers will output 1 at first
|
||||
this.template.dtime.innerHTML = utils.secondToTime(this.duration);
|
||||
}
|
||||
});
|
||||
|
||||
// Can seek now
|
||||
this.on('loadedmetadata', () => {
|
||||
this.seek(0);
|
||||
if (!this.paused) {
|
||||
this.audio.play();
|
||||
}
|
||||
});
|
||||
|
||||
// show audio loaded bar: to inform interested parties of progress downloading the media
|
||||
this.on('canplay', () => {
|
||||
const percentage = this.audio.buffered.length ? this.audio.buffered.end(this.audio.buffered.length - 1) / this.duration : 0;
|
||||
this.bar.set('loaded', percentage, 'width');
|
||||
});
|
||||
this.on('progress', () => {
|
||||
const percentage = this.audio.buffered.length ? this.audio.buffered.end(this.audio.buffered.length - 1) / this.duration : 0;
|
||||
this.bar.set('loaded', percentage, 'width');
|
||||
});
|
||||
|
||||
// audio download error: an error occurs
|
||||
let skipTime;
|
||||
this.on('error', () => {
|
||||
if (this.list.audios.length > 1) {
|
||||
this.notice('An audio error has occurred, player will skip forward in 2 seconds.');
|
||||
skipTime = setTimeout(() => {
|
||||
this.skipForward();
|
||||
if (!this.paused) {
|
||||
this.play();
|
||||
}
|
||||
}, 2000);
|
||||
} else if (this.list.audios.length === 1) {
|
||||
this.notice('An audio error has occurred.');
|
||||
}
|
||||
});
|
||||
this.events.on('listswitch', () => {
|
||||
skipTime && clearTimeout(skipTime);
|
||||
});
|
||||
|
||||
// multiple audio play
|
||||
this.on('ended', () => {
|
||||
if (this.options.loop === 'none') {
|
||||
if (this.options.order === 'list') {
|
||||
if (this.list.index < this.list.audios.length - 1) {
|
||||
this.list.switch((this.list.index + 1) % this.list.audios.length);
|
||||
this.play();
|
||||
} else {
|
||||
this.list.switch((this.list.index + 1) % this.list.audios.length);
|
||||
this.pause();
|
||||
}
|
||||
} else if (this.options.order === 'random') {
|
||||
if (this.randomOrder.indexOf(this.list.index) < this.randomOrder.length - 1) {
|
||||
this.list.switch(this.nextIndex());
|
||||
this.play();
|
||||
} else {
|
||||
this.list.switch(this.nextIndex());
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
} else if (this.options.loop === 'one') {
|
||||
this.list.switch(this.list.index);
|
||||
this.play();
|
||||
} else if (this.options.loop === 'all') {
|
||||
this.skipForward();
|
||||
this.play();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setAudio(audio) {
|
||||
if (this.hls) {
|
||||
this.hls.destroy();
|
||||
this.hls = null;
|
||||
}
|
||||
let type = audio.type;
|
||||
if (this.options.customAudioType && this.options.customAudioType[type]) {
|
||||
if (Object.prototype.toString.call(this.options.customAudioType[type]) === '[object Function]') {
|
||||
this.options.customAudioType[type](this.audio, audio, this);
|
||||
} else {
|
||||
console.error(`Illegal customType: ${type}`);
|
||||
}
|
||||
} else {
|
||||
if (!type || type === 'auto') {
|
||||
if (/m3u8(#|\?|$)/i.exec(audio.url)) {
|
||||
type = 'hls';
|
||||
} else {
|
||||
type = 'normal';
|
||||
}
|
||||
}
|
||||
if (type === 'hls') {
|
||||
if (window.Hls.isSupported()) {
|
||||
this.hls = new window.Hls();
|
||||
this.hls.loadSource(audio.url);
|
||||
this.hls.attachMedia(this.audio);
|
||||
} else if (this.audio.canPlayType('application/x-mpegURL') || this.audio.canPlayType('application/vnd.apple.mpegURL')) {
|
||||
this.audio.src = audio.url;
|
||||
} else {
|
||||
this.notice('Error: HLS is not supported.');
|
||||
}
|
||||
} else if (type === 'normal') {
|
||||
this.audio.src = audio.url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
theme(color = this.list.audios[this.list.index].theme || this.options.theme, index = this.list.index, isReset = true) {
|
||||
if (isReset) {
|
||||
this.list.audios[index] && (this.list.audios[index].theme = color);
|
||||
}
|
||||
this.template.listCurs[index] && (this.template.listCurs[index].style.backgroundColor = color);
|
||||
if (index === this.list.index) {
|
||||
this.template.pic.style.backgroundColor = color;
|
||||
this.template.played.style.background = color;
|
||||
this.template.thumb.style.background = color;
|
||||
this.template.volume.style.background = color;
|
||||
}
|
||||
}
|
||||
|
||||
seek(time) {
|
||||
time = Math.max(time, 0);
|
||||
time = Math.min(time, this.duration);
|
||||
this.audio.currentTime = time;
|
||||
this.bar.set('played', time / this.duration, 'width');
|
||||
this.template.ptime.innerHTML = utils.secondToTime(time);
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return isNaN(this.audio.duration) ? 0 : this.audio.duration;
|
||||
}
|
||||
|
||||
setUIPlaying() {
|
||||
if (this.paused) {
|
||||
this.paused = false;
|
||||
this.template.button.classList.remove('aplayer-play');
|
||||
this.template.button.classList.add('aplayer-pause');
|
||||
this.template.button.innerHTML = '';
|
||||
setTimeout(() => {
|
||||
this.template.button.innerHTML = Icons.pause;
|
||||
}, 100);
|
||||
this.template.skipPlayButton.innerHTML = Icons.pause;
|
||||
}
|
||||
|
||||
this.timer.enable('loading');
|
||||
|
||||
if (this.options.mutex) {
|
||||
for (let i = 0; i < instances.length; i++) {
|
||||
if (this !== instances[i]) {
|
||||
instances[i].pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
play() {
|
||||
this.setUIPlaying();
|
||||
|
||||
const playPromise = this.audio.play();
|
||||
if (playPromise) {
|
||||
playPromise.catch((e) => {
|
||||
console.warn(e);
|
||||
if (e.name === 'NotAllowedError') {
|
||||
this.setUIPaused();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setUIPaused() {
|
||||
if (!this.paused) {
|
||||
this.paused = true;
|
||||
|
||||
this.template.button.classList.remove('aplayer-pause');
|
||||
this.template.button.classList.add('aplayer-play');
|
||||
this.template.button.innerHTML = '';
|
||||
setTimeout(() => {
|
||||
this.template.button.innerHTML = Icons.play;
|
||||
}, 100);
|
||||
this.template.skipPlayButton.innerHTML = Icons.play;
|
||||
}
|
||||
|
||||
this.container.classList.remove('aplayer-loading');
|
||||
this.timer.disable('loading');
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.setUIPaused();
|
||||
this.audio.pause();
|
||||
}
|
||||
|
||||
switchVolumeIcon() {
|
||||
if (this.volume() >= 0.95) {
|
||||
this.template.volumeButton.innerHTML = Icons.volumeUp;
|
||||
} else if (this.volume() > 0) {
|
||||
this.template.volumeButton.innerHTML = Icons.volumeDown;
|
||||
} else {
|
||||
this.template.volumeButton.innerHTML = Icons.volumeOff;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set volume
|
||||
*/
|
||||
volume(percentage, nostorage) {
|
||||
percentage = parseFloat(percentage);
|
||||
if (!isNaN(percentage)) {
|
||||
percentage = Math.max(percentage, 0);
|
||||
percentage = Math.min(percentage, 1);
|
||||
this.bar.set('volume', percentage, 'height');
|
||||
if (!nostorage) {
|
||||
this.storage.set('volume', percentage);
|
||||
}
|
||||
|
||||
this.audio.volume = percentage;
|
||||
if (this.audio.muted) {
|
||||
this.audio.muted = false;
|
||||
}
|
||||
|
||||
this.switchVolumeIcon();
|
||||
}
|
||||
|
||||
return this.audio.muted ? 0 : this.audio.volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* bind events
|
||||
*/
|
||||
on(name, callback) {
|
||||
this.events.on(name, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle between play and pause
|
||||
*/
|
||||
toggle() {
|
||||
if (this.template.button.classList.contains('aplayer-play')) {
|
||||
this.play();
|
||||
} else if (this.template.button.classList.contains('aplayer-pause')) {
|
||||
this.pause();
|
||||
}
|
||||
}
|
||||
|
||||
// abandoned
|
||||
switchAudio(index) {
|
||||
this.list.switch(index);
|
||||
}
|
||||
|
||||
// abandoned
|
||||
addAudio(audios) {
|
||||
this.list.add(audios);
|
||||
}
|
||||
|
||||
// abandoned
|
||||
removeAudio(index) {
|
||||
this.list.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy this player
|
||||
*/
|
||||
destroy() {
|
||||
instances.splice(instances.indexOf(this), 1);
|
||||
this.pause();
|
||||
this.container.innerHTML = '';
|
||||
this.audio.src = '';
|
||||
this.timer.destroy();
|
||||
this.events.trigger('destroy');
|
||||
}
|
||||
|
||||
setMode(mode = 'normal') {
|
||||
this.mode = mode;
|
||||
if (mode === 'mini') {
|
||||
this.container.classList.add('aplayer-narrow');
|
||||
} else if (mode === 'normal') {
|
||||
this.container.classList.remove('aplayer-narrow');
|
||||
}
|
||||
}
|
||||
|
||||
notice(text, time = 2000, opacity = 0.8) {
|
||||
this.template.notice.innerHTML = text;
|
||||
this.template.notice.style.opacity = opacity;
|
||||
if (this.noticeTime) {
|
||||
clearTimeout(this.noticeTime);
|
||||
}
|
||||
this.events.trigger('noticeshow', {
|
||||
text: text,
|
||||
});
|
||||
if (time) {
|
||||
this.noticeTime = setTimeout(() => {
|
||||
this.template.notice.style.opacity = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export default APlayer;
|
||||
24
aplayer/src/js/storage.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import utils from './utils';
|
||||
|
||||
class Storage {
|
||||
constructor(player) {
|
||||
this.storageName = player.options.storageName;
|
||||
|
||||
this.data = JSON.parse(utils.storage.get(this.storageName));
|
||||
if (!this.data) {
|
||||
this.data = {};
|
||||
}
|
||||
this.data.volume = this.data.volume || player.options.volume;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
return this.data[key];
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
this.data[key] = value;
|
||||
utils.storage.set(this.storageName, JSON.stringify(this.data));
|
||||
}
|
||||
}
|
||||
|
||||
export default Storage;
|
||||
62
aplayer/src/js/template.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import Icons from './icons';
|
||||
import tplPlayer from '../template/player.art';
|
||||
|
||||
class Template {
|
||||
constructor(options) {
|
||||
this.container = options.container;
|
||||
this.options = options.options;
|
||||
this.randomOrder = options.randomOrder;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
let cover = '';
|
||||
if (this.options.audio.length) {
|
||||
if (this.options.order === 'random') {
|
||||
cover = this.options.audio[this.randomOrder[0]].cover;
|
||||
} else {
|
||||
cover = this.options.audio[0].cover;
|
||||
}
|
||||
}
|
||||
|
||||
this.container.innerHTML = tplPlayer({
|
||||
options: this.options,
|
||||
icons: Icons,
|
||||
cover: cover,
|
||||
getObject: (obj) => obj,
|
||||
});
|
||||
|
||||
this.lrc = this.container.querySelector('.aplayer-lrc-contents');
|
||||
this.lrcWrap = this.container.querySelector('.aplayer-lrc');
|
||||
this.ptime = this.container.querySelector('.aplayer-ptime');
|
||||
this.info = this.container.querySelector('.aplayer-info');
|
||||
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.listCurs = this.container.querySelectorAll('.aplayer-list-cur');
|
||||
this.played = this.container.querySelector('.aplayer-played');
|
||||
this.loaded = this.container.querySelector('.aplayer-loaded');
|
||||
this.thumb = this.container.querySelector('.aplayer-thumb');
|
||||
this.volume = this.container.querySelector('.aplayer-volume');
|
||||
this.volumeBar = this.container.querySelector('.aplayer-volume-bar');
|
||||
this.volumeButton = this.container.querySelector('.aplayer-time button');
|
||||
this.volumeBarWrap = this.container.querySelector('.aplayer-volume-bar-wrap');
|
||||
this.loop = this.container.querySelector('.aplayer-icon-loop');
|
||||
this.order = this.container.querySelector('.aplayer-icon-order');
|
||||
this.menu = this.container.querySelector('.aplayer-icon-menu');
|
||||
this.pic = this.container.querySelector('.aplayer-pic');
|
||||
this.title = this.container.querySelector('.aplayer-title');
|
||||
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');
|
||||
this.lrcButton = this.container.querySelector('.aplayer-icon-lrc');
|
||||
}
|
||||
}
|
||||
|
||||
export default Template;
|
||||
67
aplayer/src/js/timer.js
Normal file
@@ -0,0 +1,67 @@
|
||||
class Timer {
|
||||
constructor(player) {
|
||||
this.player = player;
|
||||
|
||||
window.requestAnimationFrame = (() =>
|
||||
window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
window.oRequestAnimationFrame ||
|
||||
window.msRequestAnimationFrame ||
|
||||
function(callback) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
})();
|
||||
|
||||
this.types = ['loading'];
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.types.forEach((item) => {
|
||||
this[`init${item}Checker`]();
|
||||
});
|
||||
}
|
||||
|
||||
initloadingChecker() {
|
||||
let lastPlayPos = 0;
|
||||
let currentPlayPos = 0;
|
||||
let bufferingDetected = false;
|
||||
this.loadingChecker = setInterval(() => {
|
||||
if (this.enableloadingChecker) {
|
||||
// whether the audio is buffering
|
||||
currentPlayPos = this.player.audio.currentTime;
|
||||
if (!bufferingDetected && currentPlayPos === lastPlayPos && !this.player.audio.paused) {
|
||||
this.player.container.classList.add('aplayer-loading');
|
||||
bufferingDetected = true;
|
||||
}
|
||||
if (bufferingDetected && currentPlayPos > lastPlayPos && !this.player.audio.paused) {
|
||||
this.player.container.classList.remove('aplayer-loading');
|
||||
bufferingDetected = false;
|
||||
}
|
||||
lastPlayPos = currentPlayPos;
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
enable(type) {
|
||||
this[`enable${type}Checker`] = true;
|
||||
|
||||
if (type === 'fps') {
|
||||
this.initfpsChecker();
|
||||
}
|
||||
}
|
||||
|
||||
disable(type) {
|
||||
this[`enable${type}Checker`] = false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.types.forEach((item) => {
|
||||
this[`enable${item}Checker`] = false;
|
||||
this[`${item}Checker`] && clearInterval(this[`${item}Checker`]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Timer;
|
||||
55
aplayer/src/js/utils.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const isMobile = /mobile/i.test(window.navigator.userAgent);
|
||||
|
||||
const utils = {
|
||||
/**
|
||||
* Parse second to time string
|
||||
*
|
||||
* @param {Number} second
|
||||
* @return {String} 00:00 or 00:00:00
|
||||
*/
|
||||
secondToTime: (second) => {
|
||||
const add0 = (num) => (num < 10 ? '0' + num : '' + num);
|
||||
const hour = Math.floor(second / 3600);
|
||||
const min = Math.floor((second - hour * 3600) / 60);
|
||||
const sec = Math.floor(second - hour * 3600 - min * 60);
|
||||
return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(':');
|
||||
},
|
||||
|
||||
isMobile: isMobile,
|
||||
|
||||
storage: {
|
||||
set: (key, value) => {
|
||||
localStorage.setItem(key, value);
|
||||
},
|
||||
|
||||
get: (key) => localStorage.getItem(key),
|
||||
},
|
||||
|
||||
nameMap: {
|
||||
dragStart: isMobile ? 'touchstart' : 'mousedown',
|
||||
dragMove: isMobile ? 'touchmove' : 'mousemove',
|
||||
dragEnd: isMobile ? 'touchend' : 'mouseup',
|
||||
},
|
||||
|
||||
/**
|
||||
* get random order, using Fisher–Yates shuffle
|
||||
*/
|
||||
randomOrder: (length) => {
|
||||
function shuffle(arr) {
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
const randomIndex = Math.floor(Math.random() * (i + 1));
|
||||
const itemAtIndex = arr[randomIndex];
|
||||
arr[randomIndex] = arr[i];
|
||||
arr[i] = itemAtIndex;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
return shuffle(
|
||||
[...Array(length)].map(function(item, i) {
|
||||
return i;
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default utils;
|
||||
8
aplayer/src/template/list-item.art
Normal file
@@ -0,0 +1,8 @@
|
||||
{{each audio}}
|
||||
<li>
|
||||
<span class="aplayer-list-cur" style="background-color: {{ $value.theme || theme }};"></span>
|
||||
<span class="aplayer-list-index">{{ $index + index }}</span>
|
||||
<span class="aplayer-list-title">{{ $value.name }}</span>
|
||||
<span class="aplayer-list-author">{{ $value.artist }}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
3
aplayer/src/template/lrc.art
Normal file
@@ -0,0 +1,3 @@
|
||||
{{each lyrics}}
|
||||
<p{{ if $index === 0 }} class="aplayer-lrc-current"{{ /if }}>{{$value[1]}}</p>
|
||||
{{/each}}
|
||||
145
aplayer/src/template/player.art
Normal file
@@ -0,0 +1,145 @@
|
||||
{{ if !options.fixed }}
|
||||
<div class="aplayer-body">
|
||||
<div class="aplayer-pic" style="{{ if cover }}background-image: url("{{ cover }}");{{ /if }}background-color: {{ options.theme }};">
|
||||
<div class="aplayer-button aplayer-play">{{@ icons.play }}</div>
|
||||
</div>
|
||||
<div class="aplayer-info">
|
||||
<div class="aplayer-music">
|
||||
<span class="aplayer-title">No audio</span>
|
||||
<span class="aplayer-author"></span>
|
||||
</div>
|
||||
<div class="aplayer-lrc">
|
||||
<div class="aplayer-lrc-contents" style="transform: translateY(0); -webkit-transform: translateY(0);"></div>
|
||||
</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>
|
||||
<button type="button" class="aplayer-icon aplayer-icon-lrc">
|
||||
{{@ icons.lrc }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aplayer-notice"></div>
|
||||
<div class="aplayer-miniswitcher"><button class="aplayer-icon">{{@ icons.right }}</button></div>
|
||||
</div>
|
||||
<ol class="aplayer-list{{ if options.listFolded }} aplayer-list-hide{{ /if }}">
|
||||
{{ include './list-item.art' getObject({
|
||||
theme: options.theme,
|
||||
audio: options.audio,
|
||||
index: 1
|
||||
}) }}
|
||||
</ol>
|
||||
{{ else }}
|
||||
<ol class="aplayer-list{{ if options.listFolded }} aplayer-list-hide{{ /if }}">
|
||||
{{ include './list-item.art' getObject({
|
||||
theme: options.theme,
|
||||
audio: options.audio,
|
||||
index: 1
|
||||
}) }}
|
||||
</ol>
|
||||
<div class="aplayer-body">
|
||||
<div class="aplayer-pic" style="{{ if cover }}background-image: url("{{ cover }}");{{ /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>
|
||||
<button type="button" class="aplayer-icon aplayer-icon-lrc">
|
||||
{{@ icons.lrc }}
|
||||
</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}}
|
||||