Move from miccall theme to kaze

This commit is contained in:
2024-02-26 19:58:14 -05:00
parent 9de3279861
commit 32b0f8343d
205 changed files with 71658 additions and 9534 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

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.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

View 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

View 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

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

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

@@ -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

View 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

View 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
View 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
View 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;

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;

View 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
View 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
View 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 FisherYates 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;

View 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}}

View File

@@ -0,0 +1,3 @@
{{each lyrics}}
<p{{ if $index === 0 }} class="aplayer-lrc-current"{{ /if }}>{{$value[1]}}</p>
{{/each}}

View File

@@ -0,0 +1,145 @@
{{ 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>
</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(&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>
<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}}