@douyinfe/semi-foundation 2.63.1 → 2.63.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/chat/foundation.ts +18 -23
  2. package/datePicker/foundation.ts +1 -1
  3. package/image/image.scss +6 -1
  4. package/image/previewImageFoundation.ts +233 -150
  5. package/image/previewInnerFoundation.ts +11 -6
  6. package/lib/cjs/base/foundation.js +1 -1
  7. package/lib/cjs/chat/foundation.d.ts +1 -2
  8. package/lib/cjs/chat/foundation.js +2 -7
  9. package/lib/cjs/checkbox/checkboxGroupFoundation.js +1 -1
  10. package/lib/cjs/datePicker/foundation.js +1 -1
  11. package/lib/cjs/image/image.css +5 -1
  12. package/lib/cjs/image/image.scss +6 -1
  13. package/lib/cjs/image/previewImageFoundation.d.ts +46 -13
  14. package/lib/cjs/image/previewImageFoundation.js +207 -181
  15. package/lib/cjs/image/previewInnerFoundation.d.ts +4 -3
  16. package/lib/cjs/image/previewInnerFoundation.js +5 -2
  17. package/lib/cjs/navigation/foundation.d.ts +8 -8
  18. package/lib/cjs/navigation/itemFoundation.d.ts +5 -4
  19. package/lib/cjs/radio/radioInnerFoundation.js +1 -1
  20. package/lib/cjs/table/utils.d.ts +1 -1
  21. package/lib/cjs/tabs/constants.d.ts +1 -0
  22. package/lib/cjs/tabs/constants.js +2 -1
  23. package/lib/cjs/tabs/tabs.css +28 -199
  24. package/lib/cjs/tabs/tabs.scss +336 -297
  25. package/lib/cjs/tabs/variables.scss +21 -2
  26. package/lib/cjs/tree/treeUtil.d.ts +1 -1
  27. package/lib/cjs/upload/constants.d.ts +1 -1
  28. package/lib/es/base/foundation.js +1 -1
  29. package/lib/es/chat/foundation.d.ts +1 -2
  30. package/lib/es/chat/foundation.js +2 -7
  31. package/lib/es/checkbox/checkboxGroupFoundation.js +1 -1
  32. package/lib/es/datePicker/foundation.js +1 -1
  33. package/lib/es/image/image.css +5 -1
  34. package/lib/es/image/image.scss +6 -1
  35. package/lib/es/image/previewImageFoundation.d.ts +46 -13
  36. package/lib/es/image/previewImageFoundation.js +207 -181
  37. package/lib/es/image/previewInnerFoundation.d.ts +4 -3
  38. package/lib/es/image/previewInnerFoundation.js +5 -2
  39. package/lib/es/navigation/foundation.d.ts +8 -8
  40. package/lib/es/navigation/itemFoundation.d.ts +5 -4
  41. package/lib/es/radio/radioInnerFoundation.js +1 -1
  42. package/lib/es/table/utils.d.ts +1 -1
  43. package/lib/es/tabs/constants.d.ts +1 -0
  44. package/lib/es/tabs/constants.js +2 -1
  45. package/lib/es/tabs/tabs.css +28 -199
  46. package/lib/es/tabs/tabs.scss +336 -297
  47. package/lib/es/tabs/variables.scss +21 -2
  48. package/lib/es/tree/treeUtil.d.ts +1 -1
  49. package/lib/es/upload/constants.d.ts +1 -1
  50. package/navigation/foundation.ts +8 -8
  51. package/navigation/itemFoundation.ts +6 -4
  52. package/package.json +3 -3
  53. package/tabs/constants.ts +2 -1
  54. package/tabs/tabs.scss +336 -297
  55. package/tabs/variables.scss +21 -2
@@ -5,14 +5,14 @@ import { debounce } from "lodash";
5
5
  import { getUuidv4 } from "../utils/uuid";
6
6
  import { handlePrevent } from "../utils/a11y";
7
7
 
8
- const { PIC_PREFIX, PIC_SUFFIX_ARRAY, ROLE,
8
+ const { PIC_PREFIX, PIC_SUFFIX_ARRAY, ROLE,
9
9
  SCROLL_ANIMATION_TIME, SHOW_SCROLL_GAP
10
10
  } = strings;
11
11
 
12
12
  export interface Content {
13
13
  type: 'text' | 'image_url' | 'file_url';
14
14
  text?: string;
15
- image_url?: {
15
+ image_url?: {
16
16
  url: string;
17
17
  [x: string]: any
18
18
  };
@@ -37,7 +37,7 @@ export interface Message {
37
37
  }
38
38
 
39
39
  export interface ChatAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
40
- getContainerRef: () => React.RefObject<HTMLDivElement>;
40
+ getContainerRef: () => HTMLDivElement;
41
41
  setWheelScroll: (flag: boolean) => void;
42
42
  notifyChatsChange: (chats: Message[]) => void;
43
43
  notifyLikeMessage: (message: Message) => void;
@@ -80,17 +80,15 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
80
80
  }
81
81
 
82
82
  scrollToBottomImmediately = () => {
83
- const containerRef = this._adapter.getContainerRef();
84
- const element = containerRef?.current;
83
+ const element = this._adapter.getContainerRef();
85
84
  if (element) {
86
85
  element.scrollTop = element.scrollHeight;
87
- }
86
+ }
88
87
  }
89
88
 
90
89
  scrollToBottomWithAnimation = () => {
91
90
  const duration = SCROLL_ANIMATION_TIME;
92
- const containerRef = this._adapter.getContainerRef();
93
- const element = containerRef?.current;
91
+ const element = this._adapter.getContainerRef();
94
92
  if (!element) {
95
93
  return;
96
94
  }
@@ -106,18 +104,15 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
106
104
  easing: 'easeInOutCubic'
107
105
  }
108
106
  );
109
-
107
+
110
108
  this.animation.on('frame', ({ scrollTop }: { scrollTop: number }) => {
111
109
  element.scrollTop = scrollTop;
112
110
  });
113
-
111
+
114
112
  this.animation.start();
115
113
  }
116
114
 
117
115
  containerScroll = (e: any) => {
118
- if (e.target !== e.currentTarget) {
119
- return;
120
- }
121
116
  e.persist();
122
117
  const update = () => {
123
118
  this.getScroll(e.target);
@@ -155,7 +150,7 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
155
150
  const newChats = [...chats, dividerMessage];
156
151
  this._adapter.notifyChatsChange(newChats);
157
152
  this._adapter.notifyClearContext();
158
- }
153
+ }
159
154
 
160
155
  onMessageSend = (input: string, attachment: any[]) => {
161
156
  let content;
@@ -169,13 +164,13 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
169
164
  const suffix = name.split('.').pop();
170
165
  const isImg = fileInstance?.type?.startsWith(PIC_PREFIX) || PIC_SUFFIX_ARRAY.includes(suffix);
171
166
  if (isImg) {
172
- content.push({
173
- type: 'image_url',
174
- image_url: { url: url }
167
+ content.push({
168
+ type: 'image_url',
169
+ image_url: { url: url }
175
170
  });
176
171
  } else {
177
- content.push({
178
- type: 'file_url',
172
+ content.push({
173
+ type: 'file_url',
179
174
  file_url: {
180
175
  url: url,
181
176
  name: name,
@@ -238,7 +233,7 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
238
233
  newChats.splice(index, 1, newChat);
239
234
  this._adapter.notifyChatsChange(newChats);
240
235
  }
241
-
236
+
242
237
  dislikeMessage = (message: Message) => {
243
238
  const { chats } = this.getStates();
244
239
  this._adapter.notifyDislikeMessage(message);
@@ -252,7 +247,7 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
252
247
  newChats.splice(index, 1, newChat);
253
248
  this._adapter.notifyChatsChange(newChats);
254
249
  }
255
-
250
+
256
251
  resetMessage = (message: Message) => {
257
252
  const { chats } = this.getStates();
258
253
  const lastMessage = chats[chats.length - 1];
@@ -284,7 +279,7 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
284
279
  //Disable the default implementation, preventing files from being opened
285
280
  handlePrevent(e);
286
281
  }
287
-
282
+
288
283
  handleContainerDragLeave = (e: any) => {
289
284
  handlePrevent(e);
290
285
  // 鼠标移动至 container 的子元素,则不做任何操作
@@ -295,7 +290,7 @@ export default class ChatFoundation <P = Record<string, any>, S = Record<string,
295
290
  }
296
291
  /**
297
292
  * 延迟隐藏 container ,防止父元素的 mouseOver 被触发,导致 container 无法隐藏
298
- * Delay hiding of the container to prevent the parent element's mouseOver from being triggered,
293
+ * Delay hiding of the container to prevent the parent element's mouseOver from being triggered,
299
294
  * causing the container to be unable to be hidden.
300
295
  */
301
296
  setTimeout(() => {
@@ -436,7 +436,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
436
436
  */
437
437
  resetInnerSelectedStates(willUpdateDates?: Date[]) {
438
438
  const { value } = this._adapter.getStates();
439
- const needResetCachedSelectedValue = !this.isCachedSelectedValueValid(willUpdateDates) || this._adapter.needConfirm() && !this.clickConfirmButton;
439
+ const needResetCachedSelectedValue = isNullOrUndefined(willUpdateDates) || !this.isCachedSelectedValueValid(willUpdateDates) || this._adapter.needConfirm() && !this.clickConfirmButton;
440
440
  if (needResetCachedSelectedValue) {
441
441
  this.resetCachedSelectedValue(value);
442
442
  }
package/image/image.scss CHANGED
@@ -100,6 +100,7 @@ $module: #{$prefix}-image;
100
100
  align-items: center;
101
101
  padding: $spacing-image_preview_header-paddingY $spacing-image_preview_header-paddingX;
102
102
  z-index: $z-image_preview_header;
103
+ pointer-events: none;
103
104
 
104
105
  &-title {
105
106
  flex: 1;
@@ -113,6 +114,7 @@ $module: #{$prefix}-image;
113
114
  width: $width-image_preview_header_close;
114
115
  height: $height-image_preview_header_close;
115
116
  border-radius: 50%;
117
+ pointer-events: auto;
116
118
 
117
119
  &:hover {
118
120
  background-color: $color-image_header_close-bg;
@@ -192,11 +194,14 @@ $module: #{$prefix}-image;
192
194
  &-image {
193
195
  position: relative;
194
196
  height: 100%;
197
+ display: flex;
198
+ justify-content: center;
199
+ align-items: center;
195
200
 
196
201
  &-img {
197
202
  position: absolute;
198
203
  transform: scale3d($transform_scale3d-image_preview_image_img) $transform_rotate-image_preview_image_img;
199
- transition: transform $transition_duration-image_preview_image_img $transition_delay-image_preview_image_img;
204
+ // transition: transform $transition_duration-image_preview_image_img $transition_delay-image_preview_image_img;
200
205
  z-index: 0;
201
206
  user-select: none;
202
207
  }
@@ -12,60 +12,86 @@ export interface DragDirection {
12
12
  canDragHorizontal: boolean
13
13
  }
14
14
 
15
- export interface ExtremeBounds {
16
- left: number;
17
- top: number
15
+ export interface ExtremeTranslate {
16
+ x: number;
17
+ y: number
18
+ }
19
+
20
+ export interface Offset {
21
+ x: number;
22
+ y: number
18
23
  }
19
24
 
20
- export interface ImageOffset {
25
+ export interface Translate {
21
26
  x: number;
22
27
  y: number
23
28
  }
24
29
 
25
- const DefaultDOMRect = {
26
- bottom: 0,
27
- height: 0,
28
- left: 0,
29
- right: 0,
30
- top: 0,
31
- width: 0,
32
- x: 0,
33
- y: 0,
34
- toJSON: () => ({})
35
- };
30
+ interface CalcBoundingRectMouseOffset {
31
+ offset: Offset;
32
+ width: number;
33
+ height: number;
34
+ rotation?: number
35
+ }
36
+
37
+ export interface BoundingRectSize {
38
+ width: number;
39
+ height: number
40
+ }
41
+
36
42
  export default class PreviewImageFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<PreviewImageAdapter<P, S>, P, S> {
37
43
  constructor(adapter: PreviewImageAdapter<P, S>) {
38
44
  super({ ...adapter });
39
45
  }
40
46
 
41
- startMouseOffset = { x: 0, y: 0 };
47
+ startMouseClientPosition = { x: 0, y: 0 };
42
48
  originImageWidth = null;
43
49
  originImageHeight = null;
44
50
 
51
+ containerWidth = 0;
52
+ containerHeight = 0;
53
+
54
+ init() {
55
+ this._getContainerBoundingRectSize();
56
+ }
57
+
45
58
  _isImageVertical = (): boolean => this.getProp("rotation") % 180 !== 0;
46
59
 
47
- _getImageBounds = (): DOMRect => {
48
- const imageDOM = this._adapter.getImage();
49
- if (imageDOM) {
50
- return imageDOM.getBoundingClientRect();
60
+ _getContainerBoundingRectSize = () => {
61
+ const containerDOM = this._adapter.getContainer();
62
+ if (containerDOM) {
63
+ this.containerWidth = containerDOM.clientWidth;
64
+ this.containerHeight = containerDOM.clientHeight;
51
65
  }
52
- return DefaultDOMRect;
53
- };
66
+ }
54
67
 
55
- _getContainerBounds = (): DOMRect => {
68
+ _getAdaptationZoom = () => {
69
+ let _zoom = 1;
56
70
  const containerDOM = this._adapter.getContainer();
57
- if (containerDOM) {
58
- return containerDOM.getBoundingClientRect();
71
+
72
+ if (containerDOM && this.originImageWidth && this.originImageHeight) {
73
+ const { rotation } = this.getProps();
74
+ const { width: imageWidth, height: imageHeight } = this.calcBoundingRectSize(this.originImageWidth, this.originImageHeight, rotation);
75
+ const reservedWidth = this.containerWidth - 80;
76
+ const reservedHeight = this.containerHeight - 80;
77
+
78
+ _zoom = Number(
79
+ Math.min(reservedWidth / imageWidth, reservedHeight / imageHeight).toFixed(2)
80
+ );
59
81
  }
60
- return DefaultDOMRect;
82
+
83
+ return _zoom;
61
84
  }
62
85
 
63
- _getOffset = (e: any): ImageOffset => {
64
- const { left, top } = this._getImageBounds();
65
- return {
66
- x: e.clientX - left,
67
- y: e.clientY - top,
68
- };
86
+ _getInitialZoom = () => {
87
+ const { ratio } = this.getProps();
88
+ let _zoom = 1;
89
+
90
+ if (ratio === 'adaptation') {
91
+ _zoom = this._getAdaptationZoom();
92
+ }
93
+
94
+ return _zoom;
69
95
  }
70
96
 
71
97
  setLoading = (loading: boolean) => {
@@ -73,9 +99,8 @@ export default class PreviewImageFoundation<P = Record<string, any>, S = Record<
73
99
  }
74
100
 
75
101
  handleWindowResize = (): void => {
76
- if (this.originImageWidth && this.originImageHeight) {
77
- this.handleResizeImage();
78
- }
102
+ this._getContainerBoundingRectSize();
103
+ this.initializeImage();
79
104
  };
80
105
 
81
106
  handleLoad = (e: any): void => {
@@ -88,7 +113,7 @@ export default class PreviewImageFoundation<P = Record<string, any>, S = Record<
88
113
  } as any);
89
114
  // 图片初次加载,计算 zoom,zoom 改变不需要通过回调透出
90
115
  // When the image is loaded for the first time, zoom is calculated, and zoom changes do not need to be exposed through callbacks.
91
- this.handleResizeImage(false);
116
+ this.initializeImage(false);
92
117
  }
93
118
  const { src, onLoad } = this.getProps();
94
119
  onLoad && onLoad(src);
@@ -102,53 +127,35 @@ export default class PreviewImageFoundation<P = Record<string, any>, S = Record<
102
127
  onError && onError(src);
103
128
  }
104
129
 
105
- handleResizeImage = (notify: boolean = true) => {
106
- const horizontal = !this._isImageVertical();
130
+ handleRatioChange = () => {
131
+ this.initializeImage();
132
+ }
133
+
134
+ initializeImageZoom = (notify = true) => {
107
135
  const { currZoom } = this.getStates();
108
- const imgWidth = horizontal ? this.originImageWidth : this.originImageHeight;
109
- const imgHeight = horizontal ? this.originImageHeight : this.originImageWidth;
110
- const { onZoom, setRatio, ratio } = this.getProps();
111
- const containerDOM = this._adapter.getContainer();
112
- if (containerDOM) {
113
- const { width: containerWidth, height: containerHeight } = this._getContainerBounds();
114
- const reservedWidth = containerWidth - 80;
115
- const reservedHeight = containerHeight - 80;
116
- let _zoom = 1;
117
- if (imgWidth > reservedWidth || imgHeight > reservedHeight) {
118
- _zoom = Number(
119
- Math.min(reservedWidth / imgWidth, reservedHeight / imgHeight).toFixed(2)
120
- );
121
- }
122
- if (currZoom === _zoom) {
123
- this.calculatePreviewImage(_zoom, null);
124
- } else {
125
- onZoom(_zoom, notify);
126
- }
136
+ const { onZoom } = this.getProps();
137
+
138
+ const _zoom = this._getInitialZoom();
139
+
140
+ if (currZoom !== _zoom) {
141
+ onZoom(_zoom, notify);
142
+ } else {
143
+ this.changeZoom(_zoom);
127
144
  }
128
145
  }
129
146
 
130
- handleRatioChange = () => {
131
- if (this.originImageWidth && this.originImageHeight) {
132
- const { currZoom } = this.getStates();
133
- const { ratio, onZoom } = this.getProps();
134
- let _zoom: number;
135
- if (ratio === 'adaptation') {
136
- const horizontal = !this._isImageVertical();
137
- const imgWidth = horizontal ? this.originImageWidth : this.originImageHeight;
138
- const imgHeight = horizontal ? this.originImageHeight : this.originImageWidth;
139
- const { width: containerWidth, height: containerHeight } = this._getContainerBounds();
140
- const reservedWidth = containerWidth - 80;
141
- const reservedHeight = containerHeight - 80;
142
- _zoom = Number(
143
- Math.min(reservedWidth / imgWidth, reservedHeight / imgHeight).toFixed(2)
144
- );
145
- } else {
146
- _zoom = 1;
147
- }
148
- if (currZoom !== _zoom) {
149
- onZoom(_zoom);
147
+ initializeTranslate = () => {
148
+ this.setState({
149
+ translate: {
150
+ x: 0,
151
+ y: 0
150
152
  }
151
- }
153
+ } as any);
154
+ }
155
+
156
+ initializeImage = (notify = true) => {
157
+ this.initializeImageZoom(notify);
158
+ this.initializeTranslate();
152
159
  }
153
160
 
154
161
  handleRightClickImage = (e: any) => {
@@ -162,111 +169,187 @@ export default class PreviewImageFoundation<P = Record<string, any>, S = Record<
162
169
  }
163
170
  };
164
171
 
165
- calcCanDragDirection = (): DragDirection => {
166
- const { width, height } = this.getStates();
167
- const { rotation } = this.getProps();
168
- const { width: containerWidth, height: containerHeight } =this._getContainerBounds();
169
- let canDragHorizontal = width > containerWidth;
170
- let canDragVertical = height > containerHeight;
171
- if (this._isImageVertical()) {
172
- canDragHorizontal = height > containerWidth;
173
- canDragVertical = width > containerHeight;
174
- }
172
+ calcBoundingRectSize(width = 0, height = 0, rotation = 0) {
173
+ const angleInRadians = rotation * Math.PI / 180;
174
+ const sinTheta = Math.abs(Math.sin(angleInRadians));
175
+ const cosTheta = Math.abs(Math.cos(angleInRadians));
176
+ const boundingWidth = width * cosTheta + height * sinTheta;
177
+ const boundingHeight = width * sinTheta + height * cosTheta;
178
+
179
+ return {
180
+ width: boundingWidth,
181
+ height: boundingHeight
182
+ };
183
+ }
184
+
185
+ getCanDragDirection = (width: number, height: number): DragDirection => {
186
+ let canDragHorizontal = width > this.containerWidth;
187
+ let canDragVertical = height > this.containerHeight;
188
+
175
189
  return {
176
190
  canDragVertical,
177
191
  canDragHorizontal,
178
192
  };
179
193
  };
180
194
 
181
- calculatePreviewImage = (newZoom: number, e: any): void => {
195
+ changeZoom = (newZoom: number, e?: WheelEvent): void => {
182
196
  const imageDOM = this._adapter.getImage();
183
- const { canDragVertical, canDragHorizontal } = this.calcCanDragDirection();
184
- const canDrag = canDragVertical || canDragHorizontal;
185
- const { width: containerWidth, height: containerHeight } = this._getContainerBounds();
197
+ const { currZoom, translate, width, height } = this.getStates();
198
+ const { rotation } = this.getProps();
199
+ const changeScale = newZoom / (currZoom || 1);
186
200
  const newWidth = Math.floor(this.originImageWidth * newZoom);
187
201
  const newHeight = Math.floor(this.originImageHeight * newZoom);
202
+ let newTranslateX = Math.floor(translate.x * changeScale);
203
+ let newTranslateY = Math.floor(translate.y * changeScale);
188
204
 
189
- // debugger;
190
- let _offset;
191
- const horizontal = !this._isImageVertical();
192
- let newTop = 0;
193
- let newLeft = 0;
194
- if (horizontal) {
195
- _offset = {
196
- x: 0.5 * (containerWidth - newWidth),
197
- y: 0.5 * (containerHeight - newHeight),
198
- };
199
-
200
- newLeft = _offset.x;
201
- newTop= _offset.y;
202
- } else {
203
- _offset = {
204
- x: 0.5 * (containerWidth - newHeight),
205
- y: 0.5 * (containerHeight - newWidth),
206
- };
207
- newLeft = _offset.x - (newWidth - newHeight) / 2;
208
- newTop = _offset.y + (newWidth - newHeight) / 2;
205
+ const imageBound = this.calcBoundingRectSize(width, height, rotation);
206
+ const newImageBound = {
207
+ width: imageBound.width * changeScale,
208
+ height: imageBound.height * changeScale
209
+ };
210
+
211
+ if (e && imageDOM && e.target === imageDOM) {
212
+ const { x: offsetX, y: offsetY } = this.calcBoundingRectMouseOffset({
213
+ width,
214
+ height,
215
+ offset: {
216
+ x: e.offsetX,
217
+ y: e.offsetY
218
+ },
219
+ rotation
220
+ });
221
+
222
+ const imageNewCenterX = e.clientX + (imageBound.width / 2 - offsetX) * changeScale;
223
+ const imageNewCenterY = e.clientY + (imageBound.height / 2 - offsetY) * changeScale;
224
+ const containerCenterX = this.containerWidth / 2;
225
+ const containerCenterY = this.containerHeight / 2;
226
+
227
+ newTranslateX = imageNewCenterX - containerCenterX;
228
+ newTranslateY = imageNewCenterY - containerCenterY;
209
229
  }
210
-
230
+
231
+ const newTranslate = this.getSafeTranslate(newImageBound.width, newImageBound.height, newTranslateX, newTranslateY);
232
+
211
233
  this.setState({
234
+ translate: newTranslate,
212
235
  width: newWidth,
213
236
  height: newHeight,
214
- offset: _offset,
215
- left: newLeft,
216
- top: newTop,
217
237
  currZoom: newZoom,
218
238
  } as any);
219
239
  if (imageDOM) {
240
+ const { canDragVertical, canDragHorizontal } = this.getCanDragDirection(newImageBound.width, newImageBound.height);
241
+ const canDrag = canDragVertical || canDragHorizontal;
242
+
220
243
  this._adapter.setImageCursor(canDrag);
221
244
  }
222
245
  };
223
246
 
224
- calcExtremeBounds = (): ExtremeBounds => {
225
- const { width, height } = this.getStates();
226
- const { width: containerWidth, height: containerHeight } = this._getContainerBounds();
227
- let extremeLeft = containerWidth - width;
228
- let extremeTop = containerHeight - height;
229
- if (this._isImageVertical()) {
230
- extremeLeft = containerWidth - height;
231
- extremeTop = containerHeight - width;
232
- }
247
+ getExtremeTranslate = (width: number, height: number): ExtremeTranslate => {
233
248
  return {
234
- left: extremeLeft,
235
- top: extremeTop,
249
+ x: (width - this.containerWidth) / 2,
250
+ y: (height - this.containerHeight) / 2,
236
251
  };
237
252
  };
238
253
 
239
- handleMoveImage = (e: any): void => {
240
- const { offset, width, height } = this.getStates();
241
- const { canDragVertical, canDragHorizontal } = this.calcCanDragDirection();
254
+ getSafeTranslate = (width: number, height: number, translateX: number, translateY: number) => {
255
+ const { x: extremeX, y: extremeY } = this.getExtremeTranslate(width, height);
256
+ const { canDragVertical, canDragHorizontal } = this.getCanDragDirection(width, height);
257
+
258
+ let newTranslateX = 0,
259
+ newTranslateY = 0;
260
+
261
+ if (canDragHorizontal) {
262
+ newTranslateX = translateX > 0 ? Math.min(translateX, extremeX) : Math.max(translateX, -extremeX);
263
+ }
264
+
265
+ if (canDragVertical) {
266
+ newTranslateY = translateY > 0 ? Math.min(translateY, extremeY) : Math.max(translateY, -extremeY);
267
+ }
268
+
269
+ return {
270
+ x: newTranslateX,
271
+ y: newTranslateY
272
+ };
273
+ }
274
+
275
+ handleImageMove = (e: MouseEvent): void => {
242
276
  // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
243
277
  const mouseLeftPress = e.buttons === 1;
244
- if (mouseLeftPress && (canDragVertical || canDragHorizontal)) {
245
- const { clientX, clientY } = e;
246
- const { left: containerLeft, top: containerTop } = this._getContainerBounds();
247
- const { left: extremeLeft, top: extremeTop } = this.calcExtremeBounds();
248
- let newX = canDragHorizontal ? clientX - containerLeft - this.startMouseOffset.x : offset.x;
249
- let newY = canDragVertical ? clientY - containerTop - this.startMouseOffset.y : offset.y;
250
- if (canDragHorizontal) {
251
- newX = newX > 0 ? 0 : newX < extremeLeft ? extremeLeft : newX;
252
- }
253
- if (canDragVertical) {
254
- newY = newY > 0 ? 0 : newY < extremeTop ? extremeTop : newY;
255
- }
256
- const _offset = {
257
- x: newX,
258
- y: newY,
259
- };
278
+
279
+ if (mouseLeftPress) {
280
+ this.moveImage(e);
281
+ }
282
+ };
283
+
284
+ moveImage = (e: MouseEvent) => {
285
+ const { clientX, clientY } = e;
286
+ const { width, height, translate } = this.getStates();
287
+ const { rotation } = this.getProps();
288
+ const imageBound = this.calcBoundingRectSize(width, height, rotation);
289
+ const { canDragVertical, canDragHorizontal } = this.getCanDragDirection(imageBound.width, imageBound.height);
290
+
291
+ if (canDragVertical || canDragHorizontal) {
292
+ let newTranslateX = canDragHorizontal ? translate.x + clientX - this.startMouseClientPosition.x : translate.x;
293
+ let newTranslateY = canDragVertical ? translate.y + clientY - this.startMouseClientPosition.y : translate.y;
294
+
295
+ const newTranslate = this.getSafeTranslate(imageBound.width, imageBound.height, newTranslateX, newTranslateY);
296
+
260
297
  this.setState({
261
- offset: _offset,
262
- left: this._isImageVertical() ? _offset.x - (width - height) / 2 : _offset.x,
263
- top: this._isImageVertical() ? _offset.y + (width - height) / 2 : _offset.y,
298
+ translate: newTranslate,
264
299
  } as any);
300
+
301
+ this.startMouseClientPosition = {
302
+ x: clientX,
303
+ y: clientY
304
+ };
265
305
  }
266
306
  };
267
307
 
268
308
  handleImageMouseDown = (e: any): void => {
269
- this.startMouseOffset = this._getOffset(e);
309
+ this.startMouseClientPosition = {
310
+ x: e.clientX,
311
+ y: e.clientY
312
+ };
270
313
  };
271
314
 
315
+ // 鼠标事件的 e.offset 是以 dom 旋转前左上角为零点的, 这个方法会转换为以旋转后元素的外接矩形左上角为零点的 offset
316
+ calcBoundingRectMouseOffset = (calcBoundingRectMouseOffset: CalcBoundingRectMouseOffset) => {
317
+ const {
318
+ width,
319
+ height,
320
+ offset,
321
+ rotation = 0
322
+ } = calcBoundingRectMouseOffset;
323
+
324
+ let degrees = rotation % 360;
325
+ degrees = degrees >= 0 ? degrees : 360 + degrees;
326
+ let boundOffsetX = 0,
327
+ boundOffsetY = 0;
328
+
329
+ switch (degrees) {
330
+ case 0:
331
+ boundOffsetX = offset.x;
332
+ boundOffsetY = offset.y;
333
+ break;
334
+ case 90:
335
+ boundOffsetX = height - offset.y;
336
+ boundOffsetY = offset.x;
337
+ break;
338
+ case 180:
339
+ boundOffsetX = width - offset.x;
340
+ boundOffsetY = height - offset.y;
341
+ break;
342
+ case 270:
343
+ boundOffsetX = offset.y;
344
+ boundOffsetY = width - offset.x;
345
+ break;
346
+ default:
347
+ break;
348
+ }
349
+
350
+ return {
351
+ x: boundOffsetX,
352
+ y: boundOffsetY
353
+ };
354
+ }
272
355
  }