Add timestamp window
This commit is contained in:
parent
9649fe173b
commit
c46c28c9bb
File diff suppressed because one or more lines are too long
|
|
@ -5,7 +5,6 @@
|
||||||
<link rel="stylesheet" href="styles.css" />
|
<link rel="stylesheet" href="styles.css" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<title>Lightningbeam</title>
|
<title>Lightningbeam</title>
|
||||||
<script src="Tone.js"></script>
|
|
||||||
|
|
||||||
<!-- Polyfill for web mode -->
|
<!-- Polyfill for web mode -->
|
||||||
<link type='text/css' rel='stylesheet' href='/jsMenus.css'>
|
<link type='text/css' rel='stylesheet' href='/jsMenus.css'>
|
||||||
|
|
|
||||||
149
src/main.js
149
src/main.js
|
|
@ -1032,9 +1032,18 @@ async function handleAudioEvent(event) {
|
||||||
case 'PlaybackPosition':
|
case 'PlaybackPosition':
|
||||||
// Sync frontend time with DAW time
|
// Sync frontend time with DAW time
|
||||||
if (playing) {
|
if (playing) {
|
||||||
context.activeObject.currentTime = event.time;
|
// Quantize time to framerate for animation playback
|
||||||
|
const framerate = context.activeObject.frameRate;
|
||||||
|
const frameDuration = 1 / framerate;
|
||||||
|
const quantizedTime = Math.floor(event.time / frameDuration) * frameDuration;
|
||||||
|
|
||||||
|
context.activeObject.currentTime = quantizedTime;
|
||||||
if (context.timelineWidget?.timelineState) {
|
if (context.timelineWidget?.timelineState) {
|
||||||
context.timelineWidget.timelineState.currentTime = event.time;
|
context.timelineWidget.timelineState.currentTime = quantizedTime;
|
||||||
|
}
|
||||||
|
// Update time display
|
||||||
|
if (context.updateTimeDisplay) {
|
||||||
|
context.updateTimeDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -2569,7 +2578,7 @@ function stage() {
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
"image/webp", //'image/svg+xml' // Disabling SVG until we can export them nicely
|
"image/webp", //'image/svg+xml' // Disabling SVG until we can export them nicely
|
||||||
];
|
];
|
||||||
const audioTypes = ["audio/mpeg"]; // TODO: figure out what other audio formats Tone.js accepts
|
const audioTypes = ["audio/mpeg"];
|
||||||
if (e.dataTransfer.items) {
|
if (e.dataTransfer.items) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (let item of e.dataTransfer.items) {
|
for (let item of e.dataTransfer.items) {
|
||||||
|
|
@ -3826,16 +3835,111 @@ function timelineV2() {
|
||||||
|
|
||||||
controls.push(recordGroup);
|
controls.push(recordGroup);
|
||||||
|
|
||||||
// Time format toggle button
|
// Time display
|
||||||
const toggleButton = document.createElement("button");
|
const timeDisplay = document.createElement("div");
|
||||||
toggleButton.textContent = timelineWidget.timelineState.timeFormat === 'frames' ? 'Frames' : 'Seconds';
|
timeDisplay.className = "time-display";
|
||||||
toggleButton.style.marginLeft = '10px';
|
timeDisplay.style.cursor = "pointer";
|
||||||
toggleButton.addEventListener("click", () => {
|
timeDisplay.title = "Click to change time format";
|
||||||
timelineWidget.toggleTimeFormat();
|
|
||||||
toggleButton.textContent = timelineWidget.timelineState.timeFormat === 'frames' ? 'Frames' : 'Seconds';
|
// Function to update time display
|
||||||
updateCanvasSize(); // Redraw after format change
|
const updateTimeDisplay = () => {
|
||||||
|
const currentTime = context.activeObject?.currentTime || 0;
|
||||||
|
const timeFormat = timelineWidget.timelineState.timeFormat;
|
||||||
|
const framerate = timelineWidget.timelineState.framerate;
|
||||||
|
|
||||||
|
if (timeFormat === 'frames') {
|
||||||
|
// Frames mode: show frame number and framerate
|
||||||
|
const frameNumber = Math.floor(currentTime * framerate);
|
||||||
|
|
||||||
|
timeDisplay.innerHTML = `
|
||||||
|
<div class="time-value time-frame-clickable" data-action="toggle-format">${frameNumber}</div>
|
||||||
|
<div class="time-label">FRAME</div>
|
||||||
|
<div class="time-fps-group time-fps-clickable" data-action="edit-fps">
|
||||||
|
<div class="time-value">${framerate}</div>
|
||||||
|
<div class="time-label">FPS</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
// Seconds mode: show MM:SS.mmm or HH:MM:SS.mmm
|
||||||
|
const totalSeconds = Math.floor(currentTime);
|
||||||
|
const milliseconds = Math.floor((currentTime - totalSeconds) * 1000);
|
||||||
|
const seconds = totalSeconds % 60;
|
||||||
|
const minutes = Math.floor(totalSeconds / 60) % 60;
|
||||||
|
const hours = Math.floor(totalSeconds / 3600);
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
timeDisplay.innerHTML = `
|
||||||
|
<div class="time-value">${hours}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}</div>
|
||||||
|
<div class="time-label">SEC</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
timeDisplay.innerHTML = `
|
||||||
|
<div class="time-value">${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}</div>
|
||||||
|
<div class="time-label">SEC</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Click handler for time display
|
||||||
|
timeDisplay.addEventListener("click", (e) => {
|
||||||
|
const target = e.target.closest('[data-action]');
|
||||||
|
|
||||||
|
if (!target) {
|
||||||
|
// Clicked outside specific elements in frames mode or anywhere in seconds mode
|
||||||
|
// Toggle format
|
||||||
|
timelineWidget.toggleTimeFormat();
|
||||||
|
updateTimeDisplay();
|
||||||
|
updateCanvasSize();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = target.getAttribute('data-action');
|
||||||
|
|
||||||
|
if (action === 'toggle-format') {
|
||||||
|
// Clicked on frame number - toggle format
|
||||||
|
timelineWidget.toggleTimeFormat();
|
||||||
|
updateTimeDisplay();
|
||||||
|
updateCanvasSize();
|
||||||
|
} else if (action === 'edit-fps') {
|
||||||
|
// Clicked on FPS - show input to edit framerate
|
||||||
|
console.log('[FPS Edit] Starting FPS edit');
|
||||||
|
const currentFps = timelineWidget.timelineState.framerate;
|
||||||
|
console.log('[FPS Edit] Current FPS:', currentFps);
|
||||||
|
|
||||||
|
const newFps = prompt('Enter framerate (FPS):', currentFps);
|
||||||
|
console.log('[FPS Edit] Prompt returned:', newFps);
|
||||||
|
|
||||||
|
if (newFps !== null && !isNaN(newFps) && newFps > 0) {
|
||||||
|
const fps = parseFloat(newFps);
|
||||||
|
console.log('[FPS Edit] Parsed FPS:', fps);
|
||||||
|
|
||||||
|
console.log('[FPS Edit] Setting framerate on timeline state');
|
||||||
|
timelineWidget.timelineState.framerate = fps;
|
||||||
|
|
||||||
|
console.log('[FPS Edit] Setting frameRate on activeObject');
|
||||||
|
context.activeObject.frameRate = fps;
|
||||||
|
|
||||||
|
console.log('[FPS Edit] Updating time display');
|
||||||
|
updateTimeDisplay();
|
||||||
|
|
||||||
|
console.log('[FPS Edit] Requesting redraw');
|
||||||
|
if (timelineWidget.requestRedraw) {
|
||||||
|
timelineWidget.requestRedraw();
|
||||||
|
}
|
||||||
|
console.log('[FPS Edit] Done');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
controls.push(toggleButton);
|
|
||||||
|
// Initial update
|
||||||
|
updateTimeDisplay();
|
||||||
|
|
||||||
|
// Store reference for updates
|
||||||
|
context.timeDisplay = timeDisplay;
|
||||||
|
context.updateTimeDisplay = updateTimeDisplay;
|
||||||
|
|
||||||
|
controls.push(timeDisplay);
|
||||||
|
|
||||||
return controls;
|
return controls;
|
||||||
};
|
};
|
||||||
|
|
@ -5689,27 +5793,6 @@ function getMimeType(filePath) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function startToneOnUserInteraction() {
|
|
||||||
// Function to handle the first interaction (click or key press)
|
|
||||||
const startTone = () => {
|
|
||||||
Tone.start()
|
|
||||||
.then(() => {
|
|
||||||
console.log("Tone.js started!");
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Error starting Tone.js:", err);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove the event listeners to prevent them from firing again
|
|
||||||
document.removeEventListener("click", startTone);
|
|
||||||
document.removeEventListener("keydown", startTone);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add event listeners for mouse click and key press
|
|
||||||
document.addEventListener("click", startTone);
|
|
||||||
document.addEventListener("keydown", startTone);
|
|
||||||
}
|
|
||||||
startToneOnUserInteraction();
|
|
||||||
|
|
||||||
function renderAll() {
|
function renderAll() {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -947,3 +947,88 @@ button {
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Time Display */
|
||||||
|
.time-display {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: linear-gradient(to bottom, #3a3a3a, #2a2a2a);
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
margin-left: 10px;
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-display:hover {
|
||||||
|
background: linear-gradient(to bottom, #4a4a4a, #3a3a3a);
|
||||||
|
border-color: #666;
|
||||||
|
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-display:active {
|
||||||
|
background: linear-gradient(to bottom, #2a2a2a, #1a1a1a);
|
||||||
|
box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-value {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #f0f0f0;
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||||
|
min-width: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-fps-group {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-fps-clickable {
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: background-color 0.15s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-fps-clickable:hover {
|
||||||
|
background-color: rgba(100, 150, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-fps-clickable:hover .time-value {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label {
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: #999;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark mode time display adjustments */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.time-display {
|
||||||
|
background: linear-gradient(to bottom, #3a3a3a, #2a2a2a);
|
||||||
|
border-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-value {
|
||||||
|
color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -578,6 +578,11 @@ class TimelineWindowV2 extends Widget {
|
||||||
draw(ctx) {
|
draw(ctx) {
|
||||||
ctx.save()
|
ctx.save()
|
||||||
|
|
||||||
|
// Update time display if it exists
|
||||||
|
if (this.context.updateTimeDisplay) {
|
||||||
|
this.context.updateTimeDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
// Draw background
|
// Draw background
|
||||||
ctx.fillStyle = backgroundColor
|
ctx.fillStyle = backgroundColor
|
||||||
ctx.fillRect(0, 0, this.width, this.height)
|
ctx.fillRect(0, 0, this.width, this.height)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue