This commit is contained in:
ct
2025-06-16 23:16:10 +08:00
parent 4220709b57
commit ef2871a983
6 changed files with 142 additions and 19 deletions

View File

@@ -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">

View File

@@ -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}`;
});

View File

@@ -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)}