Update
This commit is contained in:
@@ -518,13 +518,17 @@ const VideoEditor = ({ width, height }) => {
|
||||
emitter.on('video-play', handlePlay);
|
||||
emitter.on('video-reset', handleReset);
|
||||
emitter.on('video-seek', handleSeek);
|
||||
emitter.on('text-update', ({ elementId, updates }) => {
|
||||
handleElementUpdate(elementId, updates);
|
||||
});
|
||||
|
||||
return () => {
|
||||
emitter.off('video-play', handlePlay);
|
||||
emitter.off('video-reset', handleReset);
|
||||
emitter.off('video-seek', handleSeek);
|
||||
emitter.off('text-update');
|
||||
};
|
||||
}, [emitter, handlePlay, handleReset, handleSeek]);
|
||||
}, [emitter, handlePlay, handleReset, handleSeek, handleElementUpdate]);
|
||||
|
||||
return (
|
||||
<div style={{ width: dimensions.width, height: dimensions.height }} className="rounded-3xl">
|
||||
|
||||
@@ -98,15 +98,18 @@ const useVideoExport = ({ timelineElements, dimensions, totalDuration }) => {
|
||||
|
||||
showConsoleLogs && console.log('🎵 Audio args:', audioArgs);
|
||||
|
||||
// Process text elements with centering
|
||||
texts.forEach((t, i) => {
|
||||
const escapedText = t.text.replace(/'/g, is_string ? "\\'" : "'").replace(/:/g, '\\:');
|
||||
|
||||
// Center the text: x position is the center point, y is adjusted for baseline
|
||||
const centerX = Math.round(t.x);
|
||||
const centerY = Math.round(t.y + t.fontSize * 0.3); // Adjust for text baseline
|
||||
|
||||
filters.push(
|
||||
`[${videoLayer}]drawtext=fontfile=/arial.ttf:text='${escapedText}':x=${Math.round(
|
||||
t.x,
|
||||
)}:y=${Math.round(t.y)}:fontsize=${t.fontSize}:fontcolor=${t.fill}:borderw=${t.strokeWidth}:bordercolor=${
|
||||
`[${videoLayer}]drawtext=fontfile=/arial.ttf:text='${escapedText}':x=${centerX}:y=${centerY}:fontsize=${t.fontSize}:fontcolor=${t.fill}:borderw=${t.strokeWidth}:bordercolor=${
|
||||
t.stroke
|
||||
}:enable='between(t,${t.startTime},${t.startTime + t.duration})'[t${i}]`,
|
||||
}:text_align=center:enable='between(t,${t.startTime},${t.startTime + t.duration})'[t${i}]`,
|
||||
);
|
||||
videoLayer = `t${i}`;
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useMitt } from '@/plugins/MittContext';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Image, Layer, Line, Stage, Text, Transformer } from 'react-konva';
|
||||
|
||||
@@ -36,6 +37,8 @@ const VideoPreview = ({
|
||||
// Refs
|
||||
layerRef,
|
||||
}) => {
|
||||
const emitter = useMitt();
|
||||
|
||||
// Selection state
|
||||
const [selectedElementId, setSelectedElementId] = useState(null);
|
||||
const transformerRef = useRef(null);
|
||||
@@ -116,16 +119,28 @@ const VideoPreview = ({
|
||||
};
|
||||
|
||||
// Handle element selection
|
||||
const handleElementSelect = useCallback((elementId) => {
|
||||
setSelectedElementId(elementId);
|
||||
// Clear guide lines when selecting
|
||||
setGuideLines({
|
||||
vertical: null,
|
||||
horizontal: null,
|
||||
showVertical: false,
|
||||
showHorizontal: false,
|
||||
});
|
||||
}, []);
|
||||
const handleElementSelect = useCallback(
|
||||
(elementId) => {
|
||||
setSelectedElementId(elementId);
|
||||
|
||||
// Find the selected element
|
||||
const element = timelineElements.find((el) => el.id === elementId);
|
||||
|
||||
// If it's a text element, emit text-element-selected event
|
||||
if (element && element.type === 'text') {
|
||||
emitter.emit('text-element-selected', element);
|
||||
}
|
||||
|
||||
// Clear guide lines when selecting
|
||||
setGuideLines({
|
||||
vertical: null,
|
||||
horizontal: null,
|
||||
showVertical: false,
|
||||
showHorizontal: false,
|
||||
});
|
||||
},
|
||||
[emitter, timelineElements],
|
||||
);
|
||||
|
||||
// Handle clicking on empty space to deselect
|
||||
const handleStageClick = useCallback((e) => {
|
||||
@@ -434,6 +449,10 @@ const VideoPreview = ({
|
||||
stroke={element.stroke}
|
||||
strokeWidth={element.strokeWidth}
|
||||
rotation={element.rotation || 0}
|
||||
// Center the text horizontally
|
||||
align="center"
|
||||
// Let text have natural width and height for multiline support
|
||||
wrap="word"
|
||||
draggable
|
||||
dragBoundFunc={createDragBoundFunc(element.id)}
|
||||
onClick={() => handleElementSelect(element.id)}
|
||||
|
||||
Reference in New Issue
Block a user