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;
|
border-radius: 2px;
|
||||||
transition: width 0.3s ease;
|
transition: width 0.3s ease;
|
||||||
width: 0%;
|
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 {
|
.control-group {
|
||||||
@ -174,6 +183,7 @@
|
|||||||
|
|
||||||
<article class="controls-card">
|
<article class="controls-card">
|
||||||
<div class="timeline-progress" id="timelineProgress"></div>
|
<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">
|
<div class="control-group">
|
||||||
|
|
||||||
|
|
||||||
@ -291,14 +301,14 @@
|
|||||||
thumb.classList.toggle('active', index === currentIndex);
|
thumb.classList.toggle('active', index === currentIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Scroll active thumbnail into view
|
// Scroll active thumbnail into view
|
||||||
// if (thumbnails[currentIndex]) {
|
if (thumbnails[currentIndex]) {
|
||||||
// thumbnails[currentIndex].scrollIntoView({
|
thumbnails[currentIndex].scrollIntoView({
|
||||||
// behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
// block: 'nearest',
|
block: 'nearest',
|
||||||
// inline: 'center'
|
inline: 'center'
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToFirst() {
|
function goToFirst() {
|
||||||
@ -350,6 +360,117 @@
|
|||||||
function updateTimeline() {
|
function updateTimeline() {
|
||||||
const progress = images.length > 0 ? ((currentIndex + 1) / images.length) * 100 : 0;
|
const progress = images.length > 0 ? ((currentIndex + 1) / images.length) * 100 : 0;
|
||||||
document.getElementById('timelineProgress').style.width = progress + '%';
|
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() {
|
function togglePlay() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user