/** * 视频编辑连接线统一配置 * 确保所有连接线组件使用一致的视觉参数和几何计算 */ /** * 统一的连接线视觉样式配置 */ export const CONNECTION_STYLE = { // 颜色配置 color: 'rgba(255, 255, 255, 0.9)', // 统一的白色,确保在深色背景下清晰可见 strokeWidth: 2, // 统一的线条粗细 dashArray: '8,4', // 统一的虚线样式:8px实线,4px间隔 // 阴影效果 dropShadow: 'drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.8))', // 动画配置 animation: { pathDuration: 0.6, pathEasing: 'easeInOut', opacityDuration: 0.3, arrowDelay: 0.4, arrowDuration: 0.3, springConfig: { stiffness: 300, damping: 25 } } } as const; /** * 统一的箭头几何参数 */ export const ARROW_GEOMETRY = { size: 8, // 箭头长度 halfHeight: 4, // 箭头半高(宽度的一半) centerOffset: 0.6 // 连接线连接到箭头的位置比例(0.6表示稍微向前偏移) } as const; /** * 弧线计算参数 */ export const CURVE_CONFIG = { curvature: 0.3, // 弧线弯曲程度 minControlOffset: 10, // 最小控制点偏移 maxControlOffset: 60 // 最大控制点偏移 } as const; /** * 计算统一的箭头几何形状 */ export function calculateArrowGeometry( startPoint: { x: number; y: number }, endPoint: { x: number; y: number } ) { // 计算连接方向向量 const dx = endPoint.x - startPoint.x; const dy = endPoint.y - startPoint.y; const distance = Math.sqrt(dx * dx + dy * dy); const normalizedDx = dx / distance; const normalizedDy = dy / distance; // 箭头几何计算 const arrowTip = { x: endPoint.x, y: endPoint.y }; const arrowBase = { x: endPoint.x - normalizedDx * ARROW_GEOMETRY.size, y: endPoint.y - normalizedDy * ARROW_GEOMETRY.size }; const arrowCenter = { x: endPoint.x - normalizedDx * (ARROW_GEOMETRY.size * ARROW_GEOMETRY.centerOffset), y: endPoint.y - normalizedDy * (ARROW_GEOMETRY.size * ARROW_GEOMETRY.centerOffset) }; // 计算垂直向量用于箭头宽度 const perpX = -normalizedDy; const perpY = normalizedDx; const arrowPoints = [ arrowTip, { x: arrowBase.x + perpX * ARROW_GEOMETRY.halfHeight, y: arrowBase.y + perpY * ARROW_GEOMETRY.halfHeight }, { x: arrowBase.x - perpX * ARROW_GEOMETRY.halfHeight, y: arrowBase.y - perpY * ARROW_GEOMETRY.halfHeight } ]; return { tip: arrowTip, base: arrowBase, center: arrowCenter, points: arrowPoints, direction: { dx: normalizedDx, dy: normalizedDy }, perpendicular: { perpX, perpY }, distance }; } /** * 计算统一的弧线路径 */ export function calculateCurvePath( startPoint: { x: number; y: number }, endPoint: { x: number; y: number }, containerSize: { width: number; height: number } ): string { const dx = endPoint.x - startPoint.x; const dy = endPoint.y - startPoint.y; const distance = Math.sqrt(dx * dx + dy * dy); // 计算控制点,创建优雅的弧线 const midX = (startPoint.x + endPoint.x) / 2; const midY = (startPoint.y + endPoint.y) / 2; let controlX = midX; let controlY = midY; // 根据方向调整控制点 if (Math.abs(dx) > Math.abs(dy)) { controlY = midY + (dy > 0 ? -1 : 1) * distance * CURVE_CONFIG.curvature; } else { controlX = midX + (dx > 0 ? -1 : 1) * distance * CURVE_CONFIG.curvature; } // 确保控制点在容器范围内 controlX = Math.max(CURVE_CONFIG.minControlOffset, Math.min(containerSize.width - CURVE_CONFIG.minControlOffset, controlX)); controlY = Math.max(CURVE_CONFIG.minControlOffset, Math.min(containerSize.height - CURVE_CONFIG.minControlOffset, controlY)); // 创建二次贝塞尔曲线路径 return `M ${startPoint.x} ${startPoint.y} Q ${controlX} ${controlY} ${endPoint.x} ${endPoint.y}`; } /** * 动画配置类型定义 */ export interface ConnectionAnimationConfig { line: { initial: Record; animate: Record; transition: Record; }; arrow: { initial: Record; animate: Record; transition: Record; }; } /** * 获取统一的动画配置 */ export function getConnectionAnimationConfig(animated: boolean = true): ConnectionAnimationConfig { if (!animated) { return { line: { initial: {}, animate: {}, transition: {} }, arrow: { initial: {}, animate: {}, transition: {} } }; } return { line: { initial: { pathLength: 0, opacity: 0 }, animate: { pathLength: 1, opacity: 1 }, transition: { pathLength: { duration: CONNECTION_STYLE.animation.pathDuration, ease: CONNECTION_STYLE.animation.pathEasing as any }, opacity: { duration: CONNECTION_STYLE.animation.opacityDuration } } }, arrow: { initial: { scale: 0, opacity: 0 }, animate: { scale: 1, opacity: 1 }, transition: { delay: CONNECTION_STYLE.animation.arrowDelay, duration: CONNECTION_STYLE.animation.arrowDuration, type: "spring" as const, ...CONNECTION_STYLE.animation.springConfig } } }; }