Files
TrueGrowth/docs/TOOL_TITLEBAR_DRAGGING.md

6.9 KiB
Raw Blame History

工具标题栏拖动功能

问题描述

画布上的工具是通过 iframe 方式插入的,由于画布上的元素支持拖动,导致 iframe 内的页面无法正常点击和滚动。

解决方案

添加一个工具标题栏将拖动功能只应用在标题栏上iframe 页面可以正常操作。

实现细节

1. 标题栏结构 (tool.generator.ts)

添加了一个标题栏组件,包含以下元素:

┌─────────────────────────────────────────────┐
│ 🔧 工具名称                         [↻]    │  ← 标题栏(可拖动)
├─────────────────────────────────────────────┤
│                                             │
│          iframe 内容区域                    │  ← 可交互区域
│        (可点击、滚动)                      │
│                                             │
└─────────────────────────────────────────────┘

标题栏功能:

  • 工具图标 (🔧): 视觉标识
  • 工具名称: 显示工具的名称(来自 element.metadata.name
  • 刷新按钮 (↻): 重新加载 iframe

2. 拖动逻辑修改

原来的逻辑(有问题):

// 整个工具元素都可拖动
cursor: move;
iframe.style.pointerEvents = 'none'; // iframe 无法交互

新的逻辑(修复后):

// 只有标题栏可拖动
titleBar.style.cursor = 'move';
iframe.style.pointerEvents = 'auto'; // iframe 可以正常交互

// 碰撞检测只在标题栏和边缘生效
function isHitToolElement(element, point) {
  const inTitleBar = y >= rect.y && y <= rect.y + 36;
  const nearEdge = /* 边缘检测 */;
  return inTitleBar || nearEdge;
}

3. 关键修改点

tool.generator.ts - 创建标题栏

// 容器改为 flex 布局
container.style.cssText = `
  display: flex;
  flex-direction: column;
  ...
`;

// 添加标题栏36px 高)
const titleBar = this.createTitleBar(element);
titleBar.setAttribute('data-draggable', 'true');
titleBar.style.cursor = 'move';

// iframe 启用交互
iframe.style.pointerEvents = 'auto';

with-tool.ts - 修改碰撞检测

function isHitToolElement(element: PlaitTool, point: Point): boolean {
  const TITLEBAR_HEIGHT = 36;
  const EDGE_THRESHOLD = 8;

  // 只有标题栏或边缘才算命中
  const inTitleBar = y >= rect.y && y <= rect.y + TITLEBAR_HEIGHT;
  const nearEdge = /* ... */;

  return inTitleBar || nearEdge;
}

tool.component.scss - 样式更新

.plait-tool-titlebar {
  height: 36px;
  cursor: move;
  background: linear-gradient(180deg, #f5f5f5 0%, #ebebeb 100%);

  &:hover {
    background: linear-gradient(180deg, #f8f8f8 0%, #efefef 100%);
  }
}

.plait-tool-content iframe {
  pointer-events: auto; // 确保可交互
}

功能特性

1. 拖动功能

  • 点击标题栏可以拖动整个工具
  • 点击边缘可以调整大小resize
  • 点击 iframe 内容不会触发拖动

2. 标题栏按钮

刷新按钮 (↻)

  • 重新加载 iframe
  • 用于刷新工具内容

3. 视觉反馈

普通状态

  • 浅灰色渐变背景
  • cursor: move 指示可拖动

Hover 状态

  • 背景变亮
  • 按钮显示 hover 效果

选中状态

  • 标题栏变为浅蓝色
  • 边框高亮显示

拖动状态

  • cursor: grabbing
  • 阴影增强

交互行为

iframe 内容区域

  • 可以点击:链接、按钮等元素正常响应
  • 可以滚动:鼠标滚轮、触摸滚动都正常
  • 可以选择文本:可以复制内容
  • 可以输入:表单输入框正常工作

标题栏区域

  • 可以拖动:鼠标按下并移动可拖动工具
  • 按钮可点击:刷新、关闭按钮正常工作
  • 不能选择文本user-select: none 避免误选

边缘区域

  • 可以调整大小:拖动边缘调整工具尺寸
  • 显示 resize 光标:根据位置显示对应光标

代码结构

packages/drawnix/src/
├── components/tool-element/
│   ├── tool.generator.ts           # 修改:添加标题栏创建逻辑
│   ├── tool.component.ts           # 无修改
│   └── tool.component.scss         # 修改:添加标题栏样式
└── plugins/
    └── with-tool.ts                # 修改:碰撞检测逻辑

测试方法

1. 拖动测试

1. 在画布上插入一个工具
2. 点击标题栏并拖动 ✅ 应该可以移动
3. 点击 iframe 内容 ❌ 不应该触发拖动

2. iframe 交互测试

1. 在画布上插入一个工具(如 posemaniacs
2. 点击 iframe 内的链接 ✅ 应该跳转
3. 滚动 iframe 内容 ✅ 应该正常滚动
4. 选择 iframe 内文本 ✅ 应该可以选择

3. 测试方法

1. 点击刷新按钮 ✅ iframe 应该重新加载
2. 按钮 hover ✅ 应该显示背景高亮

4. 调整大小测试

1. 移动鼠标到工具边缘
2. 应该显示 resize 光标
3. 拖动边缘调整大小 ✅ 应该正常工作

兼容性

  • Chrome/Edge: 完全支持
  • Firefox: 完全支持
  • Safari: 完全支持
  • 移动浏览器: 触摸拖动支持

性能优化

1. 事件委托

  • 按钮事件使用 stopPropagation() 避免冒泡
  • 减少事件监听器数量

2. CSS 优化

  • 使用 CSS 渐变而非图片
  • 硬件加速的 transform 动画
  • 最小化重排/重绘

3. 内存管理

  • 组件销毁时移除事件监听
  • 及时清理 iframe 引用

已知限制

1. 标题栏高度固定

  • 当前固定为 36px
  • 未来可配置

2. 最小化功能未实现

  • 代码中已预留接口
  • 可在后续版本实现

3. 标题栏自定义

  • 当前样式固定
  • 未来可支持主题定制

未来改进

短期

  • 支持双击标题栏最大化/还原
  • 支持标题栏右键菜单
  • 支持拖拽标题栏到边缘吸附

中期

  • 支持最小化到托盘
  • 支持多窗口管理
  • 支持工具间通信

长期

  • 支持标题栏主题定制
  • 支持自定义标题栏按钮
  • 支持标题栏插件系统

相关文件

  • packages/drawnix/src/components/tool-element/tool.generator.ts - 标题栏创建
  • packages/drawnix/src/components/tool-element/tool.component.scss - 标题栏样式
  • packages/drawnix/src/plugins/with-tool.ts - 拖动逻辑
  • packages/drawnix/src/types/toolbox.types.ts - 类型定义

总结

通过添加标题栏并修改碰撞检测逻辑,成功解决了工具拖动与 iframe 交互冲突的问题:

拖动功能:保留在标题栏和边缘 iframe 交互:完全恢复,可点击可滚动 用户体验:符合桌面应用习惯 代码质量:结构清晰,易于维护