Files
memefast/resources/js/modules/editor/partials/canvas/video-preview/video-preview-utils.js
2025-06-18 11:50:17 +08:00

151 lines
4.7 KiB
JavaScript

// video-preview-utils.js
// Snap settings
export const POSITION_SNAP_THRESHOLD = 10; // Pixels within which to snap to center
// Function to determine which image source to use for videos
export const getImageSource = (element, videoStates, isPlaying) => {
const isVideoActive = videoStates[element.id] && isPlaying;
if (isVideoActive && element.videoElement && element.isVideoReady) {
return element.videoElement;
} else if (element.posterImage && element.isVideoPoster) {
return element.posterImage;
}
return null;
};
// Helper function to get font style for text elements
export const getTextFontStyle = (element) => {
const isBold = element.fontWeight === 'bold' || element.fontWeight === 700;
const isItalic = element.fontStyle === 'italic';
if (isBold && isItalic) {
return 'bold italic';
} else if (isBold) {
return 'bold';
} else if (isItalic) {
return 'italic';
} else {
return 'normal';
}
};
// Check if element uses center-offset positioning
export const usesCenterPositioning = (elementType) => {
return elementType === 'video' || elementType === 'image';
};
// Helper function to get the visual bounds of an element accounting for offsetX
export const getVisualBounds = (element, x, y, width, height) => {
// Check if element has offsetX property
if (element.offsetX !== undefined) {
// For elements with offsetX, calculate the visual position
const visualLeft = x - element.offsetX;
const elementWidth = element.fixedWidth || width;
return {
left: visualLeft,
top: y,
right: visualLeft + elementWidth,
bottom: y + height,
width: elementWidth,
height: height,
centerX: visualLeft + elementWidth / 2,
centerY: y + height / 2,
};
} else if (usesCenterPositioning(element.type)) {
// For center-positioned elements (video/image)
const left = x - width / 2;
const top = y - height / 2;
return {
left: left,
top: top,
right: left + width,
bottom: top + height,
width: width,
height: height,
centerX: x,
centerY: y,
};
} else {
// For regular top-left positioned elements
return {
left: x,
top: y,
right: x + width,
bottom: y + height,
width: width,
height: height,
centerX: x + width / 2,
centerY: y + height / 2,
};
}
};
// Check if position should snap to center and calculate guide lines
export const calculateSnapAndGuides = (elementId, newX, newY, width, height, timelineElements, dimensions) => {
const centerX = dimensions.width / 2;
const centerY = dimensions.height / 2;
const element = timelineElements.find((el) => el.id === elementId);
if (!element)
return {
x: newX,
y: newY,
guideLines: {
vertical: null,
horizontal: null,
showVertical: false,
showHorizontal: false,
},
};
// Get visual bounds accounting for offsetX and fixedWidth
const visualBounds = getVisualBounds(element, newX, newY, width, height);
let snapX = newX;
let snapY = newY;
let showVertical = false;
let showHorizontal = false;
let verticalLine = null;
let horizontalLine = null;
// Check vertical center snap using visual center
if (Math.abs(visualBounds.centerX - centerX) < POSITION_SNAP_THRESHOLD) {
if (element.offsetX !== undefined) {
// For elements with offsetX, calculate the x position that will center the visual bounds
const targetVisualLeft = centerX - visualBounds.width / 2;
snapX = targetVisualLeft + element.offsetX;
} else if (usesCenterPositioning(element.type)) {
snapX = centerX;
} else {
snapX = centerX - width / 2;
}
showVertical = true;
verticalLine = centerX;
}
// Check horizontal center snap using visual center
if (Math.abs(visualBounds.centerY - centerY) < POSITION_SNAP_THRESHOLD) {
if (usesCenterPositioning(element.type)) {
snapY = centerY;
} else {
snapY = centerY - height / 2;
}
showHorizontal = true;
horizontalLine = centerY;
}
return {
x: snapX,
y: snapY,
guideLines: {
vertical: verticalLine,
horizontal: horizontalLine,
showVertical,
showHorizontal,
},
};
};