Compare commits
10 Commits
8948a2d7c8
...
ccee2e2049
Author | SHA1 | Date |
---|---|---|
Zsolt Zitting | ccee2e2049 | |
Zsolt Zitting | 449a25ba7b | |
Zsolt Zitting | 21549a0f04 | |
Zsolt Zitting | 845bef2080 | |
WenqiOfficial | 5b7d63bb3b | |
WenqiOfficial | 252df94d9e | |
WenqiOfficial | ccfc70030f | |
WenqiOfficial | 9f30c58968 | |
WenqiOfficial | 90dda58d87 | |
WenqiOfficial | 7ca41fc85e |
|
@ -0,0 +1,40 @@
|
|||
# MercuryPlayerPlus
|
||||
|
||||
A map player of WACCA, plus for HACCA Project
|
||||
|
||||
Still updating...
|
||||
|
||||
## How to use?
|
||||
|
||||
You can deploy by yourself! ( •̀ ω •́ )
|
||||
|
||||
Or using my deployment(
|
||||
|
||||
Currently you need: **maps file** and **music file**.
|
||||
|
||||
I planed add song list in next version.(☆▽☆)
|
||||
|
||||
## Suggest?
|
||||
|
||||
You can use Github Issues, I will watch it. o(* ̄▽ ̄*)ブ
|
||||
|
||||
## Function
|
||||
|
||||
|
||||
| | Function |
|
||||
| ------- | ------- |
|
||||
| √ | Map Playing |
|
||||
|√ |Speed Changing|
|
||||
|√|Note SE|
|
||||
|Dev...|MV Playing|
|
||||
|Dev...|Song List|
|
||||
|||
|
||||
|...|Waiting For Your Suggestions...|
|
||||
|
||||
|
||||
|
||||
## OnlineWeb
|
||||
|
||||
[VercelURL(Some areas might block it)](https://mercury-player-plus.vercel.app/)
|
||||
|
||||
[MyURL](https://player.hacca.wenqi.ml)
|
12
index.htm
12
index.htm
|
@ -9,7 +9,7 @@
|
|||
<style>.long-audio audio {width:1000px}</style>
|
||||
<style>
|
||||
.bgm,.bgm *{opacity:1 !important}
|
||||
.bgm{display:flex;height:40px;line-height:40px;background:rgba(30,30,30,0.7);width:300px;text-align:center}
|
||||
.bgm{display:flex;height:40px;line-height:40px;background:rgba(30,30,30,0.7);width:500px;text-align:center}
|
||||
.bgm svg{fill:currentColor}
|
||||
.bgm *:hover>svg{fill:#48a0f7}
|
||||
.bgm *:hover:active>svg{fill:#2d89e6}
|
||||
|
@ -24,9 +24,17 @@
|
|||
.bgm .volume_slider input[type=range]{height:40px;margin:0;padding:0;width:100%}
|
||||
.long-audio .bgm {width:1000px;max-width:calc(100vw - 16px)}
|
||||
</style>
|
||||
<style>
|
||||
.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}
|
||||
@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}
|
||||
@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}
|
||||
.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}
|
||||
.github-corner {position:absolute; top:0; border:0; right:0; z-index:10}
|
||||
</style>
|
||||
</head>
|
||||
<body ontouchstart>
|
||||
<video src="https://cloud.zeroarea.ml:5221/api/v3/file/get/9419/mv_s01_016.mp4?sign=P90P_FYMwo7YAODlSee4oP1dMWir55SG6PQCnmPtZnI%3D%3A0" preload id="bga"></video>
|
||||
<a href="https://github.com/yellowberryHN/MercuryPlayer" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#fff; color:#151513" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
|
||||
<video src preload id="bga"></video>
|
||||
<canvas id="canvas"></canvas>
|
||||
<div style="position:fixed;left:5px;bottom:5px;color:white" class="no-hide">
|
||||
<div>SE Volume: <select id="se_volume"></select> <input id="se_file" name="mer_se_file" type="file" accept="audio/*" placeholder="sound effect file path..."/></div>
|
||||
|
|
118
main.js
118
main.js
|
@ -62,6 +62,12 @@ const arrowCanvas = {
|
|||
in: document.createElement('canvas'),
|
||||
out: document.createElement('canvas'),
|
||||
}
|
||||
function pathToArcPoint(ctx, x, y, r, angle) {
|
||||
ctx.lineTo(
|
||||
x + Math.cos(angle) * r,
|
||||
y + Math.sin(angle) * r,
|
||||
)
|
||||
}
|
||||
function createArrows() {
|
||||
arrowCanvas.in.width = maxR*2, arrowCanvas.in.height = maxR*2
|
||||
arrowCanvas.out.width = maxR*2, arrowCanvas.out.height = maxR*2
|
||||
|
@ -75,9 +81,9 @@ function createArrows() {
|
|||
ctx.in.translate(maxR, maxR); ctx.in.rotate(Math.PI / 2); ctx.in.translate(-maxR, -maxR)
|
||||
for (let i = 0; i < 30; i++) {
|
||||
ctx.in.beginPath()
|
||||
ctx.in.arc(maxR, maxR, maxR * 0.9, (i+0.25) * Math.PI / 15, (i+0.25) * Math.PI / 15)
|
||||
ctx.in.arc(maxR, maxR, maxR * 0.8, (i+0.5) * Math.PI / 15, (i+0.5) * Math.PI / 15)
|
||||
ctx.in.arc(maxR, maxR, maxR * 0.9, (i+0.75) * Math.PI / 15, (i+0.75) * Math.PI / 15)
|
||||
pathToArcPoint(ctx.in, maxR, maxR, maxR * 0.9, (i+0.25) * Math.PI / 15)
|
||||
pathToArcPoint(ctx.in, maxR, maxR, maxR * 0.8, (i+0.5) * Math.PI / 15)
|
||||
pathToArcPoint(ctx.in, maxR, maxR, maxR * 0.9, (i+0.75) * Math.PI / 15)
|
||||
ctx.in.strokeStyle = 'rgb(200,200,200)'; ctx.in.lineWidth = borderWidth; ctx.in.stroke()
|
||||
ctx.in.strokeStyle = 'rgb(203,29,25)'; ctx.in.lineWidth = colorWidth; ctx.in.stroke()
|
||||
}
|
||||
|
@ -85,9 +91,9 @@ function createArrows() {
|
|||
ctx.out.translate(maxR, maxR); ctx.out.rotate(Math.PI / 2); ctx.out.translate(-maxR, -maxR)
|
||||
for (let i = 0; i < 30; i++) {
|
||||
ctx.out.beginPath()
|
||||
ctx.out.arc(maxR, maxR, maxR * 0.75, (i+0.25) * Math.PI / 15, (i+0.25) * Math.PI / 15)
|
||||
ctx.out.arc(maxR, maxR, maxR * 0.85, (i+0.5) * Math.PI / 15, (i+0.5) * Math.PI / 15)
|
||||
ctx.out.arc(maxR, maxR, maxR * 0.75, (i+0.75) * Math.PI / 15, (i+0.75) * Math.PI / 15)
|
||||
pathToArcPoint(ctx.out, maxR, maxR, maxR * 0.75, (i+0.25) * Math.PI / 15)
|
||||
pathToArcPoint(ctx.out, maxR, maxR, maxR * 0.85, (i+0.5) * Math.PI / 15)
|
||||
pathToArcPoint(ctx.out, maxR, maxR, maxR * 0.75, (i+0.75) * Math.PI / 15)
|
||||
ctx.out.strokeStyle = 'rgb(200,200,200)'; ctx.out.lineWidth = borderWidth; ctx.out.stroke()
|
||||
ctx.out.strokeStyle = 'rgb(33,180,251)'; ctx.out.lineWidth = colorWidth; ctx.out.stroke()
|
||||
}
|
||||
|
@ -144,28 +150,43 @@ fetch('Table/music_info.json').then(r=>r.json()).then(r => {
|
|||
console.error('failed loading music table', e)
|
||||
})
|
||||
const seContext = new AudioContext
|
||||
//const bgmSrc = seContext.createMediaElementSource(bgm)
|
||||
//bgmSrc.connect(seContext.destination)
|
||||
|
||||
let seBuffer = null
|
||||
fetch('note.wav').then(r => r.arrayBuffer()).then(r => {
|
||||
seContext.decodeAudioData(r, buf => {
|
||||
if (buf) {
|
||||
seBuffer = buf
|
||||
} else {
|
||||
console.error('decode failed')
|
||||
}
|
||||
}, e => console.error(e))
|
||||
})
|
||||
let seRBuffer = null
|
||||
fetch('232.adx.wav').then(r => r.arrayBuffer()).then(r => {
|
||||
seContext.decodeAudioData(r, buf => {
|
||||
if (buf) {
|
||||
seRBuffer = buf
|
||||
} else {
|
||||
console.error('decode failed')
|
||||
}
|
||||
}, e => console.error(e))
|
||||
})
|
||||
let clkBuffer = null
|
||||
|
||||
if (window.location.protocol !== "file:") {
|
||||
fetch('note.wav').then(r => r.arrayBuffer()).then(r => {
|
||||
seContext.decodeAudioData(r, buf => {
|
||||
if (buf) {
|
||||
seBuffer = buf
|
||||
} else {
|
||||
console.error('decode failed')
|
||||
}
|
||||
}, e => console.error(e))
|
||||
})
|
||||
|
||||
fetch('rnote.wav').then(r => r.arrayBuffer()).then(r => {
|
||||
seContext.decodeAudioData(r, buf => {
|
||||
if (buf) {
|
||||
seRBuffer = buf
|
||||
} else {
|
||||
console.error('decode failed')
|
||||
}
|
||||
}, e => console.error(e))
|
||||
})
|
||||
|
||||
fetch('52.hca.wav').then(r => r.arrayBuffer()).then(r => {
|
||||
seContext.decodeAudioData(r, buf => {
|
||||
if (buf) {
|
||||
clkBuffer = buf
|
||||
} else {
|
||||
console.error('decode failed')
|
||||
}
|
||||
}, e => console.error(e))
|
||||
})
|
||||
}
|
||||
|
||||
function loadUsingSelect() {
|
||||
const id = music_select.value | 0
|
||||
const diffi = diffi_select.value | 0
|
||||
|
@ -584,7 +605,8 @@ function render(now) {
|
|||
|
||||
drawCount.actualFrame++
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
const centerX = canvas.width / 2, centerY = canvas.height / 2
|
||||
const cabView = (canvas.width == 1080 && canvas.height == 1920)
|
||||
const centerX = canvas.width / 2, centerY = cabView ? (canvas.height / 2) - 58 : canvas.height / 2
|
||||
|
||||
// outer ring
|
||||
ctx.lineWidth = 3
|
||||
|
@ -593,10 +615,10 @@ function render(now) {
|
|||
ctx.arc(centerX, centerY, maxR, 0, Math.PI * 2)
|
||||
ctx.stroke()
|
||||
if (enableBga) {
|
||||
ctx.fillStyle = 'rgba(0,0,0,0.8)'
|
||||
ctx.fillStyle = 'rgba(80,80,80,0.4)'
|
||||
ctx.fill()
|
||||
} else {
|
||||
ctx.fillStyle = 'rgba(32,32,32,0.8)'
|
||||
ctx.fillStyle = 'rgba(128,128,128,0.4)'
|
||||
ctx.fill()
|
||||
}
|
||||
|
||||
|
@ -628,7 +650,7 @@ function render(now) {
|
|||
if (chartHeader.OFFSET) {
|
||||
const offset = parseFloat(chartHeader.OFFSET)
|
||||
if (!isNaN(offset)) {
|
||||
startTs -= offset * 1000
|
||||
startTs += offset * 1000
|
||||
}
|
||||
}
|
||||
updateLaneOnState(-1, currentTs)
|
||||
|
@ -657,9 +679,9 @@ function render(now) {
|
|||
updateLaneOnState(previousTs, currentTs)
|
||||
// black out "off" lanes
|
||||
const laneBgGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, maxR)
|
||||
laneBgGradient.addColorStop(0, 'rgba(255,255,255,0)');
|
||||
laneBgGradient.addColorStop(0.2, 'rgba(255,255,255,0.1)');
|
||||
laneBgGradient.addColorStop(1, enableBga ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.4)');
|
||||
laneBgGradient.addColorStop(0, 'rgba(0,0,0,0.3)');
|
||||
laneBgGradient.addColorStop(0.2, 'rgba(0,0,0,0.4)');
|
||||
laneBgGradient.addColorStop(1, enableBga ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0.7)');
|
||||
ctx.fillStyle = laneBgGradient
|
||||
ctx.beginPath()
|
||||
for (let i = 0; i < 60 * laneEffectMul; i++) {
|
||||
|
@ -674,7 +696,7 @@ function render(now) {
|
|||
ctx.lineTo(centerX, centerY)
|
||||
}
|
||||
ctx.fill()
|
||||
const notesToRenderArr = getNotesForDraw(currentDistance - drawDistance * 0.1, drawDistance * 1.1)
|
||||
const notesToRenderArr = getNotesForDraw(currentDistance - drawDistance * 0.1, drawDistance * 1.1).filter(i => i.timestamp > currentTs - 200)
|
||||
notesToRender = {
|
||||
sectionSep: [],
|
||||
touch: [],
|
||||
|
@ -693,7 +715,7 @@ function render(now) {
|
|||
};
|
||||
notesToRenderArr.forEach(i => {
|
||||
switch (i.noteType) {
|
||||
case 'sectionSep': {notesToRender.sectionSep.push(i); break;}
|
||||
case 'sectionSep': {if (i.section) notesToRender.sectionSep.push(i); break;}
|
||||
case '1': // touch
|
||||
case '2': // bonus touch
|
||||
case '20': {notesToRender.touch.push(i); break;} // touch R
|
||||
|
@ -777,11 +799,7 @@ function render(now) {
|
|||
Math.PI * (start / 30) + gapWidth, Math.PI * (end / 30) - gapWidth
|
||||
)
|
||||
} else {
|
||||
ctx.arc(
|
||||
centerX, centerY,
|
||||
r,
|
||||
Math.PI * (end / 30) - gapWidth, Math.PI * (end / 30) - gapWidth
|
||||
)
|
||||
pathToArcPoint(ctx, centerX, centerY, r, Math.PI * (end / 30) - gapWidth)
|
||||
}
|
||||
}
|
||||
for (let i=nodeCount-1; i>=0; i--) {
|
||||
|
@ -796,11 +814,7 @@ function render(now) {
|
|||
true
|
||||
)
|
||||
} else {
|
||||
ctx.arc(
|
||||
centerX, centerY,
|
||||
r,
|
||||
Math.PI * (start / 30) + gapWidth, Math.PI * (start / 30) + gapWidth
|
||||
)
|
||||
pathToArcPoint(ctx, centerX, centerY, r, Math.PI * (start / 30) - gapWidth)
|
||||
}
|
||||
}
|
||||
ctx.closePath()
|
||||
|
@ -1007,7 +1021,7 @@ function render(now) {
|
|||
ctx.lineWidth = i.w / 0.8 * 4 * displayRatio
|
||||
ctx.beginPath()
|
||||
i.n.forEach(i => {
|
||||
ctx.arc(0, 0, maxR * (0.875 + 0.075 * i[0]), i[1], i[1])
|
||||
pathToArcPoint(ctx, 0, 0, maxR * (0.875 + 0.075 * i[0]), i[1])
|
||||
})
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
|
@ -1037,6 +1051,7 @@ function render(now) {
|
|||
ctx.stroke()
|
||||
}
|
||||
|
||||
// circle mask
|
||||
{
|
||||
ctx.globalCompositeOperation = 'destination-in'
|
||||
ctx.beginPath();
|
||||
|
@ -1090,7 +1105,7 @@ function render(now) {
|
|||
|
||||
{
|
||||
while (pendingSeTrigger.length && pendingSeTrigger[0] - currentTs < 100) {
|
||||
if (!seBuffer) break
|
||||
if (!seBuffer && !clkBuffer) break
|
||||
if (pendingSeTrigger[0] - currentTs > -25) {
|
||||
let bufSrc = seContext.createBufferSource()
|
||||
bufSrc.buffer = seBuffer
|
||||
|
@ -1103,7 +1118,7 @@ function render(now) {
|
|||
if (!seRBuffer) break
|
||||
if (pendingSeRTrigger[0] - currentTs > -25) {
|
||||
let bufSrc = seContext.createBufferSource()
|
||||
bufSrc.buffer = seRBuffer
|
||||
bufSrc.buffer = clkBuffer != null ? clkBuffer : seRBuffer
|
||||
bufSrc.connect(gain)
|
||||
bufSrc.start(seContext.currentTime + Math.max(0, pendingSeRTrigger[0] - currentTs) / 1000)
|
||||
}
|
||||
|
@ -1122,7 +1137,7 @@ function render(now) {
|
|||
}
|
||||
}
|
||||
let globalPlaybackOffset = 0
|
||||
if (/win32/i.test(navigator.platform) && /firefox/i.test(navigator.userAgent)) globalPlaybackOffset = -60
|
||||
//if (/win32/i.test(navigator.platform) && /firefox/i.test(navigator.userAgent)) globalPlaybackOffset = -60
|
||||
let CALC_CONE_HEIGHT = 6
|
||||
let CALC_CONE_RADIUS = 2
|
||||
function distanceToRenderRadius (maxR, distance) {
|
||||
|
@ -1283,11 +1298,12 @@ function resize() {
|
|||
const w = Math.round(window.innerWidth * devicePixelRatio), h = Math.round(window.innerHeight * devicePixelRatio)
|
||||
canvas.width = w
|
||||
canvas.height = h
|
||||
maxR = Math.round(Math.min(w, h) * 0.45)
|
||||
const cabView = (canvas.width == 1080 && canvas.height == 1920)
|
||||
maxR = cabView ? 530 : Math.round(Math.min(w, h) * 0.45)
|
||||
drawForNextFrame = true
|
||||
displayRatio = Math.max(w, h) / 1920
|
||||
const wView = window.innerWidth, hView = window.innerHeight
|
||||
const centerX = wView / 2, centerY = hView / 2
|
||||
const centerX = wView / 2, centerY = cabView ? (hView / 2) - 58 : hView / 2
|
||||
const rView = Math.round(Math.min(wView, hView) * 0.45)
|
||||
|
||||
if (enableBga) {
|
||||
|
|
Loading…
Reference in New Issue