gaoluyang
2026-06-24 712aa51536236d43e87273e4ce45ac5691dffad8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import type { CSSProperties } from 'vue';
 
import type { HotZoneItemProperty } from '../../config';
 
export const HOT_ZONE_MIN_SIZE = 100; // 热区的最小宽高
 
/** 控制的类型 */
export enum CONTROL_TYPE_ENUM {
  LEFT,
  TOP,
  WIDTH,
  HEIGHT,
}
 
/** 定义热区的控制点 */
export interface ControlDot {
  position: string;
  types: CONTROL_TYPE_ENUM[];
  style: CSSProperties;
}
 
/** 热区的 8 个控制点 */
export const CONTROL_DOT_LIST = [
  {
    position: '左上角',
    types: [
      CONTROL_TYPE_ENUM.LEFT,
      CONTROL_TYPE_ENUM.TOP,
      CONTROL_TYPE_ENUM.WIDTH,
      CONTROL_TYPE_ENUM.HEIGHT,
    ],
    style: { left: '-5px', top: '-5px', cursor: 'nwse-resize' },
  },
  {
    position: '上方中间',
    types: [CONTROL_TYPE_ENUM.TOP, CONTROL_TYPE_ENUM.HEIGHT],
    style: {
      left: '50%',
      top: '-5px',
      cursor: 'n-resize',
      transform: 'translateX(-50%)',
    },
  },
  {
    position: '右上角',
    types: [
      CONTROL_TYPE_ENUM.TOP,
      CONTROL_TYPE_ENUM.WIDTH,
      CONTROL_TYPE_ENUM.HEIGHT,
    ],
    style: { right: '-5px', top: '-5px', cursor: 'nesw-resize' },
  },
  {
    position: '右侧中间',
    types: [CONTROL_TYPE_ENUM.WIDTH],
    style: {
      right: '-5px',
      top: '50%',
      cursor: 'e-resize',
      transform: 'translateX(-50%)',
    },
  },
  {
    position: '右下角',
    types: [CONTROL_TYPE_ENUM.WIDTH, CONTROL_TYPE_ENUM.HEIGHT],
    style: { right: '-5px', bottom: '-5px', cursor: 'nwse-resize' },
  },
  {
    position: '下方中间',
    types: [CONTROL_TYPE_ENUM.HEIGHT],
    style: {
      left: '50%',
      bottom: '-5px',
      cursor: 's-resize',
      transform: 'translateX(-50%)',
    },
  },
  {
    position: '左下角',
    types: [
      CONTROL_TYPE_ENUM.LEFT,
      CONTROL_TYPE_ENUM.WIDTH,
      CONTROL_TYPE_ENUM.HEIGHT,
    ],
    style: { left: '-5px', bottom: '-5px', cursor: 'nesw-resize' },
  },
  {
    position: '左侧中间',
    types: [CONTROL_TYPE_ENUM.LEFT, CONTROL_TYPE_ENUM.WIDTH],
    style: {
      left: '-5px',
      top: '50%',
      cursor: 'w-resize',
      transform: 'translateX(-50%)',
    },
  },
] as ControlDot[];
 
// region 热区的缩放
export const HOT_ZONE_SCALE_RATE = 2; // 热区的缩放比例
 
/** 缩小:缩回适合手机屏幕的大小 */
export function zoomOut(list?: HotZoneItemProperty[]) {
  return (
    list?.map((hotZone) => ({
      ...hotZone,
      left: (hotZone.left /= HOT_ZONE_SCALE_RATE),
      top: (hotZone.top /= HOT_ZONE_SCALE_RATE),
      width: (hotZone.width /= HOT_ZONE_SCALE_RATE),
      height: (hotZone.height /= HOT_ZONE_SCALE_RATE),
    })) || []
  );
}
 
/** 放大:作用是为了方便在电脑屏幕上编辑 */
export function zoomIn(list?: HotZoneItemProperty[]) {
  return (
    list?.map((hotZone) => ({
      ...hotZone,
      left: (hotZone.left *= HOT_ZONE_SCALE_RATE),
      top: (hotZone.top *= HOT_ZONE_SCALE_RATE),
      width: (hotZone.width *= HOT_ZONE_SCALE_RATE),
      height: (hotZone.height *= HOT_ZONE_SCALE_RATE),
    })) || []
  );
}
 
// endregion
 
/**
 * 封装热区拖拽
 *
 * 注:为什么不使用vueuse的useDraggable。在本场景下,其使用方式比较复杂
 * @param hotZone 热区
 * @param downEvent 鼠标按下事件
 * @param callback 回调函数
 */
export const useDraggable = (
  hotZone: HotZoneItemProperty,
  downEvent: MouseEvent,
  callback: (
    left: number,
    top: number,
    width: number,
    height: number,
    moveWidth: number,
    moveHeight: number,
  ) => void,
) => {
  // 阻止事件冒泡
  downEvent.stopPropagation();
 
  // 移动前的鼠标坐标
  const { clientX: startX, clientY: startY } = downEvent;
  // 移动前的热区坐标、大小
  const { left, top, width, height } = hotZone;
 
  // 监听鼠标移动
  const handleMouseMove = (e: MouseEvent) => {
    // 移动宽度
    const moveWidth = e.clientX - startX;
    // 移动高度
    const moveHeight = e.clientY - startY;
    // 移动回调
    callback(left, top, width, height, moveWidth, moveHeight);
  };
 
  // 松开鼠标后,结束拖拽
  const handleMouseUp = () => {
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
  };
 
  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);
};