手动分割调整

This commit is contained in:
孙佳艺
2026-06-25 16:04:57 +08:00
parent a0a60bd121
commit 78c3f36ca2
3 changed files with 243 additions and 208 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
}
})();
</script>
<script type="module" crossorigin src="/assets/index-BjRp0K8U.js"></script>
<script type="module" crossorigin src="/assets/index-DUhIsGse.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css">
</head>
<body>
+136 -105
View File
@@ -243,6 +243,7 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
const abortRef = useRef(false);
const rangeRef = useRef<[number, number]>([0, minDuration]);
const currentTimeRef = useRef(0);
const frameContainerRef = useRef<HTMLDivElement>(null);
const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
@@ -380,8 +381,13 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
const handleRangeChange = useCallback((value: number[]) => {
if (!integerDuration) return;
setLocalError('');
setRange((prev) => normalizeRange([Number(value[0]), Number(value[1])], prev, integerDuration, minDuration, maxDuration));
}, [integerDuration, maxDuration, minDuration, setRange]);
const nextRange = [Number(value[0]), Number(value[1])] as [number, number];
const clamped = [
Math.max(0, Math.min(integerDuration, nextRange[0])),
Math.max(nextRange[0], Math.min(integerDuration, nextRange[1])),
] as [number, number];
setRange(clamped);
}, [integerDuration, setRange]);
const handleRangeChangeComplete = useCallback(() => {
seekPreview(rangeRef.current[0]);
@@ -534,114 +540,139 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
</span>
</div>
<div style={{ position: 'relative', padding: '0 6px' }}>
<div
ref={frameContainerRef}
style={{
overflowX: 'auto',
overflowY: 'hidden',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
padding: '0 6px',
}}
>
<style>{`
div::-webkit-scrollbar {
display: none;
}
`}</style>
<div
style={{
display: 'grid',
gridTemplateColumns: `repeat(${Math.max(frames.length, 1)}, minmax(0, 1fr))`,
height: 86,
overflow: 'hidden',
borderRadius: 12,
background: '#fff',
position: 'relative',
gap: 1,
margin: 'auto',
display: 'flex',
flexDirection: 'column',
width: frames.length > 0 ? `${frames.length * 80 + Math.max(0, frames.length - 1) * 1}px` : '100%',
}}
>
{frameLoading && frames.length === 0 ? (
<div style={{ gridColumn: '1 / -1', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Spin size="small" />
<Text style={{ marginLeft: 8, color: '#64748b' }}>...</Text>
</div>
) : frames.length > 0 ? (
frames.map((frame) => {
const isSelected = frame.second >= range[0] && frame.second < range[1];
return (
<button
key={frame.second}
type="button"
onClick={() => handleFrameClick(frame.second)}
title={`${frame.second}s${frame.fallback ? `,实际取帧 ${frame.captureSecond?.toFixed(1)}s` : ''}`}
style={{
minWidth: 0,
height: 86,
borderTop: isSelected ? '5px solid #6366f1' : 'none',
borderBottom: isSelected ? '5px solid #6366f1' : 'none',
padding: 0,
background: 'transparent',
overflow: 'hidden',
cursor: disabledByDuration ? 'not-allowed' : 'pointer',
position: 'relative',
// borderRadius: 4,
}}
>
{frame.status === 'success' && frame.image ? (
<img src={frame.image} alt={`${frame.second}s`} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
) : frame.status === 'loading' ? (
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Spin size="small" />
</div>
) : (
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#64748b', fontSize: 12, background: '#fff' }}>
<span>{frame.second}s</span>
<span style={{ fontSize: 11 }}></span>
</div>
)}
<span
style={{
position: 'absolute',
left: 0,
bottom: 3,
color: '#ffffffff',
fontSize: 10,
textShadow: '0 1px 3px rgba(0,0,0,.7)',
}}
>
{frame.second}s
</span>
</button>
);
})
) : (
<div style={{ gridColumn: '1 / -1', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#64748b' }}>
</div>
)}
</div>
<div style={{ position: 'absolute', top: -6, left: 6, right: 0, }}>
<Slider
range
min={0}
max={integerDuration || maxDuration}
step={1}
value={range}
onChange={handleRangeChange}
onChangeComplete={handleRangeChangeComplete}
tooltip={{ formatter: (value) => `${toIntegerSecond(Number(value || 0))}s` }}
disabled={!integerDuration || disabledByDuration}
styles={{
track: {
background: '#6969dd63',
height: 70,
borderRadius: 3,
margin: '0 ',
},
rail: {
// background: '#0e447e70',
height: 70,
borderRadius: 3,
},
handle: {
width: 20,
height: 70,
marginTop: 0,
// backgroundColor: '#fff',
// border: '2px solid #6366f1',
borderRadius: '50%',
},
<div
style={{
display: 'flex',
height: 86,
borderRadius: 12,
background: '#fff',
gap: 1,
position: 'relative',
}}
/>
>
{frameLoading && frames.length === 0 ? (
<div style={{ flex: '1 1 auto', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Spin size="small" />
<Text style={{ marginLeft: 8, color: '#64748b' }}>...</Text>
</div>
) : frames.length > 0 ? (
frames.map((frame) => {
const isSelected = frame.second >= range[0] && frame.second < range[1];
return (
<button
key={frame.second}
type="button"
onClick={() => handleFrameClick(frame.second)}
title={`${frame.second}s${frame.fallback ? `,实际取帧 ${frame.captureSecond?.toFixed(1)}s` : ''}`}
style={{
minWidth: 80,
flex: '0 0 80px',
height: 86,
borderTop: isSelected ? '5px solid #6366f1' : 'none',
borderBottom: isSelected ? '5px solid #6366f1' : 'none',
padding: 0,
background: 'transparent',
overflow: 'hidden',
cursor: disabledByDuration ? 'not-allowed' : 'pointer',
position: 'relative',
}}
>
{frame.status === 'success' && frame.image ? (
<img src={frame.image} alt={`${frame.second}s`} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
) : frame.status === 'loading' ? (
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Spin size="small" />
</div>
) : (
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#64748b', fontSize: 12, background: '#fff' }}>
<span>{frame.second}s</span>
<span style={{ fontSize: 11 }}></span>
</div>
)}
<span
style={{
position: 'absolute',
left: 0,
bottom: 3,
color: '#ffffffff',
fontSize: 16,
textShadow: '0 1px 3px rgba(0,0,0,.7)',
}}
>
{frame.second}s
</span>
</button>
);
})
) : (
<div style={{ flex: '1 1 auto', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#64748b' }}>
</div>
)}
<div
style={{
position: 'absolute',
top: -10,
left: 0,
right: 0,
bottom: 0,
}}
>
<Slider
range
min={0}
max={integerDuration || maxDuration}
step={1}
value={range}
onChange={handleRangeChange}
onChangeComplete={handleRangeChangeComplete}
tooltip={{ formatter: (value) => `${toIntegerSecond(Number(value || 0))}s` }}
disabled={!integerDuration || disabledByDuration}
styles={{
track: {
background: '#6969dd63',
height: 86,
borderRadius: 12,
margin: '0 ',
},
rail: {
height: 86,
borderRadius: 12,
},
handle: {
width: 20,
height: 86,
marginTop: 0,
borderRadius: '50%',
},
}}
/>
</div>
</div>
</div>
</div>