197 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 视频编辑连接线统一配置
* 确保所有连接线组件使用一致的视觉参数和几何计算
*/
/**
* 统一的连接线视觉样式配置
*/
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<string, any>;
animate: Record<string, any>;
transition: Record<string, any>;
};
arrow: {
initial: Record<string, any>;
animate: Record<string, any>;
transition: Record<string, any>;
};
}
/**
* 获取统一的动画配置
*/
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
}
}
};
}