前言
没有音乐页面怎么行,花里胡哨的都安排上,本来想摆烂直接上aplayer,但考虑到我不会写样式(虽然我可以自学) 所以我音乐页面用的是Chuckle的方案
(等会为啥开头这么像这篇文章)
原本我是想做单页背景透明的,但我发现单页背景透明和我博客背景冲突导致丑的一批,所以我把背景换成了渐变动画 以下是我之前的音乐页面: 别问为啥是之前,这个音乐页面手机访问是这样的:
当时我还以为是浏览器的问题,立即换成了Chrome浏览器,结果:
(后来我用手机访问了那位大佬的音乐页面,也是一样出现这个问题,之前有人反映过这位大佬,大佬说自己去适配,
但我懒得适配,于是就有了这个教程 )
教程
1.0
TIP提示
这个教程是我基于https://www.php.cn/xiazai/js/6169 做的适配butterfly主题的版本,但后面我发现这跟csdn里的html好看的音乐播放器 (希望csdn那篇文章的作者看完不会在意)
- 新建[Blogroot]\themes\butterfly\layout\includes\page\music.pug,内容如下:
pug
link(rel='stylesheet' href='/css/musics.css')
.player
.player__header
.player__img.player__img--absolute.slider
button.player__button.player__button--absolute--nw.playlist
img(src='/svg/playlist.svg', alt='playlist-icon')
button.player__button.player__button--absolute--center.play
img(src='/svg/play.svg', alt='play-icon')
img(src='/svg/pause.svg', alt='pause-icon')
.slider__content
each item in theme.musicplayer.musics
img.img.slider__img(src=item.image, alt='cover')
.player__controls
button.player__button.back
img.img(src='/svg/back.svg', alt='back-icon')
p.player__context.slider__context
strong.slider__title
span.slider__name.player__title
button.player__button.next
img.img(src='/svg/next.svg', alt='next-icon')
.progres
.progres__filled
ul.player__playlist.list
each musics in theme.musicplayer.musics
li.player__song
img.player__img.img(src=musics.image, alt='cover')
p.player__context
b.player__song-name=musics.name
span.flex
span.player__title=musics.author
span.player__song-time
audio.audio(src=musics.url)
if theme.musicplayer.hitokoto
p#hitokoto
a#hitokoto_text(href="#",target="_blank") :D 获取中...
script(src='/js/player.js')
if theme.musicplayer.hitokoto
script.
fetch('https://v1.hitokoto.cn')
.then(response => response.json())
.then(data => {
const hitokoto = document.querySelector('#hitokoto_text')
hitokoto.href = `https://hitokoto.cn/?uuid=${data.uuid}`
hitokoto.innerText = data.hitokoto + " —— " + data.creator + "「" + data.from + "」"
})
.catch(console.error)
- 在[blogroot]\themes\butterfly\source\css\目录下新建musics.css,内容如下:
css
::root {
--parent-height : 20em ;
--duration: 1s ;
--duration-text-wrap: 12s 1.5s cubic-bezier(0.82, 0.82, 1, 1.01) ;
--cubic-header: var(--duration) cubic-bezier(0.71, 0.21, 0.3, 0.95) ;
--cubic-slider : var(--duration) cubic-bezier(0.4, 0, 0.2, 1) ;
--cubic-play-list : .35s var(--duration) cubic-bezier(0, 0.85, 0.11, 1.64) ;
--cubic-slider-context : cubic-bezier(1, -0.01, 1, 1.01) ;
}
.img {
width: 100% ;
display: block ;
object-fit: cover ;
}
.list {
margin: 0 ;
padding: 0 ;
list-style-type: none ;
}
.flex {
display: flex ;
align-items: center ;
justify-content: space-between ;
}
.uppercase{
text-transform: uppercase ;
}
.player {
width: 17.15em ;
display: flex ;
overflow: hidden ;
font-size: 1.22em ;
border-radius: 1.35em ;
flex-direction: column ;
background-color: white ;
height: var(--parent-height) ;
margin:0 auto ;
}
.player__header {
z-index: 1 ;
gap: 0 .4em ;
width: 100% ;
display: flex;
height: 5.85em ;
flex-shrink: 0 ;
position: relative;
align-items: flex-start ;
border-radius: inherit ;
justify-content: flex-end ;
background-color: white ;
padding: .95em 0.6em 0 1.2em ;
box-shadow: 0 2px 6px 1px #0000001f ;
transition: height var(--cubic-header), box-shadow var(--duration), padding var(--duration) ease-in-out ;
}
.player__header.open-header {
height: 100% ;
padding-left: 0 ;
padding-right: 0 ;
box-shadow: unset ;
}
.player__img {
width: 3.2em ;
height: 3.2em ;
border-radius: 1.32em ;
}
.player__img--absolute {
top: 1.4em ;
left: 1.2em ;
position: absolute ;
}
.slider {
flex-shrink: 0 ;
overflow: hidden ;
transition: width var(--cubic-header), height var(--cubic-header), top var(--cubic-header), left var(--cubic-header);
}
.slider.open-slider{
top: 0 ;
left: 0 ;
width: 100% ;
height: 14.6em ;
}
.slider__content {
display: flex ;
height: 100% ;
transition: transform var(--cubic-slider);
}
.slider__img {
filter: brightness(75%) ;
}
.slider__name,
.slider__title {
overflow: hidden ;
white-space: nowrap ;
}
.text-wrap {
display: block ;
white-space: pre ;
width: fit-content ;
animation: text-wrap var(--duration-text-wrap) infinite ;
}
@keyframes text-wrap {
75%{
transform: translate3d(-51.5%, 0, 0) ;
}
100%{
transform: translate3d(-51.5%, 0, 0) ;
}
}
.player__button {
all: unset ;
z-index: 100 ;
width: 2.5em ;
height: 2.5em ;
cursor: pointer ;
}
.playlist {
transform: scale(0) ;
transition: transform calc(var(--duration) / 2) ;
}
.slider.open-slider .playlist {
transform: scale(1) ;
transition: transform var(--cubic-play-list) ;
}
.player__button--absolute--nw {
top: 5.5% ;
left: 5.5% ;
position: absolute ;
}
.player__button--absolute--center {
top: 0 ;
left: 0 ;
right: 0 ;
bottom: 0 ;
margin: auto ;
position: absolute ;
}
img[alt ="pause-icon"] {
display: none ;
}
.player__controls {
width: 77% ;
gap: .5em 0 ;
display: flex ;
flex-wrap: wrap ;
align-items: center ;
will-change: contents ;
align-content: center ;
justify-content: center ;
transition: transform var(--cubic-header) , width var(--cubic-header) ;
}
.player__controls.move {
width: 88% ;
transform: translate3d(-1.1em , calc(var(--parent-height) - 153%) , 0) ;
}
.player__context {
margin: 0 ;
width: 100% ;
display: flex ;
line-height: 1.8 ;
flex-direction: column ;
justify-content: center ;
text-transform: capitalize ;
}
.slider__context {
width: 56.28% ;
cursor: pointer ;
text-align: center ;
padding-bottom: .2em ;
will-change: contents ;
transition: width var(--cubic-header) ;
animation: calc(var(--duration) / 2) var(--cubic-slider-context) ;
}
@keyframes opacity {
0% {
opacity: 0 ;
}
90%{
opacity: 1 ;
}
}
.player__controls.move .slider__context{
width: 49.48% ;
}
.player__title {
font-size: .7em ;
font-weight: bold ;
color: #00000086 ;
}
.progres {
width: 90% ;
height: .25em ;
cursor: pointer ;
border-radius: 1em ;
background-color: #e5e7ea ;
transition: width var(--cubic-header) ;
}
.player__controls.move .progres{
width: 98% ;
}
.progres__filled {
width: 0% ;
height: 100% ;
display: flex ;
position: relative ;
align-items: center ;
border-radius: inherit ;
background-color: #78adfe ;
}
.progres__filled::before {
right: 0 ;
width: .35em ;
content: " " ;
height: .35em ;
border-radius: 50% ;
position: absolute ;
background-color: #5781bd ;
}
.player__playlist {
height: 100% ;
overflow: auto ;
padding: 1.05em .9em 0 1.2em ;
}
.player__playlist::-webkit-scrollbar {
width: 0 ;
}
.player__song {
/* gap: 0 .65em ; */
display: flex ;
cursor: pointer ;
margin-bottom: .5em ;
padding-bottom: .7em ;
border-bottom: .1em solid #d8d8d859 ;
}
.player__song .player__context {
line-height: 1.5 ;
margin-left: .65em ;
}
.player__song-name {
font-size: .88em ;
}
.player__song-time {
font-size: .65em ;
font-weight: bold ;
color: #00000079 ;
height: fit-content ;
align-self: flex-end ;
}
.audio {
display: none !important ;
}
.player__song-name,.slider__title{
color:#000
}
#hitokoto{
text-align: center;
}
- 在[blogroot]\themes\butterfly\source\js\目录下新建player.js,内容如下:
js
// Designed by: Mauricio Bucardo
// Original image: https://dribbble.com/shots/6957353-Music-Player-Widget
"use strict";
// add elemnts
const bgBody = ["#e5e7e9", "#ff4545", "#f8ded3", "#ffc382", "#f5eda6", "#ffcbdc", "#dcf3f3"];
const body = document.body;
const player = document.querySelector(".player");
const playerHeader = player.querySelector(".player__header");
const playerControls = player.querySelector(".player__controls");
const playerPlayList = player.querySelectorAll(".player__song");
const playerSongs = player.querySelectorAll(".audio");
const playButton = player.querySelector(".play");
const nextButton = player.querySelector(".next");
const backButton = player.querySelector(".back");
const playlistButton = player.querySelector(".playlist");
const slider = player.querySelector(".slider");
const sliderContext = player.querySelector(".slider__context");
const sliderName = sliderContext.querySelector(".slider__name");
const sliderTitle = sliderContext.querySelector(".slider__title");
const sliderContent = slider.querySelector(".slider__content");
const sliderContentLength = playerPlayList.length - 1;
const sliderWidth = 100;
let left = 0;
let count = 0;
let song = playerSongs[count];
let isPlay = false;
const pauseIcon = playButton.querySelector("img[alt = 'pause-icon']");
const playIcon = playButton.querySelector("img[alt = 'play-icon']");
const progres = player.querySelector(".progres");
const progresFilled = progres.querySelector(".progres__filled");
let isMove = false;
// creat functions
function openPlayer() {
playerHeader.classList.add("open-header");
playerControls.classList.add("move");
slider.classList.add("open-slider");
}
function closePlayer() {
playerHeader.classList.remove("open-header");
playerControls.classList.remove("move");
slider.classList.remove("open-slider");
}
function next() {
if (count == sliderContentLength) {
count = count;
return
}
left += sliderWidth;
left = Math.min(left, (sliderContentLength) * sliderWidth);
sliderContent.style.transform = `translate3d(-${left}%, 0, 0)`;
count++;
changeSliderContext();
changeBgBody();
selectSong();
}
function back() {
if (count == 0) {
count = count
return
}
left -= sliderWidth;
left = Math.max(0, left);
sliderContent.style.transform = `translate3d(-${left}%, 0, 0)`;
count--;
}
function changeSliderContext() {
sliderContext.style.animationName = "opacity";
sliderName.textContent = playerPlayList[count].querySelector(".player__title").textContent;
sliderTitle.textContent = playerPlayList[count].querySelector(".player__song-name").textContent;
if (sliderName.textContent.length > 16) {
const textWrap = document.createElement("span");
textWrap.className = "text-wrap";
textWrap.innerHTML = sliderName.textContent + " " + sliderName.textContent;
sliderName.innerHTML = "";
sliderName.append(textWrap);
}
if (sliderTitle.textContent.length >= 18) {
const textWrap = document.createElement("span");
textWrap.className = "text-wrap";
textWrap.innerHTML = sliderTitle.textContent + " " + sliderTitle.textContent;
sliderTitle.innerHTML = "";
sliderTitle.append(textWrap);
}
}
function changeBgBody() {
body.style.backgroundColor = bgBody[count];
}
function selectSong() {
song = playerSongs[count];
for (const item of playerSongs) {
if (item != song) {
item.pause();
item.currentTime = 0;
}
}
if (isPlay) song.play();
}
function playSong() {
if (song.paused) {
song.play();
playIcon.style.display = "none";
pauseIcon.style.display = "block";
}else{
song.pause();
isPlay = false;
playIcon.style.display = "";
pauseIcon.style.display = "";
}
}
function progresUpdate() {
const progresFilledWidth = (this.currentTime / this.duration) * 100 + "%";
progresFilled.style.width = progresFilledWidth;
if (this.duration == this.currentTime) {
next();
}
if (count == sliderContentLength && song.currentTime == song.duration) {
playIcon.style.display = "block";
pauseIcon.style.display = "";
isPlay = false;
}
}
function scurb(e) {
// If we use e.offsetX, we have trouble setting the song time, when the mousemove is running
const currentTime = ( (e.clientX - progres.getBoundingClientRect().left) / progres.offsetWidth ) * song.duration;
song.currentTime = currentTime;
}
function durationSongs() {
let min = parseInt(this.duration / 60);
if (min < 10) min = "0" + min;
let sec = parseInt(this.duration % 60);
if (sec < 10) sec = "0" + sec;
const playerSongTime = `${min}:${sec}`;
this.closest(".player__song").querySelector(".player__song-time").append(playerSongTime);
}
changeSliderContext();
// add events
sliderContext.addEventListener("click", openPlayer);
sliderContext.addEventListener("animationend", () => sliderContext.style.animationName ='');
playlistButton.addEventListener("click", closePlayer);
nextButton.addEventListener("click", next);
backButton.addEventListener("click", () => {
back();
changeSliderContext();
changeBgBody();
selectSong();
});
playButton.addEventListener("click", () => {
isPlay = true;
playSong();
});
playerSongs.forEach(song => {
song.addEventListener("loadeddata" , durationSongs);
song.addEventListener("timeupdate" , progresUpdate);
});
progres.addEventListener("mousedown", (e) => {
scurb(e);
isMove = true;
song.muted = true;
});
document.addEventListener("mousemove", (e) => isMove && scurb(e));
document.addEventListener("mouseup", () => {
isMove = false
song.muted = false;
});
document.ondragstart = () => {
return false
};
- 编辑[blogroot]\themes\butterfly\layout\page.pug
diff
case page.type
when 'tags'
include includes/page/tags.pug
when 'link'
include includes/page/flink.pug
when 'bb'
include includes/page/bb.pug
when 'categories'
include includes/page/categories.pug
+ when 'music'
+ include includes/page/music.pug
default
include includes/page/default-page.pug
- 编辑[blogroot]\themes\butterfly_config.yml,在末尾添加:
yml
musicplayer:
hitokoto: true # 一言
musics:
- name: 'Eutopia'
author: 'Yoohsic Roomz'
image: 'http://p2.music.126.net/2VW7YM7You-iOyl4_smA0Q==/109951165875618375.jpg'
url: 'http://music.163.com/song/media/outer/url?id=29129889.mp3'
- name: 'Everything'
author: 'Yinyues'
image: 'http://p2.music.126.net/TcxdEdzRbKrwli4fVGeSiw==/6628955604788949.jpg'
url: 'http://music.163.com/song/media/outer/url?id=29544794.mp3'
# - name: '歌曲名字'
# author: '歌曲作者'
# image: '歌曲图片'
# url: '歌曲链接'
- 将svg.7z压缩包里的五个文件放在[blogroot]\themes\butterfly\source\svg 里面 https://sinz.lanzouw.com/iFMmG0kjkuvg (密码:gt97)
- 运行
hexo new page music
- 编辑[blogroot]\source\music\index.md,把里面的内容替换成:
markdown
---
type: 'music'
comments: false
aside: false
---
2.0
- 新建[Blogroot]\themes\butterfly\layout\includes\page\music.pug(有搞1.0的话直接把music.pug里面的内容全都替换成下面的内容),内容如下:
pug
link(
href="/assets/css/tplayer.css"
rel="stylesheet"
type="text/css"
)
link(
href="/assets/plugins/FontAwesome4.1/css/font-awesome.min.css"
rel="stylesheet"
type="text/css"
)
script(src="/assets/js/jquery.js")
script(src="/assets/js/jquery-ui.js")
script(src="/assets/js/tPlayer.js")
#t_wrapper
#t_cover
img(src="/assets/images/logo.png")
#t_top
#t_title_info
span.artist
span.title
#t_middle
#play
#pause.hidden
#t_progress.ui-corner-all.ui-slider.ui-slider-horizontal.ui-widget.ui-widget-content
#trackInfo
#error
#current 0:00
#duration 0:00
.ui-corner-all.ui-slider-range.ui-slider-range-min.ui-widget-header(style="width: 0%;")
span.ui-corner-all.ui-slider-handle.ui-state-default(style="left: 0%;" tabindex="0")
span#prev
span#next
#t_bottom
#range
#val
#vol
#rangeVal
#t_pls_show.noselectpls
#playlist
ul
each musics in theme.musicplayer.musics
li(
t_artist=musics.author
t_cover=musics.image
t_name=musics.name
)
a(href="#")=musics.name + ' - ' + musics.author
audio(preload="none" src=musics.url type="audio/mp3")
if theme.musicplayer.hitokoto
p#hitokoto
a#hitokoto_text(href="#",target="_blank",style="text-align:center;color: #fff;") :D 获取中...
if theme.musicplayer.hitokoto
script.
fetch('https://v1.hitokoto.cn')
.then(response => response.json())
.then(data => {
const hitokoto = document.querySelector('#hitokoto_text')
hitokoto.href = `https://hitokoto.cn/?uuid=${data.uuid}`
hitokoto.innerText = data.hitokoto + " —— " + data.creator + "「" + data.from + "」"
})
.catch(console.error)
- 将下面压缩包内五个文件夹全都解压在[blogroot]\themes\butterfly\source\assets 文件夹里面 https://sinz.lanzouw.com/iUJTx0krafpi (密码:3r5i)
INFO信息
1.0想升级成2.0的,下面的步骤可以不用做
- 编辑[blogroot]\themes\butterfly_config.yml,在末尾添加:
yml
musicplayer:
hitokoto: true # 一言
musics:
- name: 'Eutopia'
author: 'Yoohsic Roomz'
image: 'http://p2.music.126.net/2VW7YM7You-iOyl4_smA0Q==/109951165875618375.jpg'
url: 'http://music.163.com/song/media/outer/url?id=29129889.mp3'
- name: 'Everything'
author: 'Yinyues'
image: 'http://p2.music.126.net/TcxdEdzRbKrwli4fVGeSiw==/6628955604788949.jpg'
url: 'http://music.163.com/song/media/outer/url?id=29544794.mp3'
# - name: '歌曲名字'
# author: '歌曲作者'
# image: '歌曲图片'
# url: '歌曲链接'
- 运行
hexo new page music
- 编辑[blogroot]\source\music\index.md,把里面的内容替换成:
markdown
---
type: 'music'
comments: false
aside: false
---