Speed slider
This commit is contained in:
parent
3ddddc168b
commit
923848a156
9
nise-replay-viewer/package-lock.json
generated
9
nise-replay-viewer/package-lock.json
generated
@ -37,6 +37,7 @@
|
||||
"sonner": "^1.3.1",
|
||||
"tailwind-merge": "^2.0.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-md5": "^1.3.1",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -3645,6 +3646,14 @@
|
||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||
},
|
||||
"node_modules/ts-md5": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz",
|
||||
"integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
"sonner": "^1.3.1",
|
||||
"tailwind-merge": "^2.0.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"ts-md5": "^1.3.1",
|
||||
"zustand": "^4.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,55 +6,71 @@ import { Button } from "../components/ui/button";
|
||||
import { ArrowLeft, ArrowRight, PauseIcon, PlayIcon } from "lucide-react";
|
||||
|
||||
export function SongSlider() {
|
||||
const { beatmap, replay, playing, time } = state();
|
||||
const { beatmap, replay, playing, time, speed } = state();
|
||||
if (!beatmap || !replay) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Card className="absolute w-[95%] bottom-10 left-1/2 translate-x-[-50%] p-4 flex flex-col items-center gap-4">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex flex-col items-start w-full ">
|
||||
<p className="text-sm opacity-50">Current time</p>
|
||||
<p>{new Date(time).toISOString().slice(11, 19)}</p>
|
||||
<Card className="absolute w-[95%] bottom-10 left-1/2 translate-x-[-50%] p-4 flex flex-col items-center gap-4">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex flex-col items-start w-full ">
|
||||
<p className="text-sm opacity-50">Current time</p>
|
||||
<p>{new Date(time).toISOString().slice(11, 19)}</p>
|
||||
|
||||
</div>
|
||||
<div className="flex gap-2 w-full justify-center">
|
||||
<Button variant="outline" size="icon"
|
||||
onClick={() => {
|
||||
OsuRenderer.setTime(OsuRenderer.time - 1000);
|
||||
}}>
|
||||
<ArrowLeft />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
OsuRenderer.setPlaying(!playing);
|
||||
}}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
>
|
||||
{playing ? <PauseIcon /> : <PlayIcon />}
|
||||
</Button>
|
||||
<Button variant="outline" size="icon"
|
||||
onClick={() => {
|
||||
OsuRenderer.setTime(OsuRenderer.time + 1000);
|
||||
}}>
|
||||
<ArrowRight />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex w-full justify-end"></div>
|
||||
</div>
|
||||
<div className="flex gap-2 w-full justify-center">
|
||||
<Button variant="outline" size="icon"
|
||||
onClick={() => {
|
||||
OsuRenderer.setTime(OsuRenderer.time - 1000);
|
||||
}}>
|
||||
<ArrowLeft/>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
OsuRenderer.setPlaying(!playing);
|
||||
}}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
>
|
||||
{playing ? <PauseIcon/> : <PlayIcon/>}
|
||||
</Button>
|
||||
<Button variant="outline" size="icon"
|
||||
onClick={() => {
|
||||
OsuRenderer.setTime(OsuRenderer.time + 1000);
|
||||
}}>
|
||||
<ArrowRight/>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex w-full justify-end"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Slider
|
||||
step={10}
|
||||
min={0}
|
||||
max={replay.replay?.length}
|
||||
value={[time]}
|
||||
onValueChange={(value: any) => {
|
||||
OsuRenderer.setTime(value[0]);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col items-start w-full ">
|
||||
<p className="text-sm opacity-50">Current speed</p>
|
||||
<p>{speed}x</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<Slider
|
||||
step={10}
|
||||
min={0}
|
||||
max={replay.replay?.length}
|
||||
value={[time]}
|
||||
onValueChange={(value: any) => {
|
||||
OsuRenderer.setTime(value[0]);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
<Slider
|
||||
step={0.1}
|
||||
min={0.1}
|
||||
max={2}
|
||||
value={[speed]}
|
||||
onValueChange={(value: any) => {
|
||||
OsuRenderer.setSpeed(value[0]);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import { Vector2 } from "osu-classes";
|
||||
import p5 from "p5";
|
||||
import { loadImageAsync } from "@/utils";
|
||||
import { Md5 } from "ts-md5";
|
||||
|
||||
export class Drawer {
|
||||
|
||||
private static imageCache: Record<string, p5.Graphics> = {};
|
||||
|
||||
static images = {
|
||||
cursor: undefined as any as p5.Image,
|
||||
cursortrail: undefined as any as p5.Image,
|
||||
@ -128,51 +131,56 @@ export class Drawer {
|
||||
static drawSliderBody(origin: Vector2, path: Vector2[], radius: number) {
|
||||
Drawer.p.push();
|
||||
|
||||
const g = Drawer.p.createGraphics(512 * 4, 384 * 4);
|
||||
g.scale(2);
|
||||
g.translate(512 - 256, 384 - 192);
|
||||
//@ts-ignore
|
||||
const ctx = g.drawingContext;
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
g.translate(origin.x, origin.y);
|
||||
g.noFill();
|
||||
const cacheKey = Md5.hashStr(JSON.stringify(origin) + JSON.stringify(path) + JSON.stringify(radius));
|
||||
if (!this.imageCache[cacheKey]) {
|
||||
const g = Drawer.p.createGraphics(512 * 4, 384 * 4);
|
||||
g.scale(2);
|
||||
g.translate(512 - 256, 384 - 192);
|
||||
//@ts-ignore
|
||||
const ctx = g.drawingContext;
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
g.translate(origin.x, origin.y);
|
||||
g.noFill();
|
||||
|
||||
g.strokeWeight(radius * 2 - 10);
|
||||
g.strokeWeight(radius * 2 - 10);
|
||||
|
||||
g.stroke(255);
|
||||
g.beginShape();
|
||||
for (const node of path) {
|
||||
g.vertex(node.x, node.y);
|
||||
}
|
||||
g.endShape();
|
||||
g.stroke(255);
|
||||
g.beginShape();
|
||||
for (const node of path) {
|
||||
g.vertex(node.x, node.y);
|
||||
}
|
||||
g.endShape();
|
||||
|
||||
g.strokeWeight(radius * 2 - 17);
|
||||
g.stroke(10);
|
||||
g.beginShape();
|
||||
for (const node of path) {
|
||||
g.vertex(node.x, node.y);
|
||||
}
|
||||
|
||||
g.endShape();
|
||||
|
||||
for (let i = 0; i < radius * 2 - 17; i += 2) {
|
||||
g.strokeWeight(radius * 2 - 17 - i);
|
||||
g.stroke(Math.round((i / (radius * 2 - 17)) * 45));
|
||||
g.strokeWeight(radius * 2 - 17);
|
||||
g.stroke(10);
|
||||
g.beginShape();
|
||||
for (const node of path) {
|
||||
g.vertex(node.x, node.y);
|
||||
}
|
||||
|
||||
g.endShape();
|
||||
}
|
||||
|
||||
for (let i = 0; i < radius * 2 - 17; i += 2) {
|
||||
g.strokeWeight(radius * 2 - 17 - i);
|
||||
g.stroke(Math.round((i / (radius * 2 - 17)) * 45));
|
||||
g.beginShape();
|
||||
for (const node of path) {
|
||||
g.vertex(node.x, node.y);
|
||||
}
|
||||
|
||||
g.endShape();
|
||||
}
|
||||
|
||||
this.imageCache[cacheKey] = g;
|
||||
}
|
||||
Drawer.p.imageMode(Drawer.p.CORNER);
|
||||
Drawer.p.image(g, -256, -192, 512 * 2, 384 * 2);
|
||||
Drawer.p.image(this.imageCache[cacheKey], -256, -192, 512 * 2, 384 * 2);
|
||||
|
||||
Drawer.p.pop();
|
||||
}
|
||||
|
||||
|
||||
static drawCursorPath(
|
||||
path: {
|
||||
position: Vector2;
|
||||
|
||||
@ -20,6 +20,7 @@ export enum OsuRendererEvents {
|
||||
LOAD = "LOAD",
|
||||
PLAY = "PLAY",
|
||||
TIME = "TIME",
|
||||
SPEED = "SPEED",
|
||||
}
|
||||
|
||||
export class OsuRendererBridge extends EventEmitter {
|
||||
@ -37,6 +38,7 @@ export class OsuRenderer {
|
||||
|
||||
static event = new OsuRendererBridge();
|
||||
|
||||
static speedMultiplier = 0.5;
|
||||
static time: number = 0;
|
||||
static beatmap: StandardBeatmap;
|
||||
static og_beatmap: StandardBeatmap;
|
||||
@ -64,7 +66,7 @@ export class OsuRenderer {
|
||||
}
|
||||
|
||||
if (this.playing) {
|
||||
this.setTime(this.time + (Date.now() - this.lastRender));
|
||||
this.setTime(this.time + ((Date.now() - this.lastRender) * this.speedMultiplier));
|
||||
}
|
||||
|
||||
this.lastRender = Date.now();
|
||||
@ -189,6 +191,11 @@ export class OsuRenderer {
|
||||
this.event.emit(OsuRendererEvents.TIME);
|
||||
}
|
||||
|
||||
static setSpeed(speed: number) {
|
||||
this.speedMultiplier = speed;
|
||||
this.event.emit(OsuRendererEvents.SPEED);
|
||||
}
|
||||
|
||||
private static renderObject(hitObject: StandardHitObject) {
|
||||
if (hitObject instanceof Circle) {
|
||||
this.renderCircle(hitObject);
|
||||
@ -318,15 +325,6 @@ export class OsuRenderer {
|
||||
Drawer.drawSliderFollowPoint(sliderPos, hitObject.radius);
|
||||
}
|
||||
|
||||
// if (GameplayAnalyzer.renderJudgements[hitObject.startTime]) {
|
||||
// Drawer.setDrawingOpacity(opacity / 2);
|
||||
|
||||
// Drawer.drawCircleJudgement(
|
||||
// hitObject.stackedStartPosition,
|
||||
// hitObject.radius,
|
||||
// GameplayAnalyzer.renderJudgements[hitObject.startTime]
|
||||
// );
|
||||
// }
|
||||
Drawer.endDrawing();
|
||||
return arScale;
|
||||
}
|
||||
|
||||
@ -52,5 +52,10 @@ export class Renderer {
|
||||
time: OsuRenderer.time,
|
||||
});
|
||||
});
|
||||
OsuRenderer.event.on(OsuRendererEvents.SPEED, () => {
|
||||
state.setState({
|
||||
speed: OsuRenderer.speedMultiplier,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +55,7 @@ export const state = create<{
|
||||
mods: IMod[] | null;
|
||||
playing: boolean;
|
||||
time: number;
|
||||
speed: number;
|
||||
}>(() => ({
|
||||
metadataEditorDialog: false,
|
||||
openDialog: false,
|
||||
@ -69,6 +70,7 @@ export const state = create<{
|
||||
mods: null,
|
||||
playing: false,
|
||||
time: 0,
|
||||
speed: 1
|
||||
}));
|
||||
|
||||
state.subscribe((newState) => {
|
||||
|
||||
@ -1899,6 +1899,11 @@ ts-interface-checker@^0.1.9:
|
||||
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
|
||||
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
|
||||
|
||||
ts-md5@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz"
|
||||
integrity sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user