6、机械/电气工程CAD设计组件 - /设计与仿真组件/engineering-cad-designer

76个工业组件库示例汇总

机械/电气工程CAD设计组件

这是一个交互式的二维CAD设计工具,专为机械和电气工程图纸绘制设计。该组件支持自适应布局,提供了丰富的绘图工具和机床结构元素库。

功能特点

  • 多种绘图工具:直线、矩形、圆形、弧线、多边形、文本等基本图形绘制
  • 尺寸标注:支持长度测量和尺寸标注功能
  • 机床组件库:内置常用的机床结构组件,如床身、立柱、主轴箱等
  • 属性编辑:可以实时修改所有图形和组件的属性
  • 缩放和平移:支持画布的缩放和平移,方便查看大型图纸
  • 网格辅助:可调整网格大小,辅助精确绘图
  • 可导出设计:将设计导出为PNG图片格式
  • 自适应布局:适应不同屏幕尺寸和设备

使用方法

基本操作

  1. 选择工具:点击顶部工具栏中的绘图工具(如直线、矩形等)
  2. 绘制图形:在画布上点击并拖动鼠标来绘制图形
  3. 选择对象:点击"选择"工具,然后点击画布上的对象进行选择
  4. 移动对象:选中对象后,拖动它可以改变位置
  5. 编辑属性:选中对象后,右侧属性面板会显示该对象的属性,可以直接修改
  6. 删除对象:选中对象后,点击属性面板中的"删除对象"按钮

使用机床组件

  1. 浏览组件库:在右侧面板的"机床组件库"中查看可用组件
  2. 添加组件:将组件从库中拖放到画布上
  3. 调整大小和位置:通过属性面板或直接拖动来调整组件

视图控制

  • 平移画布:在选择工具模式下,拖动空白区域可以平移画布
  • 缩放画布:使用鼠标滚轮或点击右上角的缩放按钮
  • 重置视图:点击右上角的"重置视图"按钮返回默认视图

导出设计

点击顶部导航栏中的"导出设计"按钮,将当前绘图保存为PNG图片。

自定义配置

样式设置

CAD设计器的颜色主题和样式可以通过修改CSS变量来自定义:

:root {
  --background: #F5F5F7;      /* 背景色 */
  --card-bg: #FFFFFF;         /* 面板背景色 */
  --primary-text: #1D1D1F;    /* 主文本颜色 */
  --secondary-text: #86868B;  /* 次要文本颜色 */
  --accent-blue: #0066CC;     /* 强调色(蓝) */
  --border-color: #D2D2D7;    /* 边框颜色 */
  --grid-color: #E8E8ED;      /* 网格线颜色 */
}

扩展机床组件库

可以通过编辑JavaScript代码中的ShapeFactory对象来添加新的机床组件类型:

'new-component': (x, y, options = {}) => ({
  type: 'machine-component',
  componentType: 'new-component',
  x,
  y,
  width: 100,   // 组件默认宽度
  height: 50,   // 组件默认高度
  strokeColor: options.strokeColor || config.strokeColor,
  lineWidth: options.lineWidth || config.lineWidth,
  id: Date.now().toString(),
})

然后在HTML中的组件库中添加对应的条目:

<div class="cd-component-item" draggable="true" data-type="new-component">新组件名称</div>

技术细节

该CAD设计工具基于HTML5 Canvas实现,使用了以下技术:

  • HTML5 Canvas API用于绘图功能
  • 纯JavaScript实现,无依赖
  • CSS Grid和Flexbox用于响应式布局
  • CSS变量用于主题定制
  • 拖放API用于组件库交互

未来计划

  • 添加保存和加载设计功能
  • 支持图层管理
  • 增加更多机床组件类型
  • 实现尺寸自动约束功能
  • 添加协作编辑功能

项目结构

在这里插入图片描述

效果展示

在这里插入图片描述

源码

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>机械/电气工程CAD设计工具</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div id="cad-designer">
    <!-- 顶部导航栏 -->
    <div class="cd-header">
      <div class="cd-logo">
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <path d="M2 12h20M2 12a10 10 0 0 1 20 0M2 12a10 10 0 0 0 20 0M14 6l-4 12"></path>
        </svg>
        <span>机床CAD设计器</span>
      </div>
      <div class="cd-actions">
        <button class="cd-btn cd-btn-primary" id="exportBtn">导出设计</button>
        <button class="cd-btn" id="clearBtn">清空画布</button>
      </div>
    </div>

    <!-- 主内容区域 -->
    <div class="cd-content">
      <!-- 工具栏 -->
      <div class="cd-toolbar">
        <div class="cd-toolbar-group">
          <button class="cd-tool-btn active" data-tool="select">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M3 3l7 7m0 0v-6m0 6h-6"></path>
            </svg>
            选择
          </button>
          <button class="cd-tool-btn" data-tool="line">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M5 12h14"></path>
            </svg>
            直线
          </button>
          <button class="cd-tool-btn" data-tool="rectangle">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <rect x="3" y="3" width="18" height="18" rx="2"></rect>
            </svg>
            矩形
          </button>
          <button class="cd-tool-btn" data-tool="circle">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <circle cx="12" cy="12" r="10"></circle>
            </svg>
            圆形
          </button>
          <button class="cd-tool-btn" data-tool="text">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M4 7V4h16v3"></path>
              <path d="M9 20h6"></path>
              <path d="M12 4v16"></path>
            </svg>
            文本
          </button>
        </div>
        <div class="cd-toolbar-group">
          <button class="cd-tool-btn" data-tool="dimension">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M3 6h18"></path>
              <path d="M3 12h18"></path>
              <path d="M3 18h18"></path>
            </svg>
            标注
          </button>
          <button class="cd-tool-btn" data-tool="arc">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M3 12a9 9 0 1 0 18 0"></path>
            </svg>
            弧线
          </button>
          <button class="cd-tool-btn" data-tool="polygon">
            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <path d="M12 2L2 7l10 5 10-5-10-5z"></path>
              <path d="M2 17l10 5 10-5"></path>
              <path d="M2 12l10 5 10-5"></path>
            </svg>
            多边形
          </button>
        </div>
        <div class="cd-toolbar-group">
          <select class="cd-select" id="lineWidth">
            <option value="1">线宽: 1px</option>
            <option value="2" selected>线宽: 2px</option>
            <option value="3">线宽: 3px</option>
            <option value="4">线宽: 4px</option>
          </select>
          <div class="cd-color-picker">
            <span>颜色:</span>
            <input type="color" id="strokeColor" value="#1D1D1F">
          </div>
        </div>
      </div>
      
      <!-- 主面板区域 -->
      <div class="cd-main-panels">
        <!-- 绘图区域 -->
        <div class="cd-panel cd-drawing-panel">
          <div class="cd-panel-header">
            <h2>机床结构设计</h2>
            <div class="cd-panel-actions">
              <button class="cd-tool-btn" id="zoomIn">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <circle cx="11" cy="11" r="8"></circle>
                  <path d="M21 21l-4.3-4.3"></path>
                  <path d="M8 11h6"></path>
                  <path d="M11 8v6"></path>
                </svg>
              </button>
              <button class="cd-tool-btn" id="zoomOut">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <circle cx="11" cy="11" r="8"></circle>
                  <path d="M21 21l-4.3-4.3"></path>
                  <path d="M8 11h6"></path>
                </svg>
              </button>
              <button class="cd-tool-btn" id="resetView">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                  <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                  <polyline points="9 22 9 12 15 12 15 22"></polyline>
                </svg>
              </button>
            </div>
          </div>
          <div class="cd-panel-body">
            <div class="cd-canvas-container">
              <canvas id="cadCanvas"></canvas>
            </div>
          </div>
        </div>
        
        <!-- 右侧面板 -->
        <div class="cd-side-panels">
          <!-- 属性面板 -->
          <div class="cd-panel cd-properties-panel">
            <div class="cd-panel-header">
              <h2>属性</h2>
            </div>
            <div class="cd-panel-body">
              <div class="cd-properties-list" id="propertiesPanel">
                <div class="cd-empty-state">
                  请选择一个对象以查看其属性
                </div>
              </div>
            </div>
          </div>
          
          <!-- 机床组件库 -->
          <div class="cd-panel cd-components-panel">
            <div class="cd-panel-header">
              <h2>机床组件库</h2>
              <div class="cd-panel-actions">
                <button class="cd-tool-btn" id="collapseAll">
                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                    <polyline points="4 14 10 14 10 20"></polyline>
                    <polyline points="20 10 14 10 14 4"></polyline>
                    <line x1="14" y1="10" x2="21" y2="3"></line>
                    <line x1="3" y1="21" x2="10" y2="14"></line>
                  </svg>
                </button>
              </div>
            </div>
            <div class="cd-panel-body">
              <div class="cd-search-bar">
                <input type="text" class="cd-search-input" placeholder="搜索组件..." id="componentSearch">
              </div>
              <div class="cd-components-tree">
                <div class="cd-component-category">
                  <div class="cd-category-header">
                    <span class="cd-expand-icon"></span>
                    机床主体
                  </div>
                  <div class="cd-category-items">
                    <div class="cd-component-item" draggable="true" data-type="machine-bed">床身</div>
                    <div class="cd-component-item" draggable="true" data-type="machine-column">立柱</div>
                    <div class="cd-component-item" draggable="true" data-type="machine-spindle">主轴箱</div>
                  </div>
                </div>
                
                <div class="cd-component-category">
                  <div class="cd-category-header">
                    <span class="cd-expand-icon"></span>
                    运动部件
                  </div>
                  <div class="cd-category-items">
                    <div class="cd-component-item" draggable="true" data-type="linear-guide">直线导轨</div>
                    <div class="cd-component-item" draggable="true" data-type="ball-screw">滚珠丝杠</div>
                    <div class="cd-component-item" draggable="true" data-type="servo-motor">伺服电机</div>
                  </div>
                </div>
                
                <div class="cd-component-category">
                  <div class="cd-category-header">
                    <span class="cd-expand-icon"></span>
                    控制部件
                  </div>
                  <div class="cd-category-items">
                    <div class="cd-component-item" draggable="true" data-type="control-panel">控制面板</div>
                    <div class="cd-component-item" draggable="true" data-type="electrical-cabinet">电气柜</div>
                    <div class="cd-component-item" draggable="true" data-type="limit-switch">限位开关</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      
      <!-- 底部状态栏 -->
      <div class="cd-status-bar">
        <div class="cd-coordinates">
          <span id="mouseCoordinates">X: 0, Y: 0</span>
        </div>
        <div class="cd-view-info">
          <span id="zoomLevel">缩放: 100%</span>
        </div>
        <div class="cd-grid-settings">
          <label>
            <input type="checkbox" id="showGrid" checked>
            显示网格
          </label>
          <select class="cd-select cd-select-sm" id="gridSize">
            <option value="10">网格: 10px</option>
            <option value="20" selected>网格: 20px</option>
            <option value="50">网格: 50px</option>
          </select>
        </div>
      </div>
    </div>
  </div>
  
  <!-- 文本编辑模态框 -->
  <div class="cd-modal" id="textModal">
    <div class="cd-modal-content">
      <div class="cd-modal-header">
        <h3>添加文本</h3>
        <button class="cd-modal-close" id="closeTextModal">&times;</button>
      </div>
      <div class="cd-modal-body">
        <div class="cd-form-group">
          <label for="textContent">文本内容</label>
          <input type="text" class="cd-input" id="textContent" placeholder="输入文本内容...">
        </div>
        <div class="cd-form-group">
          <label for="fontSize">字体大小</label>
          <select class="cd-select" id="fontSize">
            <option value="12">12px</option>
            <option value="14" selected>14px</option>
            <option value="16">16px</option>
            <option value="18">18px</option>
            <option value="20">20px</option>
          </select>
        </div>
      </div>
      <div class="cd-modal-footer">
        <button class="cd-btn" id="cancelTextBtn">取消</button>
        <button class="cd-btn cd-btn-primary" id="confirmTextBtn">确认</button>
      </div>
    </div>
  </div>
  
  <script src="script.js"></script>
</body>
</html> 

styles.css

/* 机械/电气工程CAD设计工具 - 苹果科技风格 */

:root {
  /* 颜色变量 - 苹果风格 */
  --background: #F5F5F7;
  --card-bg: #FFFFFF;
  --primary-text: #1D1D1F;
  --secondary-text: #86868B;
  --accent-blue: #0066CC;
  --accent-green: #34C759;
  --accent-orange: #FF9500;
  --accent-red: #FF3B30;
  --accent-purple: #5E5CE6;
  --border-color: #D2D2D7;
  --grid-color: #E8E8ED;
  
  /* 阴影 */
  --card-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  --header-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
  
  /* 尺寸变量 */
  --header-height: 60px;
  --toolbar-height: 48px;
  --status-bar-height: 36px;
  --panels-gap: 16px;
  --border-radius: 10px;
  --input-height: 32px;
}

/* 基础样式 */
#cad-designer {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', sans-serif;
  color: var(--primary-text);
  background-color: var(--background);
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
  position: relative;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

#cad-designer * {
  box-sizing: border-box;
}

/* 顶部导航栏 */
.cd-header {
  height: var(--header-height);
  background-color: var(--card-bg);
  border-bottom: 1px solid var(--border-color);
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;
  box-shadow: var(--header-shadow);
  z-index: 10;
}

.cd-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 500;
}

.cd-logo svg {
  color: var(--accent-blue);
}

.cd-actions {
  display: flex;
  gap: 12px;
}

/* 主内容区域 */
.cd-content {
  display: flex;
  flex-direction: column;
  flex: 1;
  height: calc(100% - var(--header-height));
  overflow: hidden;
  position: relative;
}

/* 工具栏 */
.cd-toolbar {
  height: var(--toolbar-height);
  background-color: var(--card-bg);
  border-bottom: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  padding: 0 16px;
  gap: 20px;
  overflow-x: auto;
}

.cd-toolbar-group {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.cd-toolbar-group:not(:last-child) {
  padding-right: 20px;
  border-right: 1px solid var(--border-color);
}

.cd-tool-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 6px;
  border: none;
  background-color: transparent;
  color: var(--primary-text);
  font-size: 13px;
  cursor: pointer;
  transition: all 0.2s;
  white-space: nowrap;
}

.cd-tool-btn:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

.cd-tool-btn.active {
  background-color: rgba(0, 102, 204, 0.1);
  color: var(--accent-blue);
}

.cd-tool-btn svg {
  color: var(--secondary-text);
}

.cd-tool-btn.active svg {
  color: var(--accent-blue);
}

/* 选择器样式 */
.cd-select {
  height: var(--input-height);
  padding: 0 12px;
  border-radius: 6px;
  border: 1px solid var(--border-color);
  background-color: var(--card-bg);
  color: var(--primary-text);
  font-size: 13px;
  outline: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2386868B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}

.cd-select:hover {
  border-color: var(--secondary-text);
}

.cd-select:focus {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}

.cd-select-sm {
  height: 28px;
  font-size: 12px;
  padding: 0 8px;
  padding-right: 28px;
}

/* 颜色选择器样式 */
.cd-color-picker {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}

.cd-color-picker input[type="color"] {
  width: 28px;
  height: 28px;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  padding: 0;
  cursor: pointer;
}

/* 主面板区域 - 使用Grid布局 */
.cd-main-panels {
  display: grid;
  grid-template-columns: 1fr 320px;
  grid-gap: var(--panels-gap);
  padding: var(--panels-gap);
  flex: 1;
  overflow: hidden;
  height: calc(100% - var(--toolbar-height) - var(--status-bar-height));
}

/* 绘图面板样式 */
.cd-drawing-panel {
  height: 100%;
}

/* 面板样式 */
.cd-panel {
  background-color: var(--card-bg);
  border-radius: var(--border-radius);
  box-shadow: var(--card-shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.cd-panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-color);
}

.cd-panel-header h2 {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
}

.cd-panel-actions {
  display: flex;
  gap: 8px;
}

.cd-panel-body {
  flex: 1;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* 右侧面板布局 */
.cd-side-panels {
  display: grid;
  grid-template-rows: 1fr 1fr;
  grid-gap: var(--panels-gap);
  overflow: hidden;
}

/* 画布容器 */
.cd-canvas-container {
  width: 100%;
  height: 100%;
  overflow: auto;
  position: relative;
  background-color: var(--card-bg);
}

#cadCanvas {
  position: absolute;
  top: 0;
  left: 0;
  background-color: var(--card-bg);
  background-image: 
    linear-gradient(var(--grid-color) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
  background-size: 20px 20px;
  cursor: crosshair;
}

/* 属性面板 */
.cd-properties-panel {
  display: flex;
  flex-direction: column;
}

.cd-properties-list {
  padding: 12px;
  overflow-y: auto;
}

.cd-property-item {
  margin-bottom: 12px;
}

.cd-property-item label {
  display: block;
  font-size: 12px;
  color: var(--secondary-text);
  margin-bottom: 4px;
}

.cd-property-value {
  width: 100%;
  height: var(--input-height);
  padding: 0 12px;
  border-radius: 6px;
  border: 1px solid var(--border-color);
  font-size: 13px;
  outline: none;
}

.cd-property-value:focus {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}

/* 空状态样式 */
.cd-empty-state {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--secondary-text);
  font-size: 14px;
  text-align: center;
  padding: 20px;
}

/* 组件库面板 */
.cd-components-panel {
  display: flex;
  flex-direction: column;
}

.cd-search-bar {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-color);
}

.cd-search-input {
  width: 100%;
  height: var(--input-height);
  padding: 0 12px;
  border-radius: 6px;
  border: 1px solid var(--border-color);
  background-color: var(--background);
  font-size: 13px;
  outline: none;
}

.cd-search-input:focus {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}

.cd-components-tree {
  overflow-y: auto;
  padding: 8px;
  flex: 1;
}

.cd-component-category {
  margin-bottom: 8px;
}

.cd-category-header {
  padding: 8px 10px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  border-radius: 6px;
  transition: background-color 0.2s;
}

.cd-category-header:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

.cd-expand-icon {
  display: inline-block;
  margin-right: 6px;
  font-size: 10px;
  transition: transform 0.2s;
}

.cd-category-header.collapsed .cd-expand-icon {
  transform: rotate(-90deg);
}

.cd-category-items {
  padding: 4px 0 4px 20px;
}

.cd-component-item {
  padding: 6px 10px;
  font-size: 13px;
  cursor: grab;
  border-radius: 4px;
  transition: background-color 0.2s;
  margin-bottom: 4px;
}

.cd-component-item:hover {
  background-color: rgba(0, 0, 0, 0.05);
}

.cd-component-item:active {
  cursor: grabbing;
}

/* 底部状态栏 */
.cd-status-bar {
  height: var(--status-bar-height);
  background-color: var(--card-bg);
  border-top: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  font-size: 12px;
  color: var(--secondary-text);
}

.cd-grid-settings {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* 按钮样式 */
.cd-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  border-radius: 6px;
  border: 1px solid var(--border-color);
  background-color: var(--card-bg);
  color: var(--primary-text);
  font-size: 14px;
  cursor: pointer;
  transition: all 0.2s;
}

.cd-btn:hover {
  background-color: var(--background);
}

.cd-btn-primary {
  background-color: var(--accent-blue);
  border-color: var(--accent-blue);
  color: white;
}

.cd-btn-primary:hover {
  background-color: #0055B3;
  border-color: #0055B3;
}

/* 模态框样式 */
.cd-modal {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 1000;
  justify-content: center;
  align-items: center;
}

.cd-modal.active {
  display: flex;
}

.cd-modal-content {
  background-color: var(--card-bg);
  border-radius: var(--border-radius);
  width: 90%;
  max-width: 400px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}

.cd-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  border-bottom: 1px solid var(--border-color);
}

.cd-modal-header h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
}

.cd-modal-close {
  background: none;
  border: none;
  font-size: 20px;
  cursor: pointer;
  color: var(--secondary-text);
}

.cd-modal-body {
  padding: 20px;
  overflow-y: auto;
  flex: 1;
}

.cd-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 15px 20px;
  border-top: 1px solid var(--border-color);
}

/* 表单样式 */
.cd-form-group {
  margin-bottom: 16px;
}

.cd-form-group label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  margin-bottom: 6px;
}

.cd-input {
  width: 100%;
  height: var(--input-height);
  padding: 0 12px;
  border-radius: 6px;
  border: 1px solid var(--border-color);
  font-size: 13px;
  outline: none;
}

.cd-input:focus {
  border-color: var(--accent-blue);
  box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}

/* 响应式布局 */
@media (max-width: 992px) {
  .cd-main-panels {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr auto;
  }
  
  .cd-side-panels {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr;
  }
}

@media (max-width: 768px) {
  .cd-side-panels {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
  }
  
  .cd-toolbar {
    overflow-x: auto;
    justify-content: flex-start;
  }
  
  .cd-toolbar-group {
    flex-shrink: 0;
  }
} 

script.js

/**
 * 机械/电气工程CAD设计工具 - JavaScript
 */

// 全局变量
let canvas, ctx;
let canvasWidth = 2000;
let canvasHeight = 1500;
let zoomLevel = 1;
let panOffset = { x: 0, y: 0 };
let isDrawing = false;
let isDragging = false;
let lastX = 0;
let lastY = 0;
let shapes = [];
let selectedShape = null;
let currentTool = 'select';
let tempShape = null;
let gridSize = 20;
let showGrid = true;

// 基本配置和设置
const config = {
  strokeColor: '#1D1D1F',
  lineWidth: 2,
  fontSize: 14,
};

// 形状工厂对象
const ShapeFactory = {
  line: (startX, startY, endX, endY, options = {}) => ({
    type: 'line',
    startX,
    startY,
    endX,
    endY,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
  
  rectangle: (x, y, width, height, options = {}) => ({
    type: 'rectangle',
    x,
    y,
    width,
    height,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
  
  circle: (centerX, centerY, radius, options = {}) => ({
    type: 'circle',
    centerX,
    centerY,
    radius,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),

  text: (x, y, content, options = {}) => ({
    type: 'text',
    x,
    y,
    content: content || '文本',
    fontSize: options.fontSize || config.fontSize,
    strokeColor: options.strokeColor || config.strokeColor,
    id: Date.now().toString(),
  }),
  
  arc: (centerX, centerY, radius, startAngle, endAngle, options = {}) => ({
    type: 'arc',
    centerX,
    centerY,
    radius,
    startAngle,
    endAngle,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
  
  polygon: (points, options = {}) => ({
    type: 'polygon',
    points: [...points], // 点的数组
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
  
  dimension: (startX, startY, endX, endY, options = {}) => ({
    type: 'dimension',
    startX,
    startY,
    endX,
    endY,
    value: Math.round(Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)) * 100) / 100,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
  
  // 预定义机床组件
  'machine-bed': (x, y, options = {}) => ({
    type: 'machine-component',
    componentType: 'machine-bed',
    x,
    y,
    width: 200,
    height: 80,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
  
  'machine-column': (x, y, options = {}) => ({
    type: 'machine-component',
    componentType: 'machine-column',
    x,
    y,
    width: 50,
    height: 120,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
  
  'machine-spindle': (x, y, options = {}) => ({
    type: 'machine-component',
    componentType: 'machine-spindle',
    x,
    y,
    width: 80,
    height: 60,
    strokeColor: options.strokeColor || config.strokeColor,
    lineWidth: options.lineWidth || config.lineWidth,
    id: Date.now().toString(),
  }),
};

// 初始化函数
function init() {
  // 获取Canvas元素和绘图上下文
  canvas = document.getElementById('cadCanvas');
  ctx = canvas.getContext('2d');
  
  // 设置画布尺寸
  canvas.width = canvasWidth;
  canvas.height = canvasHeight;
  
  // 绑定事件监听器
  setupEventListeners();
  
  // 渲染初始画布
  render();
}

// 设置事件监听器
function setupEventListeners() {
  // 鼠标事件
  canvas.addEventListener('mousedown', handleMouseDown);
  canvas.addEventListener('mousemove', handleMouseMove);
  canvas.addEventListener('mouseup', handleMouseUp);
  canvas.addEventListener('wheel', handleMouseWheel);
  
  // 工具按钮点击事件
  document.querySelectorAll('.cd-tool-btn[data-tool]').forEach(button => {
    button.addEventListener('click', function() {
      setActiveTool(this.getAttribute('data-tool'));
    });
  });
  
  // 缩放和视图控制
  document.getElementById('zoomIn').addEventListener('click', () => {
    changeZoom(0.1);
  });
  
  document.getElementById('zoomOut').addEventListener('click', () => {
    changeZoom(-0.1);
  });
  
  document.getElementById('resetView').addEventListener('click', () => {
    zoomLevel = 1;
    panOffset = { x: 0, y: 0 };
    render();
    updateZoomDisplay();
  });
  
  // 线宽和颜色选择器
  document.getElementById('lineWidth').addEventListener('change', function() {
    config.lineWidth = parseInt(this.value);
    updateSelectedShapeProperties();
  });
  
  document.getElementById('strokeColor').addEventListener('input', function() {
    config.strokeColor = this.value;
    updateSelectedShapeProperties();
  });
  
  // 网格设置
  document.getElementById('showGrid').addEventListener('change', function() {
    showGrid = this.checked;
    render();
  });
  
  document.getElementById('gridSize').addEventListener('change', function() {
    gridSize = parseInt(this.value);
    render();
  });
  
  // 导出和清空按钮
  document.getElementById('exportBtn').addEventListener('click', exportDesign);
  document.getElementById('clearBtn').addEventListener('click', clearCanvas);
  
  // 文本模态框相关事件
  document.getElementById('confirmTextBtn').addEventListener('click', confirmTextInput);
  document.getElementById('cancelTextBtn').addEventListener('click', closeTextModal);
  document.getElementById('closeTextModal').addEventListener('click', closeTextModal);
  
  // 组件树折叠/展开事件
  document.querySelectorAll('.cd-category-header').forEach(header => {
    header.addEventListener('click', function() {
      this.classList.toggle('collapsed');
      const items = this.nextElementSibling;
      if (this.classList.contains('collapsed')) {
        items.style.display = 'none';
      } else {
        items.style.display = 'block';
      }
    });
  });
  
  // 组件拖放
  document.querySelectorAll('.cd-component-item').forEach(item => {
    item.addEventListener('dragstart', handleDragStart);
  });
  
  canvas.addEventListener('dragover', handleDragOver);
  canvas.addEventListener('drop', handleDrop);
  
  // 折叠所有按钮
  document.getElementById('collapseAll').addEventListener('click', collapseAllCategories);
  
  // 组件搜索
  document.getElementById('componentSearch').addEventListener('input', filterComponents);
}

// 鼠标事件处理函数
function handleMouseDown(e) {
  const pos = getCanvasCoordinates(e);
  lastX = pos.x;
  lastY = pos.y;
  
  // 先检查是否有选中对象
  if (currentTool === 'select') {
    const clickedShape = findShapeAtPosition(pos.x, pos.y);
    if (clickedShape) {
      selectedShape = clickedShape;
      isDragging = true;
      updatePropertiesPanel();
      render();
      return;
    } else {
      selectedShape = null;
      updatePropertiesPanel();
      render();
    }
    
    // 如果没选中对象,则开始平移画布
    isDragging = true;
    canvas.style.cursor = 'grabbing';
    return;
  }
  
  // 开始绘制
  isDrawing = true;
  const options = {
    strokeColor: config.strokeColor,
    lineWidth: config.lineWidth,
    fontSize: config.fontSize,
  };
  
  // 根据当前工具创建临时形状
  switch (currentTool) {
    case 'line':
      tempShape = ShapeFactory.line(pos.x, pos.y, pos.x, pos.y, options);
      break;
    case 'rectangle':
      tempShape = ShapeFactory.rectangle(pos.x, pos.y, 0, 0, options);
      break;
    case 'circle':
      tempShape = ShapeFactory.circle(pos.x, pos.y, 0, options);
      break;
    case 'text':
      openTextModal(pos.x, pos.y);
      isDrawing = false;
      break;
    case 'arc':
      tempShape = ShapeFactory.arc(pos.x, pos.y, 0, 0, Math.PI, options);
      break;
    case 'polygon':
      if (!tempShape) {
        tempShape = ShapeFactory.polygon([[pos.x, pos.y]], options);
      } else {
        tempShape.points.push([pos.x, pos.y]);
        if (tempShape.points.length > 2) {
          // 检查是否点击了第一个点以闭合多边形
          const firstPoint = tempShape.points[0];
          const dx = firstPoint[0] - pos.x;
          const dy = firstPoint[1] - pos.y;
          const distance = Math.sqrt(dx * dx + dy * dy);
          
          if (distance < 10) {
            shapes.push(tempShape);
            tempShape = null;
            isDrawing = false;
            render();
          }
        }
      }
      break;
    case 'dimension':
      tempShape = ShapeFactory.dimension(pos.x, pos.y, pos.x, pos.y, options);
      break;
  }
}

function handleMouseMove(e) {
  const pos = getCanvasCoordinates(e);
  
  // 更新坐标显示
  document.getElementById('mouseCoordinates').textContent = `X: ${Math.round(pos.x)}, Y: ${Math.round(pos.y)}`;
  
  if (isDragging && currentTool === 'select') {
    if (selectedShape) {
      // 移动选中的形状
      moveShape(selectedShape, pos.x - lastX, pos.y - lastY);
      lastX = pos.x;
      lastY = pos.y;
      render();
    } else {
      // 平移画布
      panOffset.x += pos.x - lastX;
      panOffset.y += pos.y - lastY;
      lastX = pos.x;
      lastY = pos.y;
      render();
    }
    return;
  }
  
  if (!isDrawing) return;
  
  // 更新临时形状
  if (tempShape) {
    switch (tempShape.type) {
      case 'line':
        tempShape.endX = pos.x;
        tempShape.endY = pos.y;
        break;
      case 'rectangle':
        tempShape.width = pos.x - tempShape.x;
        tempShape.height = pos.y - tempShape.y;
        break;
      case 'circle':
        const dx = pos.x - tempShape.centerX;
        const dy = pos.y - tempShape.centerY;
        tempShape.radius = Math.sqrt(dx * dx + dy * dy);
        break;
      case 'arc':
        const dxArc = pos.x - tempShape.centerX;
        const dyArc = pos.y - tempShape.centerY;
        tempShape.radius = Math.sqrt(dxArc * dxArc + dyArc * dyArc);
        tempShape.endAngle = Math.atan2(dyArc, dxArc);
        break;
      case 'polygon':
        if (tempShape.points.length > 0) {
          // 更新最后一个点的位置(预览)
          const lastIdx = tempShape.points.length - 1;
          tempShape.previewX = pos.x;
          tempShape.previewY = pos.y;
        }
        break;
      case 'dimension':
        tempShape.endX = pos.x;
        tempShape.endY = pos.y;
        tempShape.value = Math.round(Math.sqrt(Math.pow(tempShape.endX - tempShape.startX, 2) + Math.pow(tempShape.endY - tempShape.startY, 2)) * 100) / 100;
        break;
    }
    
    render();
  }
}

function handleMouseUp(e) {
  if (!isDrawing && !isDragging) return;
  
  if (isDrawing && tempShape) {
    // 完成绘制,将临时形状添加到形状数组
    if (tempShape.type !== 'polygon') {
      shapes.push(tempShape);
      tempShape = null;
    } else if (tempShape.type === 'polygon' && tempShape.points.length === 1) {
      // 多边形需要至少2个点,所以在第一个点不添加
      tempShape.points.push([tempShape.previewX, tempShape.previewY]);
    }
  }
  
  isDrawing = false;
  isDragging = false;
  canvas.style.cursor = 'crosshair';
  
  render();
}

function handleMouseWheel(e) {
  e.preventDefault();
  const delta = e.deltaY < 0 ? 0.1 : -0.1;
  changeZoom(delta, e);
}

// 缩放控制
function changeZoom(delta, e) {
  const oldZoom = zoomLevel;
  zoomLevel += delta;
  zoomLevel = Math.max(0.1, Math.min(zoomLevel, 5)); // 限制缩放范围
  
  if (e) {
    // 计算鼠标位置的缩放
    const pos = getCanvasCoordinates(e);
    const factor = zoomLevel / oldZoom;
    
    panOffset.x = pos.x - (pos.x - panOffset.x) * factor;
    panOffset.y = pos.y - (pos.y - panOffset.y) * factor;
  }
  
  updateZoomDisplay();
  render();
}

// 更新缩放显示
function updateZoomDisplay() {
  document.getElementById('zoomLevel').textContent = `缩放: ${Math.round(zoomLevel * 100)}%`;
}

// 渲染函数
function render() {
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // 设置变换(缩放和平移)
  ctx.save();
  ctx.translate(panOffset.x, panOffset.y);
  ctx.scale(zoomLevel, zoomLevel);
  
  // 绘制网格
  if (showGrid) {
    drawGrid();
  }
  
  // 绘制所有形状
  shapes.forEach(shape => {
    drawShape(shape, shape === selectedShape);
  });
  
  // 绘制临时形状
  if (tempShape) {
    drawShape(tempShape, false);
    
    // 对于多边形,绘制预览线
    if (tempShape.type === 'polygon' && tempShape.points.length > 0 && tempShape.previewX !== undefined) {
      const lastPoint = tempShape.points[tempShape.points.length - 1];
      ctx.beginPath();
      ctx.strokeStyle = tempShape.strokeColor;
      ctx.lineWidth = tempShape.lineWidth;
      ctx.moveTo(lastPoint[0], lastPoint[1]);
      ctx.lineTo(tempShape.previewX, tempShape.previewY);
      
      // 如果有多于2个点,绘制到第一个点的线
      if (tempShape.points.length > 1) {
        ctx.moveTo(tempShape.previewX, tempShape.previewY);
        ctx.lineTo(tempShape.points[0][0], tempShape.points[0][1]);
      }
      
      ctx.stroke();
    }
  }
  
  ctx.restore();
}

// 绘制网格
function drawGrid() {
  const startX = Math.floor(-panOffset.x / zoomLevel / gridSize) * gridSize;
  const startY = Math.floor(-panOffset.y / zoomLevel / gridSize) * gridSize;
  const endX = Math.ceil((canvas.width - panOffset.x) / zoomLevel / gridSize) * gridSize;
  const endY = Math.ceil((canvas.height - panOffset.y) / zoomLevel / gridSize) * gridSize;
  
  ctx.strokeStyle = 'rgba(232, 232, 237, 0.5)';
  ctx.lineWidth = 0.5;
  
  // 绘制垂直网格线
  for (let x = startX; x <= endX; x += gridSize) {
    ctx.beginPath();
    ctx.moveTo(x, startY);
    ctx.lineTo(x, endY);
    ctx.stroke();
  }
  
  // 绘制水平网格线
  for (let y = startY; y <= endY; y += gridSize) {
    ctx.beginPath();
    ctx.moveTo(startX, y);
    ctx.lineTo(endX, y);
    ctx.stroke();
  }
}

// 绘制形状
function drawShape(shape, isSelected) {
  ctx.strokeStyle = shape.strokeColor;
  ctx.fillStyle = shape.strokeColor;
  ctx.lineWidth = shape.lineWidth;
  
  // 绘制选中状态
  if (isSelected) {
    ctx.strokeStyle = '#0066CC';
    ctx.lineWidth = shape.lineWidth + 1;
  }
  
  switch (shape.type) {
    case 'line':
      ctx.beginPath();
      ctx.moveTo(shape.startX, shape.startY);
      ctx.lineTo(shape.endX, shape.endY);
      ctx.stroke();
      
      if (isSelected) {
        drawSelectionHandles(shape);
      }
      break;
    
    case 'rectangle':
      ctx.beginPath();
      ctx.rect(shape.x, shape.y, shape.width, shape.height);
      ctx.stroke();
      
      if (isSelected) {
        drawSelectionHandles(shape);
      }
      break;
    
    case 'circle':
      ctx.beginPath();
      ctx.arc(shape.centerX, shape.centerY, shape.radius, 0, Math.PI * 2);
      ctx.stroke();
      
      if (isSelected) {
        drawSelectionHandles(shape);
      }
      break;
    
    case 'text':
      ctx.font = `${shape.fontSize}px -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', sans-serif`;
      ctx.fillText(shape.content, shape.x, shape.y);
      
      if (isSelected) {
        ctx.strokeStyle = '#0066CC';
        ctx.lineWidth = 1;
        const textWidth = ctx.measureText(shape.content).width;
        ctx.beginPath();
        ctx.rect(shape.x - 2, shape.y - shape.fontSize, textWidth + 4, shape.fontSize + 4);
        ctx.stroke();
      }
      break;
    
    case 'arc':
      ctx.beginPath();
      ctx.arc(shape.centerX, shape.centerY, shape.radius, shape.startAngle, shape.endAngle);
      ctx.stroke();
      
      if (isSelected) {
        drawSelectionHandles(shape);
      }
      break;
    
    case 'polygon':
      if (shape.points.length > 1) {
        ctx.beginPath();
        ctx.moveTo(shape.points[0][0], shape.points[0][1]);
        
        for (let i = 1; i < shape.points.length; i++) {
          ctx.lineTo(shape.points[i][0], shape.points[i][1]);
        }
        
        ctx.closePath();
        ctx.stroke();
        
        if (isSelected) {
          drawSelectionHandles(shape);
        }
      }
      break;
    
    case 'dimension':
      drawDimension(shape);
      
      if (isSelected) {
        drawSelectionHandles(shape);
      }
      break;
    
    case 'machine-component':
      drawMachineComponent(shape);
      
      if (isSelected) {
        drawSelectionHandles(shape);
      }
      break;
  }
}

// 绘制尺寸标注
function drawDimension(shape) {
  const { startX, startY, endX, endY, value, strokeColor, lineWidth } = shape;
  
  // 计算线的角度和长度
  const dx = endX - startX;
  const dy = endY - startY;
  const angle = Math.atan2(dy, dx);
  const length = Math.sqrt(dx * dx + dy * dy);
  
  // 绘制主线
  ctx.strokeStyle = strokeColor;
  ctx.lineWidth = lineWidth;
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  ctx.lineTo(endX, endY);
  ctx.stroke();
  
  // 绘制箭头
  const arrowSize = 10;
  
  // 起点箭头
  ctx.beginPath();
  ctx.moveTo(startX, startY);
  ctx.lineTo(
    startX + arrowSize * Math.cos(angle + Math.PI * 0.85),
    startY + arrowSize * Math.sin(angle + Math.PI * 0.85)
  );
  ctx.moveTo(startX, startY);
  ctx.lineTo(
    startX + arrowSize * Math.cos(angle - Math.PI * 0.85),
    startY + arrowSize * Math.sin(angle - Math.PI * 0.85)
  );
  ctx.stroke();
  
  // 终点箭头
  ctx.beginPath();
  ctx.moveTo(endX, endY);
  ctx.lineTo(
    endX + arrowSize * Math.cos(angle + Math.PI + Math.PI * 0.85),
    endY + arrowSize * Math.sin(angle + Math.PI + Math.PI * 0.85)
  );
  ctx.moveTo(endX, endY);
  ctx.lineTo(
    endX + arrowSize * Math.cos(angle + Math.PI - Math.PI * 0.85),
    endY + arrowSize * Math.sin(angle + Math.PI - Math.PI * 0.85)
  );
  ctx.stroke();
  
  // 绘制尺寸文本
  ctx.font = '12px -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", sans-serif';
  const textWidth = ctx.measureText(`${value}`).width;
  
  // 文本位置
  const centerX = (startX + endX) / 2;
  const centerY = (startY + endY) / 2;
  
  // 创建文本背景
  ctx.fillStyle = 'white';
  ctx.fillRect(centerX - textWidth / 2 - 3, centerY - 8, textWidth + 6, 16);
  
  // 绘制文本
  ctx.fillStyle = strokeColor;
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText(`${value}`, centerX, centerY);
  ctx.textAlign = 'start';
  ctx.textBaseline = 'alphabetic';
}

// 绘制机床组件
function drawMachineComponent(shape) {
  const { x, y, width, height, componentType, strokeColor, lineWidth } = shape;
  
  ctx.strokeStyle = strokeColor;
  ctx.lineWidth = lineWidth;
  
  switch (componentType) {
    case 'machine-bed':
      // 绘制机床床身
      ctx.beginPath();
      ctx.rect(x, y, width, height);
      ctx.stroke();
      
      // 绘制床身细节
      ctx.beginPath();
      ctx.moveTo(x, y + height / 4);
      ctx.lineTo(x + width, y + height / 4);
      ctx.moveTo(x, y + height * 3 / 4);
      ctx.lineTo(x + width, y + height * 3 / 4);
      ctx.stroke();
      break;
    
    case 'machine-column':
      // 绘制立柱
      ctx.beginPath();
      ctx.rect(x, y, width, height);
      ctx.stroke();
      
      // 绘制立柱细节
      ctx.beginPath();
      ctx.moveTo(x + width / 4, y);
      ctx.lineTo(x + width / 4, y + height);
      ctx.moveTo(x + width * 3 / 4, y);
      ctx.lineTo(x + width * 3 / 4, y + height);
      ctx.stroke();
      break;
    
    case 'machine-spindle':
      // 绘制主轴箱
      ctx.beginPath();
      ctx.rect(x, y, width, height);
      ctx.stroke();
      
      // 绘制主轴
      ctx.beginPath();
      ctx.arc(x + width / 2, y + height / 2, width / 4, 0, Math.PI * 2);
      ctx.stroke();
      break;
    
    // 可以继续添加其他机床组件的绘制方法
    case 'linear-guide':
      // 绘制直线导轨
      ctx.beginPath();
      ctx.rect(x, y, width, height / 4);
      ctx.stroke();
      
      ctx.beginPath();
      ctx.rect(x + width / 4, y + height / 4, width / 2, height / 2);
      ctx.stroke();
      break;
    
    case 'ball-screw':
      // 绘制滚珠丝杠
      ctx.beginPath();
      ctx.rect(x, y, width, height / 6);
      ctx.stroke();
      
      // 绘制丝杠
      ctx.beginPath();
      ctx.moveTo(x, y + height / 12);
      ctx.lineTo(x + width, y + height / 12);
      
      // 绘制丝杠螺纹
      const step = 5;
      for (let i = x; i < x + width; i += step) {
        ctx.moveTo(i, y + height / 12 - 3);
        ctx.lineTo(i + step / 2, y + height / 12 + 3);
      }
      ctx.stroke();
      break;
    
    case 'servo-motor':
      // 绘制伺服电机
      ctx.beginPath();
      ctx.arc(x + width / 2, y + height / 2, width / 2, 0, Math.PI * 2);
      ctx.stroke();
      
      // 绘制电机轴
      ctx.beginPath();
      ctx.moveTo(x + width / 2, y);
      ctx.lineTo(x + width / 2, y + height);
      ctx.moveTo(x, y + height / 2);
      ctx.lineTo(x + width, y + height / 2);
      ctx.stroke();
      break;
  }
}

// 绘制选择框
function drawSelectionHandles(shape) {
  const handleSize = 6 / zoomLevel;
  ctx.fillStyle = '#0066CC';
  ctx.strokeStyle = '#FFFFFF';
  ctx.lineWidth = 1 / zoomLevel;
  
  switch (shape.type) {
    case 'line':
      // 绘制线条两端的控制点
      ctx.beginPath();
      ctx.rect(shape.startX - handleSize / 2, shape.startY - handleSize / 2, handleSize, handleSize);
      ctx.rect(shape.endX - handleSize / 2, shape.endY - handleSize / 2, handleSize, handleSize);
      ctx.fill();
      ctx.stroke();
      break;
      
    case 'rectangle':
      // 绘制矩形四角的控制点
      const x = shape.x;
      const y = shape.y;
      const w = shape.width;
      const h = shape.height;
      
      ctx.beginPath();
      ctx.rect(x - handleSize / 2, y - handleSize / 2, handleSize, handleSize);
      ctx.rect(x + w - handleSize / 2, y - handleSize / 2, handleSize, handleSize);
      ctx.rect(x - handleSize / 2, y + h - handleSize / 2, handleSize, handleSize);
      ctx.rect(x + w - handleSize / 2, y + h - handleSize / 2, handleSize, handleSize);
      ctx.fill();
      ctx.stroke();
      break;
      
    case 'circle':
      // 绘制圆形中心和半径点的控制点
      ctx.beginPath();
      ctx.rect(shape.centerX - handleSize / 2, shape.centerY - handleSize / 2, handleSize, handleSize);
      const radiusPoint = {
        x: shape.centerX + shape.radius,
        y: shape.centerY
      };
      ctx.rect(radiusPoint.x - handleSize / 2, radiusPoint.y - handleSize / 2, handleSize, handleSize);
      ctx.fill();
      ctx.stroke();
      break;
      
    case 'text':
      // 文本的控制点已在绘制函数中处理
      break;
      
    case 'arc':
      // 绘制弧线的中心点和半径点
      ctx.beginPath();
      ctx.rect(shape.centerX - handleSize / 2, shape.centerY - handleSize / 2, handleSize, handleSize);
      const arcEndPoint = {
        x: shape.centerX + shape.radius * Math.cos(shape.endAngle),
        y: shape.centerY + shape.radius * Math.sin(shape.endAngle)
      };
      ctx.rect(arcEndPoint.x - handleSize / 2, arcEndPoint.y - handleSize / 2, handleSize, handleSize);
      ctx.fill();
      ctx.stroke();
      break;
      
    case 'polygon':
      // 绘制多边形每个顶点的控制点
      ctx.beginPath();
      shape.points.forEach(point => {
        ctx.rect(point[0] - handleSize / 2, point[1] - handleSize / 2, handleSize, handleSize);
      });
      ctx.fill();
      ctx.stroke();
      break;
      
    case 'dimension':
      // 绘制尺寸标注两端的控制点
      ctx.beginPath();
      ctx.rect(shape.startX - handleSize / 2, shape.startY - handleSize / 2, handleSize, handleSize);
      ctx.rect(shape.endX - handleSize / 2, shape.endY - handleSize / 2, handleSize, handleSize);
      ctx.fill();
      ctx.stroke();
      break;
      
    case 'machine-component':
      // 绘制机床组件四角的控制点
      const mx = shape.x;
      const my = shape.y;
      const mw = shape.width;
      const mh = shape.height;
      
      ctx.beginPath();
      ctx.rect(mx - handleSize / 2, my - handleSize / 2, handleSize, handleSize);
      ctx.rect(mx + mw - handleSize / 2, my - handleSize / 2, handleSize, handleSize);
      ctx.rect(mx - handleSize / 2, my + mh - handleSize / 2, handleSize, handleSize);
      ctx.rect(mx + mw - handleSize / 2, my + mh - handleSize / 2, handleSize, handleSize);
      ctx.fill();
      ctx.stroke();
      break;
  }
}

// 移动形状
function moveShape(shape, dx, dy) {
  switch (shape.type) {
    case 'line':
      shape.startX += dx;
      shape.startY += dy;
      shape.endX += dx;
      shape.endY += dy;
      break;
      
    case 'rectangle':
      shape.x += dx;
      shape.y += dy;
      break;
      
    case 'circle':
      shape.centerX += dx;
      shape.centerY += dy;
      break;
      
    case 'text':
      shape.x += dx;
      shape.y += dy;
      break;
      
    case 'arc':
      shape.centerX += dx;
      shape.centerY += dy;
      break;
      
    case 'polygon':
      shape.points.forEach(point => {
        point[0] += dx;
        point[1] += dy;
      });
      break;
      
    case 'dimension':
      shape.startX += dx;
      shape.startY += dy;
      shape.endX += dx;
      shape.endY += dy;
      break;
      
    case 'machine-component':
      shape.x += dx;
      shape.y += dy;
      break;
  }
}

// 在指定位置查找形状
function findShapeAtPosition(x, y) {
  // 倒序查找(最后绘制的形状优先选择)
  for (let i = shapes.length - 1; i >= 0; i--) {
    const shape = shapes[i];
    if (isPointInShape(x, y, shape)) {
      return shape;
    }
  }
  return null;
}

// 检查点是否在形状内
function isPointInShape(x, y, shape) {
  const tolerance = 5; // 选择容差
  
  switch (shape.type) {
    case 'line':
      return isPointNearLine(x, y, shape.startX, shape.startY, shape.endX, shape.endY, tolerance);
      
    case 'rectangle':
      return x >= shape.x && x <= shape.x + shape.width &&
             y >= shape.y && y <= shape.y + shape.height;
      
    case 'circle':
      const dx = x - shape.centerX;
      const dy = y - shape.centerY;
      const distance = Math.sqrt(dx * dx + dy * dy);
      return Math.abs(distance - shape.radius) <= tolerance;
      
    case 'text':
      const textWidth = ctx.measureText(shape.content).width;
      return x >= shape.x && x <= shape.x + textWidth &&
             y <= shape.y && y >= shape.y - shape.fontSize;
      
    case 'arc':
      const dxArc = x - shape.centerX;
      const dyArc = y - shape.centerY;
      const distanceArc = Math.sqrt(dxArc * dxArc + dyArc * dyArc);
      const angle = Math.atan2(dyArc, dxArc);
      
      // 检查点到圆心的距离是否接近弧的半径
      const isNearRadius = Math.abs(distanceArc - shape.radius) <= tolerance;
      
      // 检查点的角度是否在弧的角度范围内
      let startAngle = shape.startAngle;
      let endAngle = shape.endAngle;
      
      // 确保角度是正的并且终止角度大于起始角度
      if (endAngle < startAngle) {
        endAngle += Math.PI * 2;
      }
      
      let currentAngle = angle;
      if (currentAngle < startAngle) {
        currentAngle += Math.PI * 2;
      }
      
      return isNearRadius && currentAngle >= startAngle && currentAngle <= endAngle;
      
    case 'polygon':
      if (shape.points.length < 3) return false;
      
      // 先检查是否接近任何边
      for (let i = 0; i < shape.points.length; i++) {
        const p1 = shape.points[i];
        const p2 = shape.points[(i + 1) % shape.points.length];
        if (isPointNearLine(x, y, p1[0], p1[1], p2[0], p2[1], tolerance)) {
          return true;
        }
      }
      
      // 点在多边形内法
      return isPointInPolygon(x, y, shape.points);
      
    case 'dimension':
      return isPointNearLine(x, y, shape.startX, shape.startY, shape.endX, shape.endY, tolerance);
      
    case 'machine-component':
      return x >= shape.x && x <= shape.x + shape.width &&
             y >= shape.y && y <= shape.y + shape.height;
  }
  
  return false;
}

// 检查点是否接近线段
function isPointNearLine(x, y, x1, y1, x2, y2, tolerance) {
  const lineLength = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
  if (lineLength === 0) return false;
  
  const t = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / (lineLength * lineLength);
  
  if (t < 0) {
    // 最近的点在线段外,靠近点1
    const dist = Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
    return dist <= tolerance;
  }
  
  if (t > 1) {
    // 最近的点在线段外,靠近点2
    const dist = Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
    return dist <= tolerance;
  }
  
  // 最近点在线段上
  const projX = x1 + t * (x2 - x1);
  const projY = y1 + t * (y2 - y1);
  const dist = Math.sqrt((x - projX) * (x - projX) + (y - projY) * (y - projY));
  
  return dist <= tolerance;
}

// 点是否在多边形内
function isPointInPolygon(x, y, points) {
  let inside = false;
  for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
    const xi = points[i][0], yi = points[i][1];
    const xj = points[j][0], yj = points[j][1];
    
    const intersect = ((yi > y) !== (yj > y)) &&
        (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
    if (intersect) inside = !inside;
  }
  
  return inside;
}

// 设置当前工具
function setActiveTool(tool) {
  // 更新当前工具
  currentTool = tool;
  
  // 如果是选择多边形工具,则重置临时形状
  if (tool !== 'polygon') {
    tempShape = null;
  }
  
  // 更新工具按钮高亮状态
  document.querySelectorAll('.cd-tool-btn[data-tool]').forEach(button => {
    if (button.getAttribute('data-tool') === tool) {
      button.classList.add('active');
    } else {
      button.classList.remove('active');
    }
  });
  
  // 更新鼠标光标样式
  if (tool === 'select') {
    canvas.style.cursor = 'default';
  } else {
    canvas.style.cursor = 'crosshair';
  }
}

// 更新属性面板
function updatePropertiesPanel() {
  const propertiesPanel = document.getElementById('propertiesPanel');
  
  if (!selectedShape) {
    propertiesPanel.innerHTML = '<div class="cd-empty-state">请选择一个对象以查看其属性</div>';
    return;
  }
  
  // 创建属性面板内容
  let html = '';
  
  // 通用属性
  html += `
    <div class="cd-property-item">
      <label>ID</label>
      <input type="text" class="cd-property-value" value="${selectedShape.id}" disabled>
    </div>
    <div class="cd-property-item">
      <label>类型</label>
      <input type="text" class="cd-property-value" value="${getShapeTypeName(selectedShape)}" disabled>
    </div>
    <div class="cd-property-item">
      <label>线条颜色</label>
      <input type="color" class="cd-property-value" value="${selectedShape.strokeColor}" data-property="strokeColor">
    </div>
    <div class="cd-property-item">
      <label>线条宽度</label>
      <select class="cd-property-value" data-property="lineWidth">
        <option value="1" ${selectedShape.lineWidth === 1 ? 'selected' : ''}>1px</option>
        <option value="2" ${selectedShape.lineWidth === 2 ? 'selected' : ''}>2px</option>
        <option value="3" ${selectedShape.lineWidth === 3 ? 'selected' : ''}>3px</option>
        <option value="4" ${selectedShape.lineWidth === 4 ? 'selected' : ''}>4px</option>
      </select>
    </div>
  `;
  
  // 特定形状属性
  switch (selectedShape.type) {
    case 'line':
      html += `
        <div class="cd-property-item">
          <label>起点 X</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.startX)}" data-property="startX">
        </div>
        <div class="cd-property-item">
          <label>起点 Y</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.startY)}" data-property="startY">
        </div>
        <div class="cd-property-item">
          <label>终点 X</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.endX)}" data-property="endX">
        </div>
        <div class="cd-property-item">
          <label>终点 Y</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.endY)}" data-property="endY">
        </div>
      `;
      break;
      
    case 'rectangle':
      html += `
        <div class="cd-property-item">
          <label>X 坐标</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.x)}" data-property="x">
        </div>
        <div class="cd-property-item">
          <label>Y 坐标</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.y)}" data-property="y">
        </div>
        <div class="cd-property-item">
          <label>宽度</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.width)}" data-property="width">
        </div>
        <div class="cd-property-item">
          <label>高度</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.height)}" data-property="height">
        </div>
      `;
      break;
      
    case 'circle':
      html += `
        <div class="cd-property-item">
          <label>中心 X</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.centerX)}" data-property="centerX">
        </div>
        <div class="cd-property-item">
          <label>中心 Y</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.centerY)}" data-property="centerY">
        </div>
        <div class="cd-property-item">
          <label>半径</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.radius)}" data-property="radius">
        </div>
      `;
      break;
      
    case 'text':
      html += `
        <div class="cd-property-item">
          <label>X 坐标</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.x)}" data-property="x">
        </div>
        <div class="cd-property-item">
          <label>Y 坐标</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.y)}" data-property="y">
        </div>
        <div class="cd-property-item">
          <label>文本内容</label>
          <input type="text" class="cd-property-value" value="${selectedShape.content}" data-property="content">
        </div>
        <div class="cd-property-item">
          <label>字体大小</label>
          <select class="cd-property-value" data-property="fontSize">
            <option value="12" ${selectedShape.fontSize === 12 ? 'selected' : ''}>12px</option>
            <option value="14" ${selectedShape.fontSize === 14 ? 'selected' : ''}>14px</option>
            <option value="16" ${selectedShape.fontSize === 16 ? 'selected' : ''}>16px</option>
            <option value="18" ${selectedShape.fontSize === 18 ? 'selected' : ''}>18px</option>
            <option value="20" ${selectedShape.fontSize === 20 ? 'selected' : ''}>20px</option>
          </select>
        </div>
      `;
      break;
      
    case 'machine-component':
      html += `
        <div class="cd-property-item">
          <label>组件类型</label>
          <input type="text" class="cd-property-value" value="${getMachineComponentName(selectedShape.componentType)}" disabled>
        </div>
        <div class="cd-property-item">
          <label>X 坐标</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.x)}" data-property="x">
        </div>
        <div class="cd-property-item">
          <label>Y 坐标</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.y)}" data-property="y">
        </div>
        <div class="cd-property-item">
          <label>宽度</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.width)}" data-property="width">
        </div>
        <div class="cd-property-item">
          <label>高度</label>
          <input type="number" class="cd-property-value" value="${Math.round(selectedShape.height)}" data-property="height">
        </div>
      `;
      break;
  }
  
  // 添加删除按钮
  html += `
    <div class="cd-property-item">
      <button class="cd-btn cd-btn-primary" id="deleteShapeBtn">删除对象</button>
    </div>
  `;
  
  propertiesPanel.innerHTML = html;
  
  // 添加属性变更事件监听器
  document.querySelectorAll('.cd-property-value[data-property]').forEach(input => {
    input.addEventListener('change', function() {
      const property = this.getAttribute('data-property');
      let value = this.value;
      
      // 将数值型属性转换为数字
      if (['startX', 'startY', 'endX', 'endY', 'x', 'y', 'width', 'height', 'centerX', 'centerY', 'radius', 'lineWidth', 'fontSize'].includes(property)) {
        value = parseFloat(value);
      }
      
      selectedShape[property] = value;
      render();
    });
  });
  
  // 添加删除按钮事件监听器
  document.getElementById('deleteShapeBtn').addEventListener('click', function() {
    deleteSelectedShape();
  });
}

// 更新选中形状的属性
function updateSelectedShapeProperties() {
  if (!selectedShape) return;
  
  selectedShape.strokeColor = config.strokeColor;
  selectedShape.lineWidth = config.lineWidth;
  
  render();
  updatePropertiesPanel();
}

// 删除选中的形状
function deleteSelectedShape() {
  if (!selectedShape) return;
  
  const index = shapes.findIndex(s => s.id === selectedShape.id);
  if (index !== -1) {
    shapes.splice(index, 1);
    selectedShape = null;
    updatePropertiesPanel();
    render();
  }
}

// 获取形状类型名称
function getShapeTypeName(shape) {
  const typeMap = {
    'line': '直线',
    'rectangle': '矩形',
    'circle': '圆形',
    'text': '文本',
    'arc': '弧线',
    'polygon': '多边形',
    'dimension': '尺寸标注',
    'machine-component': '机床组件'
  };
  
  return typeMap[shape.type] || shape.type;
}

// 获取机床组件名称
function getMachineComponentName(componentType) {
  const componentMap = {
    'machine-bed': '床身',
    'machine-column': '立柱',
    'machine-spindle': '主轴箱',
    'linear-guide': '直线导轨',
    'ball-screw': '滚珠丝杠',
    'servo-motor': '伺服电机',
    'control-panel': '控制面板',
    'electrical-cabinet': '电气柜',
    'limit-switch': '限位开关'
  };
  
  return componentMap[componentType] || componentType;
}

// 拖放相关函数
function handleDragStart(e) {
  const componentType = e.target.getAttribute('data-type');
  e.dataTransfer.setData('text/plain', componentType);
}

function handleDragOver(e) {
  e.preventDefault();
  e.dataTransfer.dropEffect = 'copy';
}

function handleDrop(e) {
  e.preventDefault();
  const componentType = e.dataTransfer.getData('text/plain');
  const pos = getCanvasCoordinates(e);
  
  // 创建对应的机床组件
  if (ShapeFactory[componentType]) {
    const shape = ShapeFactory[componentType](pos.x, pos.y, {
      strokeColor: config.strokeColor,
      lineWidth: config.lineWidth
    });
    
    shapes.push(shape);
    selectedShape = shape;
    updatePropertiesPanel();
    render();
  }
}

// 折叠所有类别
function collapseAllCategories() {
  document.querySelectorAll('.cd-category-header').forEach(header => {
    header.classList.add('collapsed');
    const items = header.nextElementSibling;
    items.style.display = 'none';
  });
}

// 组件搜索过滤
function filterComponents() {
  const searchText = document.getElementById('componentSearch').value.toLowerCase();
  
  document.querySelectorAll('.cd-component-item').forEach(item => {
    const text = item.textContent.toLowerCase();
    const category = item.closest('.cd-component-category');
    const categoryHeader = category.querySelector('.cd-category-header');
    const categoryItems = category.querySelector('.cd-category-items');
    
    if (text.includes(searchText)) {
      item.style.display = 'block';
      categoryHeader.classList.remove('collapsed');
      categoryItems.style.display = 'block';
    } else {
      item.style.display = 'none';
    }
  });
  
  // 如果类别下所有项目都被隐藏,则隐藏整个类别
  document.querySelectorAll('.cd-component-category').forEach(category => {
    const items = Array.from(category.querySelectorAll('.cd-component-item'));
    const allHidden = items.every(item => item.style.display === 'none');
    
    if (allHidden) {
      category.style.display = 'none';
    } else {
      category.style.display = 'block';
    }
  });
}

// 文本工具相关函数
function openTextModal(x, y) {
  const modal = document.getElementById('textModal');
  modal.classList.add('active');
  
  // 存储位置信息
  modal.dataset.posX = x;
  modal.dataset.posY = y;
  
  // 重置表单
  document.getElementById('textContent').value = '';
  document.getElementById('fontSize').value = '14';
  
  // 聚焦文本输入框
  document.getElementById('textContent').focus();
}

function closeTextModal() {
  const modal = document.getElementById('textModal');
  modal.classList.remove('active');
}

function confirmTextInput() {
  const modal = document.getElementById('textModal');
  const x = parseFloat(modal.dataset.posX);
  const y = parseFloat(modal.dataset.posY);
  const content = document.getElementById('textContent').value;
  const fontSize = parseInt(document.getElementById('fontSize').value);
  
  if (content) {
    const textShape = ShapeFactory.text(x, y, content, {
      fontSize: fontSize,
      strokeColor: config.strokeColor
    });
    
    shapes.push(textShape);
    render();
  }
  
  closeTextModal();
}

// 获取Canvas坐标
function getCanvasCoordinates(e) {
  const rect = canvas.getBoundingClientRect();
  const scaleX = canvas.width / rect.width;
  const scaleY = canvas.height / rect.height;
  
  return {
    x: (e.clientX - rect.left) * scaleX / zoomLevel - panOffset.x / zoomLevel,
    y: (e.clientY - rect.top) * scaleY / zoomLevel - panOffset.y / zoomLevel
  };
}

// 清空画布
function clearCanvas() {
  if (confirm('确定要清空画布吗?这将删除所有图形。')) {
    shapes = [];
    selectedShape = null;
    tempShape = null;
    panOffset = { x: 0, y: 0 };
    zoomLevel = 1;
    updatePropertiesPanel();
    updateZoomDisplay();
    render();
  }
}

// 导出设计
function exportDesign() {
  // 保存当前状态
  const currentPanOffset = { ...panOffset };
  const currentZoomLevel = zoomLevel;
  const currentSelectedShape = selectedShape;
  const currentTempShape = tempShape;
  
  // 重置视图以便导出
  panOffset = { x: 0, y: 0 };
  zoomLevel = 1;
  selectedShape = null;
  tempShape = null;
  
  // 重新渲染用于导出
  render();
  
  // 创建临时画布以绘制白色背景
  const exportCanvas = document.createElement('canvas');
  exportCanvas.width = canvas.width;
  exportCanvas.height = canvas.height;
  const exportCtx = exportCanvas.getContext('2d');
  
  // 绘制白色背景
  exportCtx.fillStyle = 'white';
  exportCtx.fillRect(0, 0, exportCanvas.width, exportCanvas.height);
  
  // 复制当前画布内容
  exportCtx.drawImage(canvas, 0, 0);
  
  // 创建下载链接
  const link = document.createElement('a');
  link.download = '机床CAD设计_' + new Date().toISOString().slice(0, 10) + '.png';
  
  // 转换为图片URL
  exportCanvas.toBlob(function(blob) {
    link.href = URL.createObjectURL(blob);
    link.click();
    
    // 清理
    URL.revokeObjectURL(link.href);
    
    // 恢复原始状态
    panOffset = currentPanOffset;
    zoomLevel = currentZoomLevel;
    selectedShape = currentSelectedShape;
    tempShape = currentTempShape;
    render();
  });
}

// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', init);

// 监听浏览器调整大小事件
window.addEventListener('resize', function() {
  if (canvas) {
    const container = canvas.parentElement;
    const containerWidth = container.clientWidth;
    const containerHeight = container.clientHeight;
    
    // 设置 Canvas 的样式属性以适应容器
    canvas.style.width = '100%';
    canvas.style.height = '100%';
    
    render();
  }
}); 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

地上一の鹅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值