smarter time labels
This commit is contained in:
parent
2ef039da12
commit
0da9524f87
139
src/index.html
139
src/index.html
@ -107,7 +107,16 @@
|
||||
border-radius: 2px;
|
||||
transition: width 0.3s ease;
|
||||
width: 0%;
|
||||
margin: 0 0 0.5rem;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
.timeline-labels {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.7rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
margin-bottom: 0.5rem;
|
||||
height: 1.2rem;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
@ -174,6 +183,7 @@
|
||||
|
||||
<article class="controls-card">
|
||||
<div class="timeline-progress" id="timelineProgress"></div>
|
||||
<div class="timeline-labels" id="timelineLabels" style="display: flex; justify-content: space-between; font-size: 0.8rem; color: #fff; margin-bottom: 0.5rem;"></div>
|
||||
<div class="control-group">
|
||||
|
||||
|
||||
@ -291,14 +301,14 @@
|
||||
thumb.classList.toggle('active', index === currentIndex);
|
||||
});
|
||||
|
||||
// Scroll active thumbnail into view
|
||||
// if (thumbnails[currentIndex]) {
|
||||
// thumbnails[currentIndex].scrollIntoView({
|
||||
// behavior: 'smooth',
|
||||
// block: 'nearest',
|
||||
// inline: 'center'
|
||||
// });
|
||||
// }
|
||||
// Scroll active thumbnail into view
|
||||
if (thumbnails[currentIndex]) {
|
||||
thumbnails[currentIndex].scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
inline: 'center'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function goToFirst() {
|
||||
@ -350,6 +360,117 @@
|
||||
function updateTimeline() {
|
||||
const progress = images.length > 0 ? ((currentIndex + 1) / images.length) * 100 : 0;
|
||||
document.getElementById('timelineProgress').style.width = progress + '%';
|
||||
updateTimelineLabels();
|
||||
}
|
||||
|
||||
function updateTimelineLabels() {
|
||||
const labelsContainer = document.getElementById('timelineLabels');
|
||||
labelsContainer.innerHTML = '';
|
||||
if (images.length === 0) return;
|
||||
|
||||
const firstDate = images[0].createdDate;
|
||||
const lastDate = images[images.length - 1].createdDate;
|
||||
const totalDuration = lastDate.getTime() - firstDate.getTime();
|
||||
|
||||
// Aim for approximately 6-8 labels depending on the container width
|
||||
const desiredLabels = Math.min(8, Math.max(6, Math.floor(window.innerWidth / 200)));
|
||||
|
||||
const labels = [];
|
||||
|
||||
// Always add the first timestamp
|
||||
labels.push({
|
||||
time: firstDate,
|
||||
pos: 0
|
||||
});
|
||||
|
||||
// Add evenly spaced timestamps
|
||||
for (let i = 1; i < desiredLabels - 1; i++) {
|
||||
const time = new Date(firstDate.getTime() + (totalDuration * i / (desiredLabels - 1)));
|
||||
|
||||
// Find closest image to this time
|
||||
let closestIdx = 0;
|
||||
let minDiff = Math.abs(images[0].createdDate - time);
|
||||
|
||||
for (let j = 1; j < images.length; j++) {
|
||||
const diff = Math.abs(images[j].createdDate - time);
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
closestIdx = j;
|
||||
}
|
||||
}
|
||||
|
||||
const pos = (closestIdx / (images.length - 1)) * 100;
|
||||
labels.push({
|
||||
time: images[closestIdx].createdDate,
|
||||
pos: pos
|
||||
});
|
||||
}
|
||||
|
||||
// Add the last timestamp
|
||||
labels.push({
|
||||
time: lastDate,
|
||||
pos: 100
|
||||
});
|
||||
|
||||
// Remove any labels that are too close to each other
|
||||
const filteredLabels = labels.filter((label, index) => {
|
||||
if (index === 0) return true;
|
||||
return Math.abs(label.pos - labels[index - 1].pos) >= 5;
|
||||
});
|
||||
|
||||
// Calculate if we need to show seconds (if total duration is less than 5 minutes)
|
||||
const showSeconds = totalDuration < 5 * 60 * 1000;
|
||||
|
||||
// Calculate optimal spacing between labels
|
||||
const containerWidth = labelsContainer.offsetWidth;
|
||||
const labelWidth = 70; // Approximate width of a time label
|
||||
const minSpacing = labelWidth * 1.5; // Minimum space needed between labels
|
||||
const maxLabels = Math.floor(containerWidth / minSpacing);
|
||||
const targetLabels = Math.min(filteredLabels.length, maxLabels);
|
||||
|
||||
// Select evenly spaced labels
|
||||
const finalLabels = [];
|
||||
if (filteredLabels.length > 0) {
|
||||
const step = (filteredLabels.length - 1) / (targetLabels - 1);
|
||||
for (let i = 0; i < targetLabels; i++) {
|
||||
const index = Math.round(i * step);
|
||||
finalLabels.push(filteredLabels[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Round the timestamps
|
||||
const roundedLabels = finalLabels.map(label => {
|
||||
const newTime = new Date(label.time);
|
||||
|
||||
if (showSeconds) {
|
||||
// Round seconds to nearest 5
|
||||
const seconds = newTime.getSeconds();
|
||||
const roundedSeconds = Math.round(seconds / 5) * 5;
|
||||
newTime.setSeconds(roundedSeconds);
|
||||
} else {
|
||||
// Round minutes to nearest 5 and set seconds to 0
|
||||
const minutes = newTime.getMinutes();
|
||||
const roundedMinutes = Math.round(minutes / 5) * 5;
|
||||
newTime.setMinutes(roundedMinutes);
|
||||
newTime.setSeconds(0);
|
||||
}
|
||||
|
||||
return {
|
||||
...label,
|
||||
time: newTime
|
||||
};
|
||||
});
|
||||
|
||||
// Render labels
|
||||
labelsContainer.style.position = 'relative';
|
||||
labelsContainer.innerHTML = roundedLabels.map(l => {
|
||||
const options = showSeconds
|
||||
? {hour: '2-digit', minute:'2-digit', second:'2-digit'}
|
||||
: {hour: '2-digit', minute:'2-digit'};
|
||||
return `<span style="position: absolute; left: ${l.pos}%; transform: translateX(-50%); white-space: nowrap;">
|
||||
${l.time.toLocaleTimeString([], options)}
|
||||
</span>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function togglePlay() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user