Files
xianyan/lib/editor/snap_helper.dart
Developer f91be94e9c refactor: 完成项目架构重构,统一模块导入路径
- 清理大量废弃的 barrel 导出文件,移除冗余的中间导出层
- 修复所有相对路径导入错误,统一调整为扁平化模块引用
- 更新多平台 pubspec 版本号与依赖库版本
- 补充后端功能问题管理后台与脚本工具
- 调整部分页面的快捷方式文案适配新功能
- 更新部分翻译覆盖率与API文档
2026-06-12 08:53:57 +08:00

167 lines
4.0 KiB
Dart

// ============================================================
// 闲言APP — 智能对齐吸附辅助类
// 创建时间: 2026-04-20
// 更新时间: 2026-06-12
// 作用: 拖拽时自动吸附到边缘/中心线/其他图层边界
// 上次更新: 从 utils/ 子目录上移至 editor/ 目录
// ============================================================
import 'package:xianyan/editor/models/editor_models.dart';
/// 图层矩形 (统一表示)
class LayerRect {
const LayerRect({
required this.x,
required this.y,
required this.width,
required this.height,
});
final double x;
final double y;
final double width;
final double height;
double get centerX => x + width / 2;
double get centerY => y + height / 2;
double get right => x + width;
double get bottom => y + height;
}
/// 从 TextLayer 估算矩形
LayerRect textLayerRect(TextLayer l, double canvasW, double canvasH) {
final w = l.fontSize * l.text.length * 0.6 * l.scale;
final h = l.fontSize * l.lineHeight * l.scale;
return LayerRect(
x: l.offsetX * canvasW,
y: l.offsetY * canvasH,
width: w,
height: h,
);
}
/// 从 GlassCardLayer 计算矩形
LayerRect glassLayerRect(GlassCardLayer l) {
return LayerRect(
x: l.offsetX,
y: l.offsetY,
width: l.width * l.scale,
height: l.height * l.scale,
);
}
/// 从 StickerLayer 计算矩形
LayerRect stickerLayerRect(StickerLayer l) {
return LayerRect(
x: l.offsetX,
y: l.offsetY,
width: l.size * l.scale,
height: l.size * l.scale,
);
}
/// 吸附结果
class SnapResult {
const SnapResult({
required this.snappedX,
required this.snappedY,
this.guidesX = const [],
this.guidesY = const [],
});
final double snappedX;
final double snappedY;
final List<double> guidesX;
final List<double> guidesY;
bool get isSnapped => guidesX.isNotEmpty || guidesY.isNotEmpty;
}
/// 智能对齐辅助类
class SnapHelper {
static const double snapThreshold = 12.0;
/// 计算吸附后的偏移量 (支持所有图层类型)
static SnapResult calculateSnap({
required double rawX,
required double rawY,
required double layerW,
required double layerH,
required double canvasW,
required double canvasH,
required List<LayerRect> otherLayers,
}) {
final guidesX = <double>[];
final guidesY = <double>[];
double snappedX = rawX;
double snappedY = rawY;
final xTargets = <double>[
0.0,
(canvasW - layerW) / 2,
canvasW - layerW,
];
for (final l in otherLayers) {
xTargets.addAll([l.x, l.centerX, l.right]);
}
for (final target in xTargets) {
if ((rawX - target).abs() < snapThreshold) {
snappedX = target;
guidesX.add(target + layerW / 2);
break;
}
}
final yTargets = <double>[
0.0,
(canvasH - layerH) / 2,
canvasH - layerH,
];
for (final l in otherLayers) {
yTargets.addAll([l.y, l.centerY, l.bottom]);
}
for (final target in yTargets) {
if ((rawY - target).abs() < snapThreshold) {
snappedY = target;
guidesY.add(target + layerH / 2);
break;
}
}
return SnapResult(
snappedX: snappedX,
snappedY: snappedY,
guidesX: guidesX,
guidesY: guidesY,
);
}
/// 从画布模型构建所有图层矩形列表 (排除指定ID)
static List<LayerRect> buildLayerRects(
QuoteCanvasModel canvas, {
String? excludeId,
}) {
final rects = <LayerRect>[];
final cw = canvas.canvasWidth;
final ch = canvas.canvasHeight;
for (final l in canvas.textLayers) {
if (l.id == excludeId || !l.visible) continue;
rects.add(textLayerRect(l, cw, ch));
}
for (final l in canvas.glassCardLayers) {
if (l.id == excludeId || !l.visible) continue;
rects.add(glassLayerRect(l));
}
for (final l in canvas.stickerLayers) {
if (l.id == excludeId || !l.visible) continue;
rects.add(stickerLayerRect(l));
}
return rects;
}
}