参考这个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>时间轴剪辑播放器</title>
<style>
body {
font-family: sans-serif;
margin: 20px;
}
#preview {
width: 600px;
height: 340px;
background: #000;
display: flex;
align-items: center;
justify-content: center;
}
#preview img, #preview video {
max-width: 100%;
max-height: 100%;
}
#timelineWrapper {
position: relative;
width: 100%;
margin-top: 20px;
overflow-x: auto;
}
#timeline {
display: flex;
height: 100px;
border: 2px dashed #ccc;
padding: 10px;
position: relative;
min-width: 600px;
}
.clip {
width: 100px;
height: 80px;
margin-right: 5px;
border: 1px solid #999;
background-size: cover;
background-position: center;
position: relative;
}
#pointer {
position: absolute;
width: 2px;
background: red;
top: 0;
bottom: 0;
left: 0;
cursor: ew-resize;
}
#controls {
margin-top: 20px;
}
button {
margin-right: 10px;
}
</style>
</head>
<body>
<h2>剪辑时间轴播放器</h2>
<div id="preview"></div>
<div id="timelineWrapper">
<div id="timeline" ondragover="event.preventDefault()" ondrop="handleDrop(event)">
<div id="pointer"></div>
</div>
</div>
<div id="controls">
<button onclick="playSequence()">播放</button>
<button onclick="pauseSequence()">暂停</button>
<button onclick="stopSequence()">停止</button>
</div>
<script>
const timeline = document.getElementById('timeline');
const pointer = document.getElementById('pointer');
const preview = document.getElementById('preview');
let clips = [];
let totalDuration = 0;
let currentIndex = 0;
let currentTime = 0;
let isPaused = false;
let isStopped = false;
let pointerInterval = null;
function handleDrop(event) {
event.preventDefault();
const files = Array.from(event.dataTransfer.files);
files.forEach(file => {
const url = URL.createObjectURL(file);
const ext = file.name.split('.').pop().toLowerCase();
let type = '';
if (['mp4', 'webm', 'ogg'].includes(ext)) {
type = 'video';
const video = document.createElement('video');
video.src = url;
video.addEventListener('loadedmetadata', () => {
const duration = video.duration;
addClip(type, url, duration);
});
} else if (['png', 'jpg', 'jpeg', 'gif'].includes(ext)) {
type = 'image';
addClip(type, url, 5); // 图片固定播放 5s
}
});
}
function addClip(type, src, duration) {
clips.push({ type, src, duration });
const div = document.createElement('div');
div.className = 'clip';
if (type === 'image') {
div.style.backgroundImage = `url(${src})`;
} else {
div.style.background = '#333 url(play-icon.png) center center no-repeat';
}
timeline.appendChild(div);
updateTotalDuration();
}
function updateTotalDuration() {
totalDuration = clips.reduce((sum, clip) => sum + clip.duration, 0);
}
function playSequence(startTime = 0) {
if (clips.length === 0) return;
isPaused = false;
isStopped = false;
// 如果是从中间跳转
if (startTime > 0) {
[currentIndex, currentTime] = findClipByGlobalTime(startTime);
}
startPointerAnimation(startTime);
playClipAt(currentIndex, currentTime);
}
function pauseSequence() {
isPaused = true;
const video = preview.querySelector('video');
if (video) video.pause();
clearInterval(pointerInterval);
}
function stopSequence() {
isStopped = true;
clearInterval(pointerInterval);
preview.innerHTML = '';
currentIndex = 0;
currentTime = 0;
pointer.style.left = `0px`;
}
function findClipByGlobalTime(globalTime) {
let time = 0;
for (let i = 0; i < clips.length; i++) {
const next = time + clips[i].duration;
if (globalTime < next) {
return [i, globalTime - time];
}
time = next;
}
return [clips.length - 1, 0];
}
function playClipAt(index, offset = 0) {
if (index >= clips.length || isStopped) return;
const clip = clips[index];
preview.innerHTML = '';
if (clip.type === 'image') {
const img = document.createElement('img');
img.src = clip.src;
preview.appendChild(img);
setTimeout(() => {
if (!isPaused && !isStopped) {
currentIndex++;
playClipAt(currentIndex);
}
}, (clip.duration - offset) * 1000);
} else if (clip.type === 'video') {
const video = document.createElement('video');
video.src = clip.src;
video.autoplay = true;
video.controls = false;
video.currentTime = offset;
preview.appendChild(video);
video.onended = () => {
if (!isPaused && !isStopped) {
currentIndex++;
playClipAt(currentIndex);
}
};
}
}
function startPointerAnimation(startAt = 0) {
const timelineWidth = timeline.scrollWidth;
pointerInterval = setInterval(() => {
if (isPaused || isStopped) return;
// 计算当前总播放时间
let elapsed = 0;
for (let i = 0; i < currentIndex; i++) {
elapsed += clips[i].duration;
}
const video = preview.querySelector('video');
if (video) {
elapsed += video.currentTime;
} else {
elapsed += currentTime;
}
if (elapsed >= totalDuration) {
clearInterval(pointerInterval);
return;
}
pointer.style.left = `${(elapsed / totalDuration) * timelineWidth}px`;
}, 50);
}
pointer.addEventListener('mousedown', (e) => {
e.preventDefault();
clearInterval(pointerInterval);
isPaused = true;
const rect = timeline.getBoundingClientRect();
const onMove = (moveEvent) => {
let x = moveEvent.clientX - rect.left;
x = Math.max(0, Math.min(x, timeline.scrollWidth));
pointer.style.left = `${x}px`;
};
const onUp = (upEvent) => {
let x = upEvent.clientX - rect.left;
x = Math.max(0, Math.min(x, timeline.scrollWidth));
const percent = x / timeline.scrollWidth;
const jumpTime = percent * totalDuration;
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
stopSequence(); // 先停止
playSequence(jumpTime); // 再跳转播放
};
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
});
</script>
</body>
</html>
网友回复


