fix: optimize media thumbnail display by removing tiling utility and using CSS background instead

This commit is contained in:
Anwarul Islam 2025-07-15 00:12:01 +06:00
parent c628b5a8ee
commit 2c941b1585
2 changed files with 27 additions and 105 deletions

View File

@ -25,7 +25,6 @@ import {
getTrackElementClasses,
TIMELINE_CONSTANTS,
getTrackHeight,
calculateMediaTiling,
} from "@/constants/timeline-constants";
import {
DropdownMenu,
@ -270,71 +269,45 @@ export function TimelineElement({
}
if (mediaItem.type === "image") {
// Use utility function to calculate optimal tiling
const tiling = calculateMediaTiling(elementWidth, track.type, "image");
// Calculate tile size based on 16:9 aspect ratio
const trackHeight = getTrackHeight(track.type);
const tileHeight = trackHeight - 8; // Account for padding
const tileWidth = tileHeight * (16 / 9);
return (
<div className="w-full h-full flex items-center justify-start overflow-hidden">
<div className="bg-[#004D52]/20 h-full flex">
{Array.from({ length: tiling.totalTiles }, (_, index) => {
const isPartialTile =
index === tiling.numCompleteTiles && tiling.showPartialTile;
const tileWidthToUse = isPartialTile
? tiling.remainingWidth
: tiling.tileWidth;
return (
<div
key={index}
className="flex-shrink-0 h-full overflow-hidden"
style={{ width: `${tileWidthToUse}px` }}
>
<img
src={mediaItem.url}
alt={mediaItem.name}
className="w-full h-full object-cover"
draggable={false}
/>
</div>
);
})}
</div>
<div
className="bg-[#004D52]/20 h-full w-full"
style={{
backgroundImage: `url(${mediaItem.url})`,
backgroundRepeat: "repeat-x",
backgroundSize: `${tileWidth}px ${tileHeight}px`,
backgroundPosition: "left center",
}}
/>
</div>
);
}
if (mediaItem.type === "video" && mediaItem.thumbnailUrl) {
// Use utility function to calculate optimal tiling
const tiling = calculateMediaTiling(elementWidth, track.type, "video");
// Calculate tile size based on 16:9 aspect ratio
const trackHeight = getTrackHeight(track.type);
const tileHeight = trackHeight - 16; // Account for padding
const tileWidth = tileHeight * (16 / 9);
return (
<div className="w-full h-full flex items-center overflow-hidden relative">
<div className="flex h-full">
{Array.from({ length: tiling.totalTiles }, (_, index) => {
const isPartialTile =
index === tiling.numCompleteTiles && tiling.showPartialTile;
const tileWidthToUse = isPartialTile
? tiling.remainingWidth
: tiling.tileWidth;
return (
<div
key={index}
className="flex-shrink-0 h-full flex items-center justify-center p-1 overflow-hidden"
style={{ width: `${tileWidthToUse}px` }}
>
<img
src={mediaItem.thumbnailUrl}
alt={mediaItem.name}
className="w-full h-full object-cover rounded-sm"
draggable={false}
/>
</div>
);
})}
</div>
<div
className="h-full w-full"
style={{
backgroundImage: `url(${mediaItem.thumbnailUrl})`,
backgroundRepeat: "repeat-x",
backgroundSize: `${tileWidth}px ${tileHeight}px`,
backgroundPosition: "left center",
}}
/>
{/* Show name overlay on the right if there's sufficient space */}
{tiling.canShowOverlay && (
{elementWidth > tileWidth * 1.5 && (
<div className="absolute right-2 top-1/2 -translate-y-1/2 bg-black/70 text-white text-xs px-2 py-1 rounded pointer-events-none max-w-[40%] truncate">
{element.name}
</div>

View File

@ -104,54 +104,3 @@ export function snapTimeToFrame(time: number, fps: number): number {
export function getFrameDuration(fps: number): number {
return 1 / fps;
}
// Media thumbnail tiling constants
export const MEDIA_THUMBNAIL_CONSTANTS = {
DEFAULT_ASPECT_RATIO: 16 / 9,
IMAGE_PADDING: 8,
VIDEO_PADDING: 16,
PARTIAL_TILE_THRESHOLD_IMAGE: 0.2, // Show partial tile if >20% visible for images
PARTIAL_TILE_THRESHOLD_VIDEO: 0.3, // Show partial tile if >30% visible for videos
MIN_OVERLAY_WIDTH_RATIO: 1.5, // Minimum width ratio to show name overlay
} as const;
// Utility function to calculate media thumbnail tiling
export function calculateMediaTiling(
elementWidth: number,
trackType: TrackType,
mediaType: "image" | "video"
) {
const trackHeight = getTrackHeight(trackType);
const aspectRatio = MEDIA_THUMBNAIL_CONSTANTS.DEFAULT_ASPECT_RATIO;
const padding =
mediaType === "image"
? MEDIA_THUMBNAIL_CONSTANTS.IMAGE_PADDING
: MEDIA_THUMBNAIL_CONSTANTS.VIDEO_PADDING;
const tileHeight = trackHeight - padding;
const tileWidth = tileHeight * aspectRatio;
// Calculate how many complete tiles we can fit
const numCompleteTiles = Math.floor(elementWidth / tileWidth);
const remainingWidth = elementWidth - numCompleteTiles * tileWidth;
const threshold =
mediaType === "image"
? MEDIA_THUMBNAIL_CONSTANTS.PARTIAL_TILE_THRESHOLD_IMAGE
: MEDIA_THUMBNAIL_CONSTANTS.PARTIAL_TILE_THRESHOLD_VIDEO;
const showPartialTile = remainingWidth > tileWidth * threshold;
const totalTiles = numCompleteTiles + (showPartialTile ? 1 : 0);
return {
tileWidth,
tileHeight,
numCompleteTiles,
remainingWidth,
showPartialTile,
totalTiles: Math.max(1, totalTiles), // Always show at least one tile
canShowOverlay:
elementWidth >
tileWidth * MEDIA_THUMBNAIL_CONSTANTS.MIN_OVERLAY_WIDTH_RATIO,
};
}