@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.
- package/chat/foundation.ts +18 -23
- package/datePicker/foundation.ts +1 -1
- package/image/image.scss +6 -1
- package/image/previewImageFoundation.ts +233 -150
- package/image/previewInnerFoundation.ts +11 -6
- package/lib/cjs/base/foundation.js +1 -1
- package/lib/cjs/chat/foundation.d.ts +1 -2
- package/lib/cjs/chat/foundation.js +2 -7
- package/lib/cjs/checkbox/checkboxGroupFoundation.js +1 -1
- package/lib/cjs/datePicker/foundation.js +1 -1
- package/lib/cjs/image/image.css +5 -1
- package/lib/cjs/image/image.scss +6 -1
- package/lib/cjs/image/previewImageFoundation.d.ts +46 -13
- package/lib/cjs/image/previewImageFoundation.js +207 -181
- package/lib/cjs/image/previewInnerFoundation.d.ts +4 -3
- package/lib/cjs/image/previewInnerFoundation.js +5 -2
- package/lib/cjs/navigation/foundation.d.ts +8 -8
- package/lib/cjs/navigation/itemFoundation.d.ts +5 -4
- package/lib/cjs/radio/radioInnerFoundation.js +1 -1
- package/lib/cjs/table/utils.d.ts +1 -1
- package/lib/cjs/tabs/constants.d.ts +1 -0
- package/lib/cjs/tabs/constants.js +2 -1
- package/lib/cjs/tabs/tabs.css +28 -199
- package/lib/cjs/tabs/tabs.scss +336 -297
- package/lib/cjs/tabs/variables.scss +21 -2
- package/lib/cjs/tree/treeUtil.d.ts +1 -1
- package/lib/cjs/upload/constants.d.ts +1 -1
- package/lib/es/base/foundation.js +1 -1
- package/lib/es/chat/foundation.d.ts +1 -2
- package/lib/es/chat/foundation.js +2 -7
- package/lib/es/checkbox/checkboxGroupFoundation.js +1 -1
- package/lib/es/datePicker/foundation.js +1 -1
- package/lib/es/image/image.css +5 -1
- package/lib/es/image/image.scss +6 -1
- package/lib/es/image/previewImageFoundation.d.ts +46 -13
- package/lib/es/image/previewImageFoundation.js +207 -181
- package/lib/es/image/previewInnerFoundation.d.ts +4 -3
- package/lib/es/image/previewInnerFoundation.js +5 -2
- package/lib/es/navigation/foundation.d.ts +8 -8
- package/lib/es/navigation/itemFoundation.d.ts +5 -4
- package/lib/es/radio/radioInnerFoundation.js +1 -1
- package/lib/es/table/utils.d.ts +1 -1
- package/lib/es/tabs/constants.d.ts +1 -0
- package/lib/es/tabs/constants.js +2 -1
- package/lib/es/tabs/tabs.css +28 -199
- package/lib/es/tabs/tabs.scss +336 -297
- package/lib/es/tabs/variables.scss +21 -2
- package/lib/es/tree/treeUtil.d.ts +1 -1
- package/lib/es/upload/constants.d.ts +1 -1
- package/navigation/foundation.ts +8 -8
- package/navigation/itemFoundation.ts +6 -4
- package/package.json +3 -3
- package/tabs/constants.ts +2 -1
- package/tabs/tabs.scss +336 -297
- package/tabs/variables.scss +21 -2
package/chat/foundation.ts
CHANGED
|
@@ -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: () =>
|
|
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
|
|
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
|
|
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(() => {
|
package/datePicker/foundation.ts
CHANGED
|
@@ -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
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
25
|
+
export interface Translate {
|
|
21
26
|
x: number;
|
|
22
27
|
y: number
|
|
23
28
|
}
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
|
|
60
|
+
_getContainerBoundingRectSize = () => {
|
|
61
|
+
const containerDOM = this._adapter.getContainer();
|
|
62
|
+
if (containerDOM) {
|
|
63
|
+
this.containerWidth = containerDOM.clientWidth;
|
|
64
|
+
this.containerHeight = containerDOM.clientHeight;
|
|
51
65
|
}
|
|
52
|
-
|
|
53
|
-
};
|
|
66
|
+
}
|
|
54
67
|
|
|
55
|
-
|
|
68
|
+
_getAdaptationZoom = () => {
|
|
69
|
+
let _zoom = 1;
|
|
56
70
|
const containerDOM = this._adapter.getContainer();
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
82
|
+
|
|
83
|
+
return _zoom;
|
|
61
84
|
}
|
|
62
85
|
|
|
63
|
-
|
|
64
|
-
const {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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.
|
|
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
|
-
|
|
106
|
-
|
|
130
|
+
handleRatioChange = () => {
|
|
131
|
+
this.initializeImage();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
initializeImageZoom = (notify = true) => {
|
|
107
135
|
const { currZoom } = this.getStates();
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
195
|
+
changeZoom = (newZoom: number, e?: WheelEvent): void => {
|
|
182
196
|
const imageDOM = this._adapter.getImage();
|
|
183
|
-
const {
|
|
184
|
-
const
|
|
185
|
-
const
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
249
|
+
x: (width - this.containerWidth) / 2,
|
|
250
|
+
y: (height - this.containerHeight) / 2,
|
|
236
251
|
};
|
|
237
252
|
};
|
|
238
253
|
|
|
239
|
-
|
|
240
|
-
const {
|
|
241
|
-
const { canDragVertical, canDragHorizontal } = this.
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
}
|