anzhiyu中使用alphatab实现播放乐谱

anzhiyu中使用alphatab实现播放乐谱
麟玖Samanzhiyu中使用alphatab实现播放乐谱
要在 Hexo 中使用 anzhiyu
主题并实现通过 AlphaTab
播放乐谱,可以按照以下步骤操作。以下是详细的实现方法:
1. 确保 Hexo 和 anzhiyu 主题已正确安装
在开始之前,请确认你已经成功安装了 Hexo 和 anzhiyu
主题。如果尚未安装,可以参考以下命令:
# 安装 Hexo
npm install -g hexo-cli
# 初始化 Hexo 项目
hexo init my-blog
cd my-blog
# 安装 anzhiyu 主题
git clone https://github.com/anzhiyu/hexo-theme-anzhiyu.git themes/anzhiyu
然后在 _config.yml
文件中设置主题为 anzhiyu
:
theme: anzhiyu
运行以下命令启动本地服务器,确保一切正常:
hexo server
2. 引入 AlphaTab
AlphaTab
是一个开源的乐谱渲染和播放库。你需要将其集成到你的 Hexo 博客中。
root/
一律代表你的博客根目录
(1) 下载 AlphaTab 资源
访问 AlphaTab 官方网站 或其 GitHub 仓库,下载最新版本的 AlphaTab 文件。你需要以下文件:
alphatab.js
(核心功能)alphatab.css
(创建样式文件)root/source/config/``css
- 如果需要音频支持,可能还需要
soundfont
文件。 - root一律代表你的博客根目录
将这些文件放到你的 Hexo 项目的 root/source/config/js
和 root/source/config/``css
目录下。
例如:
root/source/config/js/alphatab.js
root/source/config/alphatab.css
body[data-type="alphatab"] #page .page-title {
display: none;
}
.at-wrap {
/*width: 80vw;*/
height: 80vh;
margin: 0 auto;
border: 1px solid rgba(0, 0, 0, 0.12);
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
.at-content {
position: relative;
overflow: hidden;
flex: 1 1 auto;
}
/** Sidebar **/
.at-sidebar {
position: absolute;
top: 0;
left: 0;
bottom: 0;
max-width: 80px;
width: auto;
display: flex;
align-content: stretch;
z-index: 1002;
overflow: hidden;
border-right: 1px solid rgba(0, 0, 0, 0.12);
background: #FFFFFF;
}
.at-sidebar:hover {
max-width: 400px;
transition: max-width 0.2s;
overflow-y: auto;
}
.at-viewport {
overflow-y: auto;
position: absolute;
top: 0;
left: 70px;
right: 0;
bottom: 0;
padding-right: 20px;
}
.at-footer {
flex: 0 0 auto;
background: #436d9d;
color: #fff;
}
/** Overlay **/
.at-overlay {
/** Fill Parent */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 3;
/* Blurry dark shade */
backdrop-filter: blur(3px);
background: rgba(0, 0, 0, 0.5);
/** center content */
display: flex;
justify-content: center;
align-items: flex-start;
}
.at-overlay-content {
/* white box with drop-shadow */
margin-top: 20px;
background: #fff;
box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.3);
padding: 10px;
}
/** Track selector **/
.at-track {
display: flex;
position: relative;
padding: 5px;
transition: background 0.2s;
cursor: pointer;
}
.at-track:hover {
background: rgba(0, 0, 0, 0.1);
}
.at-track > .at-track-icon,
.at-track > .at-track-details {
display: flex;
flex-direction: column;
justify-content: center;
}
.at-track > .at-track-icon {
flex-shrink: 0;
font-size: 32px;
opacity: 0.5;
transition: opacity 0.2s;
width: 64px;
height: 64px;
margin-right: 5px;
align-items: center;
}
.at-track-name {
font-weight: bold;
margin-bottom: 5px;
}
.at-track:hover > .at-track-icon {
opacity: 0.8;
}
.at-track.active {
background: rgba(0, 0, 0, 0.03);
}
.at-track.active > .at-track-icon {
color: #4972a1;
opacity: 1;
}
.at-track > .at-track-name {
font-weight: 500;
}
/** Footer **/
.at-controls {
flex: 0 0 auto;
display: flex;
justify-content: space-between;
background: #436d9d;
color: #fff;
}
.at-controls > div {
display: flex;
justify-content: flex-start;
align-content: center;
align-items: center;
}
.at-controls > div > * {
display: flex;
text-align: center;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 4px;
margin: 0 3px;
}
.at-controls .btn {
color: #fff;
border-radius: 0;
height: 40px;
width: 40px;
height: 40px;
font-size: 16px;
}
.at-controls .btn.disabled {
cursor: progress;
opacity: 0.5;
}
.at-controls a.active {
background: #5588c7;
text-decoration: none;
}
.at-controls .btn i {
vertical-align: top;
}
.at-controls select {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
border: none;
width: 100%;
height: 40px;
background: #436d9d;
padding: 4px 10px;
color: #fff;
font-size: 16px;
text-align-last: center;
text-align: center;
-ms-text-align-last: center;
-moz-text-align-last: center;
cursor: pointer;
}
.at-song-title {
font-weight: bold;
}
.at-cursor-bar {
/* Defines the color of the bar background when a bar is played */
background: rgba(255, 242, 0, 0.25);
}
.at-selection div {
/* Defines the color of the selection background */
background: rgba(64, 64, 255, 0.1);
}
.at-cursor-beat {
/* Defines the beat cursor */
background: rgba(64, 64, 255, 0.75);
width: 3px;
}
.at-highlight * {
/* Defines the color of the music symbols when they are being played (svg) */
fill: #0078ff;
stroke: #0078ff;
}
.at-score-selector {
padding: 10px;
border-bottom: 1px solid #ddd;
}
.at-score-selector select {
width: 100%;
padding: 8px;
border-radius: 4px;
border: 1px solid #ccc;
}
(2) 在主题中加载 AlphaTab.css
编辑 root/_config.anzhiyu.yml
文件,在 inject
中添加以下代码以加载 AlphaTab 的 CSS 文件:
<inject:
head:
- <link rel="stylesheet" href="https://cdn.ljsama.cn/config/alphaTabFont/iconfont.css" media="defer" onload="this.media='all'">
(3) 创建乐谱页面
在root/themes/anzhiyu/layout/includes/page
中创建alphaTab.pug
并写入下代码
".at-wrap
.at-overlay
.at-overlay-content 乐谱加载中
.at-content
.at-sidebar
.at-sidebar-content
.at-track-list
.at-viewport
.at-main
.at-controls
.at-controls-left
a.btn.at-player-stop.disabled
i.iconfont.icon-zanting
a.btn.at-player-play-pause.disabled
i.iconfont.icon-bofang
span.at-player-progress 0%
.at-song-info
span.at-song-title
| -
span.at-song-artist
.at-song-position 00:00 / 00:00
.at-controls-right
a.btn.toggle.at-count-in
i.iconfont.icon-shalou
a.btn.at-metronome
i.iconfont.icon-jishiben
a.btn.at-loop
i.iconfont.icon-xunhuan
a.btn.at-print
i.iconfont.icon-dayinji
.at-zoom
i.iconfont.icon-wenjianjia
select
option(value="25") 25%
option(value="50") 50%
option(value="75") 75%
option(value="90") 90%
option(value="100" selected) 100%
option(value="110") 110%
option(value="125") 125%
option(value="150") 150%
option(value="200") 200%
.at-layout
select
option(value="horizontal") 水平
option(value="page" selected) 垂直
template#at-track-template
.at-track
.at-track-icon
i.iconfont.icon-guitar-du
.at-track-details
.at-track-name
//alphaTab.js的位置就是上一步保存的位置
script(src="https://cdn.ljsama.cn/config/js/alphaTab.js")
script(type="text/javascript").
// load elements
const wrapper = document.querySelector(".at-wrap");
const main = wrapper.querySelector(".at-main");
// initialize alphatab
const settings = {
file: "https://www.alphatab.net/files/canon.gp" //你的乐谱地址
player: {
enablePlayer: true,
soundFont: "https://cdn.ljsama.cn/config/alphaTabFont/sonivox.sf2", //播放乐谱的音频文件
scrollElement: wrapper.querySelector('.at-viewport')
},
};
console.log(settings)
// 添加全局加载方法
const api = new alphaTab.AlphaTabApi(main, settings);
window.loadScore = (filePath) => {
api.score = filePath;
api.render();
};
// overlay logic
const overlay = wrapper.querySelector(".at-overlay");
api.renderStarted.on(() => {
overlay.style.display = "flex";
});
api.renderFinished.on(() => {
overlay.style.display = "none";
});
// track selector
function createTrackItem(track) {
const trackItem = document
.querySelector("#at-track-template")
.content.cloneNode(true).firstElementChild;
trackItem.querySelector(".at-track-name").innerText = track.name;
trackItem.track = track;
trackItem.onclick = (e) => {
e.stopPropagation();
api.renderTracks([track]);
};
return trackItem;
}
const trackList = wrapper.querySelector(".at-track-list");
api.scoreLoaded.on((score) => {
// clear items
trackList.innerHTML = "";
// generate a track item for all tracks of the score
score.tracks.forEach((track) => {
trackList.appendChild(createTrackItem(track));
});
});
api.renderStarted.on(() => {
// collect tracks being rendered
const tracks = new Map();
api.tracks.forEach((t) => {
tracks.set(t.index, t);
});
// mark the item as active or not
const trackItems = trackList.querySelectorAll(".at-track");
trackItems.forEach((trackItem) => {
if (tracks.has(trackItem.track.index)) {
trackItem.classList.add("active");
} else {
trackItem.classList.remove("active");
}
});
});
/** Controls **/
api.scoreLoaded.on((score) => {
wrapper.querySelector(".at-song-title").innerText = score.title;
wrapper.querySelector(".at-song-artist").innerText = score.artist;
});
const countIn = wrapper.querySelector('.at-controls .at-count-in');
countIn.onclick = () => {
countIn.classList.toggle('active');
if (countIn.classList.contains('active')) {
api.countInVolume = 1;
} else {
api.countInVolume = 0;
}
};
const metronome = wrapper.querySelector(".at-controls .at-metronome");
metronome.onclick = () => {
metronome.classList.toggle("active");
if (metronome.classList.contains("active")) {
api.metronomeVolume = 1;
} else {
api.metronomeVolume = 0;
}
};
const loop = wrapper.querySelector(".at-controls .at-loop");
loop.onclick = () => {
loop.classList.toggle("active");
api.isLooping = loop.classList.contains("active");
};
wrapper.querySelector(".at-controls .at-print").onclick = () => {
api.print();
};
const zoom = wrapper.querySelector(".at-controls .at-zoom select");
zoom.onchange = () => {
const zoomLevel = parseInt(zoom.value) / 100;
api.settings.display.scale = zoomLevel;
api.updateSettings();
api.render();
};
const layout = wrapper.querySelector(".at-controls .at-layout select");
layout.onchange = () => {
switch (layout.value) {
case "horizontal":
api.settings.display.layoutMode = alphaTab.LayoutMode.Horizontal;
break;
case "page":
api.settings.display.layoutMode = alphaTab.LayoutMode.Page;
break;
}
api.updateSettings();
api.render();
};
// player loading indicator
const playerIndicator = wrapper.querySelector(
".at-controls .at-player-progress"
);
api.soundFontLoad.on((e) => {
const percentage = Math.floor((e.loaded / e.total) * 100);
playerIndicator.innerText = percentage + "%";
});
api.playerReady.on(() => {
playerIndicator.style.display = "none";
});
// main player controls
const playPause = wrapper.querySelector(
".at-controls .at-player-play-pause"
);
const stop = wrapper.querySelector(".at-controls .at-player-stop");
playPause.onclick = (e) => {
if (e.target.classList.contains("disabled")) {
return;
}
api.playPause();
};
stop.onclick = (e) => {
if (e.target.classList.contains("disabled")) {
return;
}
api.stop();
};
api.playerReady.on(() => {
playPause.classList.remove("disabled");
stop.classList.remove("disabled");
});
api.playerStateChanged.on((e) => {
const icon = playPause.querySelector("i.iconfont");
if (e.state === alphaTab.synth.PlayerState.Playing) {
icon.classList.remove("icon-bofang");
icon.classList.add("icon-24gl-pause2");
} else {
icon.classList.remove("icon-24gl-pause2");
icon.classList.add("icon-bofang");
}
});
// song position
function formatDuration(milliseconds) {
let seconds = milliseconds / 1000;
const minutes = (seconds / 60) | 0;
seconds = (seconds - minutes * 60) | 0;
return (
String(minutes).padStart(2, "0") +
":" +
String(seconds).padStart(2, "0")
);
}
const songPosition = wrapper.querySelector(".at-song-position");
let previousTime = -1;
api.playerPositionChanged.on((e) => {
// reduce number of UI updates to second changes.
const currentSeconds = (e.currentTime / 1000) | 0;
if (currentSeconds == previousTime) {
return;
}
songPosition.innerText =
formatDuration(e.currentTime) + " / " + formatDuration(e.endTime);
});
(4)在page.pug
中引入alphaTab.pug
extends includes/layout.pug
block content
#page
if top_img === false && !page.top_single
h1.page-title= page.title
case page.type
...其他代码
when 'alphatab'
include includes/page/alphatab.pug
(5)一键三连就可以看到效果啦
hexo clean && hexo generate && hexo server -p 5000
效果图如下
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果