@scx-js/scx-dom 0.0.1 → 0.0.2
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/Download.js +67 -67
- package/FixedElement.js +26 -26
- package/HtmlToText.js +7 -7
- package/ScxDrag.js +255 -255
- package/Watermark.js +105 -105
- package/index.js +5 -5
- package/package.json +13 -13
package/Download.js
CHANGED
@@ -1,67 +1,67 @@
|
|
1
|
-
import {isNull, isString, notBlank, notNull} from "@scx-js/scx-common";
|
2
|
-
|
3
|
-
function getFileNameFromContentDisposition(str) {
|
4
|
-
if (!str) {
|
5
|
-
return null;
|
6
|
-
}
|
7
|
-
const s = str.split(/;\s*/);
|
8
|
-
//优先使用 filename* 请查看 : https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
|
9
|
-
let fileNameElement = s.filter(c => c.startsWith("filename*="))[0];
|
10
|
-
if (!fileNameElement) {
|
11
|
-
fileNameElement = s.filter(c => c.startsWith("filename="))[0];
|
12
|
-
}
|
13
|
-
if (!fileNameElement) {// 两者都没有
|
14
|
-
return null;
|
15
|
-
}
|
16
|
-
const fileNameWithCharset = fileNameElement.split("=")[1];
|
17
|
-
const fs = fileNameWithCharset.split("''");
|
18
|
-
//这里还可以获取到 charset 但是用不到 所以就管了
|
19
|
-
const encodedFileName = fs[fs.length - 1];
|
20
|
-
return notBlank(encodedFileName) ? decodeURIComponent(encodedFileName) : null;
|
21
|
-
}
|
22
|
-
|
23
|
-
/**
|
24
|
-
* 根据 url 下载文件
|
25
|
-
* @param url {string|Blob} 下载地址
|
26
|
-
* @param fileName 下载文件的名称 为空取文件原始名
|
27
|
-
*/
|
28
|
-
function download(url, fileName = null) {
|
29
|
-
const link = document.createElement("a");
|
30
|
-
link.rel = "noopener";
|
31
|
-
link.target = "_blank";
|
32
|
-
|
33
|
-
link.download = notNull(fileName) ? fileName : "";
|
34
|
-
|
35
|
-
if (isString(url) || url instanceof URL) { // 标准 URL
|
36
|
-
link.href = url;
|
37
|
-
if (link.origin !== location.origin) {
|
38
|
-
fetch(link.href, {method: "GET"}).then(r => {
|
39
|
-
if (r.ok) {
|
40
|
-
if (isNull(fileName)) { //如果原有的下载名称为空 我们尝试使用 content-disposition 中的文件名
|
41
|
-
const f = getFileNameFromContentDisposition(r.headers.get("content-disposition"));
|
42
|
-
if (notNull(f)) {
|
43
|
-
fileName = f;
|
44
|
-
}
|
45
|
-
}
|
46
|
-
return r.blob();
|
47
|
-
} else {
|
48
|
-
throw new Error(r.status + " : " + r.statusText);
|
49
|
-
}
|
50
|
-
}).then(b => download(b, fileName)).catch(e => {
|
51
|
-
console.error(e);
|
52
|
-
link.click();
|
53
|
-
});
|
54
|
-
} else {
|
55
|
-
link.click();
|
56
|
-
}
|
57
|
-
} else { // Blob 文件
|
58
|
-
link.href = URL.createObjectURL(url);
|
59
|
-
setTimeout(() => URL.revokeObjectURL(link.href), 4E4); // 40s
|
60
|
-
setTimeout(() => link.click(), 0);
|
61
|
-
}
|
62
|
-
}
|
63
|
-
|
64
|
-
export {
|
65
|
-
download,
|
66
|
-
getFileNameFromContentDisposition,
|
67
|
-
};
|
1
|
+
import {isNull, isString, notBlank, notNull} from "@scx-js/scx-common";
|
2
|
+
|
3
|
+
function getFileNameFromContentDisposition(str) {
|
4
|
+
if (!str) {
|
5
|
+
return null;
|
6
|
+
}
|
7
|
+
const s = str.split(/;\s*/);
|
8
|
+
//优先使用 filename* 请查看 : https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
|
9
|
+
let fileNameElement = s.filter(c => c.startsWith("filename*="))[0];
|
10
|
+
if (!fileNameElement) {
|
11
|
+
fileNameElement = s.filter(c => c.startsWith("filename="))[0];
|
12
|
+
}
|
13
|
+
if (!fileNameElement) {// 两者都没有
|
14
|
+
return null;
|
15
|
+
}
|
16
|
+
const fileNameWithCharset = fileNameElement.split("=")[1];
|
17
|
+
const fs = fileNameWithCharset.split("''");
|
18
|
+
//这里还可以获取到 charset 但是用不到 所以就管了
|
19
|
+
const encodedFileName = fs[fs.length - 1];
|
20
|
+
return notBlank(encodedFileName) ? decodeURIComponent(encodedFileName) : null;
|
21
|
+
}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* 根据 url 下载文件
|
25
|
+
* @param url {string|Blob} 下载地址
|
26
|
+
* @param fileName 下载文件的名称 为空取文件原始名
|
27
|
+
*/
|
28
|
+
function download(url, fileName = null) {
|
29
|
+
const link = document.createElement("a");
|
30
|
+
link.rel = "noopener";
|
31
|
+
link.target = "_blank";
|
32
|
+
|
33
|
+
link.download = notNull(fileName) ? fileName : "";
|
34
|
+
|
35
|
+
if (isString(url) || url instanceof URL) { // 标准 URL
|
36
|
+
link.href = url;
|
37
|
+
if (link.origin !== location.origin) {
|
38
|
+
fetch(link.href, {method: "GET"}).then(r => {
|
39
|
+
if (r.ok) {
|
40
|
+
if (isNull(fileName)) { //如果原有的下载名称为空 我们尝试使用 content-disposition 中的文件名
|
41
|
+
const f = getFileNameFromContentDisposition(r.headers.get("content-disposition"));
|
42
|
+
if (notNull(f)) {
|
43
|
+
fileName = f;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return r.blob();
|
47
|
+
} else {
|
48
|
+
throw new Error(r.status + " : " + r.statusText);
|
49
|
+
}
|
50
|
+
}).then(b => download(b, fileName)).catch(e => {
|
51
|
+
console.error(e);
|
52
|
+
link.click();
|
53
|
+
});
|
54
|
+
} else {
|
55
|
+
link.click();
|
56
|
+
}
|
57
|
+
} else { // Blob 文件
|
58
|
+
link.href = URL.createObjectURL(url);
|
59
|
+
setTimeout(() => URL.revokeObjectURL(link.href), 4E4); // 40s
|
60
|
+
setTimeout(() => link.click(), 0);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
export {
|
65
|
+
download,
|
66
|
+
getFileNameFromContentDisposition,
|
67
|
+
};
|
package/FixedElement.js
CHANGED
@@ -1,26 +1,26 @@
|
|
1
|
-
/**
|
2
|
-
* 将元素移除文档流并固定在原位置 一般用在 before-leave 上 使用方法如下
|
3
|
-
* <transition-group name="xxx" @before-leave="fixedElement">
|
4
|
-
* <div v-for="(item,i) in list" :key="item">
|
5
|
-
* {{item}}
|
6
|
-
* </div>
|
7
|
-
* </transition-group>
|
8
|
-
* @param el
|
9
|
-
*/
|
10
|
-
function fixedElement(el) {
|
11
|
-
const {
|
12
|
-
marginLeft,
|
13
|
-
marginTop,
|
14
|
-
width,
|
15
|
-
height,
|
16
|
-
} = window.getComputedStyle(el);
|
17
|
-
el.style.left = `${el.offsetLeft - parseFloat(marginLeft)}px`;
|
18
|
-
el.style.top = `${el.offsetTop - parseFloat(marginTop)}px`;
|
19
|
-
el.style.width = width;
|
20
|
-
el.style.height = height;
|
21
|
-
el.style.position = "absolute";
|
22
|
-
}
|
23
|
-
|
24
|
-
export {
|
25
|
-
fixedElement,
|
26
|
-
};
|
1
|
+
/**
|
2
|
+
* 将元素移除文档流并固定在原位置 一般用在 before-leave 上 使用方法如下
|
3
|
+
* <transition-group name="xxx" @before-leave="fixedElement">
|
4
|
+
* <div v-for="(item,i) in list" :key="item">
|
5
|
+
* {{item}}
|
6
|
+
* </div>
|
7
|
+
* </transition-group>
|
8
|
+
* @param el
|
9
|
+
*/
|
10
|
+
function fixedElement(el) {
|
11
|
+
const {
|
12
|
+
marginLeft,
|
13
|
+
marginTop,
|
14
|
+
width,
|
15
|
+
height,
|
16
|
+
} = window.getComputedStyle(el);
|
17
|
+
el.style.left = `${el.offsetLeft - parseFloat(marginLeft)}px`;
|
18
|
+
el.style.top = `${el.offsetTop - parseFloat(marginTop)}px`;
|
19
|
+
el.style.width = width;
|
20
|
+
el.style.height = height;
|
21
|
+
el.style.position = "absolute";
|
22
|
+
}
|
23
|
+
|
24
|
+
export {
|
25
|
+
fixedElement,
|
26
|
+
};
|
package/HtmlToText.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
function htmlToText(html) {
|
2
|
-
const tempDiv = document.createElement("div");
|
3
|
-
tempDiv.innerHTML = html;
|
4
|
-
return tempDiv.innerText;
|
5
|
-
}
|
6
|
-
|
7
|
-
export {htmlToText};
|
1
|
+
function htmlToText(html) {
|
2
|
+
const tempDiv = document.createElement("div");
|
3
|
+
tempDiv.innerHTML = html;
|
4
|
+
return tempDiv.innerText;
|
5
|
+
}
|
6
|
+
|
7
|
+
export {htmlToText};
|
package/ScxDrag.js
CHANGED
@@ -1,255 +1,255 @@
|
|
1
|
-
import {isFunction, isObject, rafThrottle} from "@scx-js/scx-common";
|
2
|
-
|
3
|
-
/**
|
4
|
-
* 获取视图的上下界限
|
5
|
-
* @return {{top: number, left: number, bottom: number, right: number}}
|
6
|
-
*/
|
7
|
-
function getViewBounds() {
|
8
|
-
//当前视图的宽高
|
9
|
-
const clientWidth = document.documentElement.clientWidth;
|
10
|
-
const clientHeight = document.documentElement.clientHeight;
|
11
|
-
|
12
|
-
//计算上下界限
|
13
|
-
return {
|
14
|
-
left: 0,
|
15
|
-
top: 0,
|
16
|
-
right: clientWidth,
|
17
|
-
bottom: clientHeight,
|
18
|
-
};
|
19
|
-
}
|
20
|
-
|
21
|
-
class ScxDrag {
|
22
|
-
|
23
|
-
/**
|
24
|
-
* 目标元素
|
25
|
-
*/
|
26
|
-
targetElement;
|
27
|
-
|
28
|
-
/**
|
29
|
-
* 可拖拽区域元素
|
30
|
-
*/
|
31
|
-
dragElement;
|
32
|
-
|
33
|
-
//存取鼠标按下时的初始状态 (包含元素的初始矩阵,鼠标位置,上下界限等)
|
34
|
-
startX;
|
35
|
-
startY;
|
36
|
-
startMatrix;
|
37
|
-
|
38
|
-
//判断为点击事件的距离
|
39
|
-
v = 10;
|
40
|
-
|
41
|
-
//当前状态
|
42
|
-
isMoving = false;
|
43
|
-
|
44
|
-
//用户自定义的 上下界
|
45
|
-
bounds;
|
46
|
-
|
47
|
-
//用户自定义的 回调
|
48
|
-
onClick;
|
49
|
-
onDragStart;
|
50
|
-
onDrag;
|
51
|
-
onDragEnd;
|
52
|
-
|
53
|
-
//上下界限 用于加速计算
|
54
|
-
minLeft;
|
55
|
-
minTop;
|
56
|
-
maxLeft;
|
57
|
-
maxTop;
|
58
|
-
|
59
|
-
//当前触摸点
|
60
|
-
nowTouch;
|
61
|
-
|
62
|
-
/**
|
63
|
-
*
|
64
|
-
* @param targetElement
|
65
|
-
* @param dragElement
|
66
|
-
* @param callback
|
67
|
-
* @param bounds {Function||{top,left,bottom,right}}
|
68
|
-
*/
|
69
|
-
constructor(targetElement, {
|
70
|
-
dragElement = targetElement, //默认使用 targetElement
|
71
|
-
onClick = (el) => {
|
72
|
-
},
|
73
|
-
onDragStart = (el) => {
|
74
|
-
},
|
75
|
-
onDrag = (el) => {
|
76
|
-
},
|
77
|
-
onDragEnd = (el) => {
|
78
|
-
},
|
79
|
-
bounds,// 自定义的界限
|
80
|
-
} = {}) {
|
81
|
-
this.targetElement = targetElement;
|
82
|
-
this.dragElement = dragElement;
|
83
|
-
this.onClick = onClick;
|
84
|
-
this.onDragStart = onDragStart;
|
85
|
-
this.onDrag = onDrag;
|
86
|
-
this.onDragEnd = onDragEnd;
|
87
|
-
this.bounds = bounds;
|
88
|
-
//节流
|
89
|
-
this.update = rafThrottle(this.update);
|
90
|
-
//强制绑定 this 指向 防止 addEventListener 改变 this 指向
|
91
|
-
this.onMousedown = this.onMousedown.bind(this);
|
92
|
-
this.onMousemove = this.onMousemove.bind(this);
|
93
|
-
this.onMouseup = this.onMouseup.bind(this);
|
94
|
-
this.onTouchstart = this.onTouchstart.bind(this);
|
95
|
-
this.onTouchmove = this.onTouchmove.bind(this);
|
96
|
-
this.onTouchend = this.onTouchend.bind(this);
|
97
|
-
}
|
98
|
-
|
99
|
-
//获取上下界 默认采用 浏览器视图
|
100
|
-
getBounds() {
|
101
|
-
if (isFunction(this.bounds)) {
|
102
|
-
return this.bounds(getViewBounds());
|
103
|
-
} else if (isObject(this.bounds)) {
|
104
|
-
return this.bounds;
|
105
|
-
} else {
|
106
|
-
return getViewBounds();
|
107
|
-
}
|
108
|
-
}
|
109
|
-
|
110
|
-
//判断坐标与start 的差值是否小于指定值
|
111
|
-
isMove(x, y) {
|
112
|
-
return Math.abs(this.startX - x) > this.v || Math.abs(this.startY - y) > this.v;
|
113
|
-
}
|
114
|
-
|
115
|
-
//当多指移动时 我们只需要获取当前的移动事件
|
116
|
-
filterTouchEvent(event) {
|
117
|
-
let touch;
|
118
|
-
for (let changedTouch of event.changedTouches) {
|
119
|
-
if (changedTouch.identifier === this.nowTouch.identifier) {
|
120
|
-
touch = changedTouch;
|
121
|
-
}
|
122
|
-
}
|
123
|
-
return touch;
|
124
|
-
}
|
125
|
-
|
126
|
-
onMousedown(event) {
|
127
|
-
//我们只响应左键
|
128
|
-
if (event.button !== 0) {
|
129
|
-
return;
|
130
|
-
}
|
131
|
-
//获取鼠标初始点击位置
|
132
|
-
this.onStart(event.clientX, event.clientY);
|
133
|
-
|
134
|
-
document.addEventListener("mousemove", this.onMousemove);
|
135
|
-
document.addEventListener("mouseup", this.onMouseup);
|
136
|
-
}
|
137
|
-
|
138
|
-
onTouchstart(event) {
|
139
|
-
//停止事件传播
|
140
|
-
event.stopPropagation();
|
141
|
-
event.preventDefault();
|
142
|
-
//设置最后一个 触摸点
|
143
|
-
this.nowTouch = event.changedTouches[event.changedTouches.length - 1];
|
144
|
-
|
145
|
-
this.onStart(this.nowTouch.clientX, this.nowTouch.clientY);
|
146
|
-
|
147
|
-
document.addEventListener("touchmove", this.onTouchmove);
|
148
|
-
document.addEventListener("touchend", this.onTouchend);
|
149
|
-
}
|
150
|
-
|
151
|
-
onMousemove(event) {
|
152
|
-
this.onMove(event.clientX, event.clientY);
|
153
|
-
}
|
154
|
-
|
155
|
-
onTouchmove(event) {
|
156
|
-
let touch = this.filterTouchEvent(event);
|
157
|
-
if (touch) {
|
158
|
-
this.onMove(touch.clientX, touch.clientY);
|
159
|
-
}
|
160
|
-
}
|
161
|
-
|
162
|
-
onMouseup(e) {
|
163
|
-
this.onEnd(e);
|
164
|
-
document.removeEventListener("mousemove", this.onMousemove);
|
165
|
-
document.removeEventListener("mouseup", this.onMouseup);
|
166
|
-
}
|
167
|
-
|
168
|
-
onTouchend(e) {
|
169
|
-
let touch = this.filterTouchEvent(e);
|
170
|
-
if (touch) {
|
171
|
-
this.onEnd(e);
|
172
|
-
document.removeEventListener("touchmove", this.onTouchmove);
|
173
|
-
document.removeEventListener("touchend", this.onTouchend);
|
174
|
-
}
|
175
|
-
}
|
176
|
-
|
177
|
-
onStart(x, y) {
|
178
|
-
this.startX = x;
|
179
|
-
this.startY = y;
|
180
|
-
|
181
|
-
//获取元素当前的矩阵状态
|
182
|
-
this.startMatrix = new DOMMatrix(window.getComputedStyle(this.targetElement).transform);
|
183
|
-
|
184
|
-
//获取元素当前的位置
|
185
|
-
const {
|
186
|
-
left,
|
187
|
-
top,
|
188
|
-
bottom,
|
189
|
-
right,
|
190
|
-
} = this.targetElement.getBoundingClientRect();
|
191
|
-
|
192
|
-
//计算上下界限
|
193
|
-
const bounds = this.getBounds();
|
194
|
-
|
195
|
-
//计算上下界限
|
196
|
-
this.minLeft = bounds.left - left;
|
197
|
-
this.minTop = bounds.top - top;
|
198
|
-
this.maxLeft = bounds.right - right;
|
199
|
-
this.maxTop = bounds.bottom - bottom;
|
200
|
-
}
|
201
|
-
|
202
|
-
onMove(x, y) {
|
203
|
-
if (this.isMoving || this.isMove(x, y)) {
|
204
|
-
this.update(x, y);
|
205
|
-
if (!this.isMoving) {
|
206
|
-
this.isMoving = true;
|
207
|
-
this.onDragStart(this.targetElement);
|
208
|
-
}
|
209
|
-
this.onDrag(this.targetElement);
|
210
|
-
}
|
211
|
-
}
|
212
|
-
|
213
|
-
onEnd(e) {
|
214
|
-
//需等待 update 完成后
|
215
|
-
this.update.cancel();
|
216
|
-
if (this.isMoving) {
|
217
|
-
this.onDragEnd(this.targetElement, this.startMatrix, e);
|
218
|
-
} else {
|
219
|
-
this.onClick(this.targetElement);
|
220
|
-
}
|
221
|
-
this.isMoving = false;
|
222
|
-
}
|
223
|
-
|
224
|
-
update(X, Y) {
|
225
|
-
let moveX = Math.min(Math.max(X - this.startX, this.minLeft), this.maxLeft);
|
226
|
-
let moveY = Math.min(Math.max(Y - this.startY, this.minTop), this.maxTop);
|
227
|
-
//todo 这里若元素有放大旋转等操作会导致错误的移动
|
228
|
-
//这里距离需要进行重新计算
|
229
|
-
moveX = moveX / this.startMatrix.a;
|
230
|
-
moveY = moveY / this.startMatrix.d;
|
231
|
-
//旋转等操作待处理
|
232
|
-
|
233
|
-
const newMatrix = this.startMatrix.translate(moveX, moveY);
|
234
|
-
|
235
|
-
this.targetElement.style.transform = newMatrix.toString();
|
236
|
-
}
|
237
|
-
|
238
|
-
enable() {
|
239
|
-
this.dragElement.addEventListener("mousedown", this.onMousedown);
|
240
|
-
this.dragElement.addEventListener("touchstart", this.onTouchstart);
|
241
|
-
return this;
|
242
|
-
}
|
243
|
-
|
244
|
-
disable() {
|
245
|
-
this.dragElement.removeEventListener("mousedown", this.onMousedown);
|
246
|
-
this.dragElement.removeEventListener("touchstart", this.onTouchstart);
|
247
|
-
return this;
|
248
|
-
}
|
249
|
-
|
250
|
-
}
|
251
|
-
|
252
|
-
export {
|
253
|
-
ScxDrag,
|
254
|
-
getViewBounds,
|
255
|
-
};
|
1
|
+
import {isFunction, isObject, rafThrottle} from "@scx-js/scx-common";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 获取视图的上下界限
|
5
|
+
* @return {{top: number, left: number, bottom: number, right: number}}
|
6
|
+
*/
|
7
|
+
function getViewBounds() {
|
8
|
+
//当前视图的宽高
|
9
|
+
const clientWidth = document.documentElement.clientWidth;
|
10
|
+
const clientHeight = document.documentElement.clientHeight;
|
11
|
+
|
12
|
+
//计算上下界限
|
13
|
+
return {
|
14
|
+
left: 0,
|
15
|
+
top: 0,
|
16
|
+
right: clientWidth,
|
17
|
+
bottom: clientHeight,
|
18
|
+
};
|
19
|
+
}
|
20
|
+
|
21
|
+
class ScxDrag {
|
22
|
+
|
23
|
+
/**
|
24
|
+
* 目标元素
|
25
|
+
*/
|
26
|
+
targetElement;
|
27
|
+
|
28
|
+
/**
|
29
|
+
* 可拖拽区域元素
|
30
|
+
*/
|
31
|
+
dragElement;
|
32
|
+
|
33
|
+
//存取鼠标按下时的初始状态 (包含元素的初始矩阵,鼠标位置,上下界限等)
|
34
|
+
startX;
|
35
|
+
startY;
|
36
|
+
startMatrix;
|
37
|
+
|
38
|
+
//判断为点击事件的距离
|
39
|
+
v = 10;
|
40
|
+
|
41
|
+
//当前状态
|
42
|
+
isMoving = false;
|
43
|
+
|
44
|
+
//用户自定义的 上下界
|
45
|
+
bounds;
|
46
|
+
|
47
|
+
//用户自定义的 回调
|
48
|
+
onClick;
|
49
|
+
onDragStart;
|
50
|
+
onDrag;
|
51
|
+
onDragEnd;
|
52
|
+
|
53
|
+
//上下界限 用于加速计算
|
54
|
+
minLeft;
|
55
|
+
minTop;
|
56
|
+
maxLeft;
|
57
|
+
maxTop;
|
58
|
+
|
59
|
+
//当前触摸点
|
60
|
+
nowTouch;
|
61
|
+
|
62
|
+
/**
|
63
|
+
*
|
64
|
+
* @param targetElement
|
65
|
+
* @param dragElement
|
66
|
+
* @param callback
|
67
|
+
* @param bounds {Function||{top,left,bottom,right}}
|
68
|
+
*/
|
69
|
+
constructor(targetElement, {
|
70
|
+
dragElement = targetElement, //默认使用 targetElement
|
71
|
+
onClick = (el) => {
|
72
|
+
},
|
73
|
+
onDragStart = (el) => {
|
74
|
+
},
|
75
|
+
onDrag = (el) => {
|
76
|
+
},
|
77
|
+
onDragEnd = (el) => {
|
78
|
+
},
|
79
|
+
bounds,// 自定义的界限
|
80
|
+
} = {}) {
|
81
|
+
this.targetElement = targetElement;
|
82
|
+
this.dragElement = dragElement;
|
83
|
+
this.onClick = onClick;
|
84
|
+
this.onDragStart = onDragStart;
|
85
|
+
this.onDrag = onDrag;
|
86
|
+
this.onDragEnd = onDragEnd;
|
87
|
+
this.bounds = bounds;
|
88
|
+
//节流
|
89
|
+
this.update = rafThrottle(this.update);
|
90
|
+
//强制绑定 this 指向 防止 addEventListener 改变 this 指向
|
91
|
+
this.onMousedown = this.onMousedown.bind(this);
|
92
|
+
this.onMousemove = this.onMousemove.bind(this);
|
93
|
+
this.onMouseup = this.onMouseup.bind(this);
|
94
|
+
this.onTouchstart = this.onTouchstart.bind(this);
|
95
|
+
this.onTouchmove = this.onTouchmove.bind(this);
|
96
|
+
this.onTouchend = this.onTouchend.bind(this);
|
97
|
+
}
|
98
|
+
|
99
|
+
//获取上下界 默认采用 浏览器视图
|
100
|
+
getBounds() {
|
101
|
+
if (isFunction(this.bounds)) {
|
102
|
+
return this.bounds(getViewBounds());
|
103
|
+
} else if (isObject(this.bounds)) {
|
104
|
+
return this.bounds;
|
105
|
+
} else {
|
106
|
+
return getViewBounds();
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
//判断坐标与start 的差值是否小于指定值
|
111
|
+
isMove(x, y) {
|
112
|
+
return Math.abs(this.startX - x) > this.v || Math.abs(this.startY - y) > this.v;
|
113
|
+
}
|
114
|
+
|
115
|
+
//当多指移动时 我们只需要获取当前的移动事件
|
116
|
+
filterTouchEvent(event) {
|
117
|
+
let touch;
|
118
|
+
for (let changedTouch of event.changedTouches) {
|
119
|
+
if (changedTouch.identifier === this.nowTouch.identifier) {
|
120
|
+
touch = changedTouch;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
return touch;
|
124
|
+
}
|
125
|
+
|
126
|
+
onMousedown(event) {
|
127
|
+
//我们只响应左键
|
128
|
+
if (event.button !== 0) {
|
129
|
+
return;
|
130
|
+
}
|
131
|
+
//获取鼠标初始点击位置
|
132
|
+
this.onStart(event.clientX, event.clientY);
|
133
|
+
|
134
|
+
document.addEventListener("mousemove", this.onMousemove);
|
135
|
+
document.addEventListener("mouseup", this.onMouseup);
|
136
|
+
}
|
137
|
+
|
138
|
+
onTouchstart(event) {
|
139
|
+
//停止事件传播
|
140
|
+
event.stopPropagation();
|
141
|
+
event.preventDefault();
|
142
|
+
//设置最后一个 触摸点
|
143
|
+
this.nowTouch = event.changedTouches[event.changedTouches.length - 1];
|
144
|
+
|
145
|
+
this.onStart(this.nowTouch.clientX, this.nowTouch.clientY);
|
146
|
+
|
147
|
+
document.addEventListener("touchmove", this.onTouchmove);
|
148
|
+
document.addEventListener("touchend", this.onTouchend);
|
149
|
+
}
|
150
|
+
|
151
|
+
onMousemove(event) {
|
152
|
+
this.onMove(event.clientX, event.clientY);
|
153
|
+
}
|
154
|
+
|
155
|
+
onTouchmove(event) {
|
156
|
+
let touch = this.filterTouchEvent(event);
|
157
|
+
if (touch) {
|
158
|
+
this.onMove(touch.clientX, touch.clientY);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
onMouseup(e) {
|
163
|
+
this.onEnd(e);
|
164
|
+
document.removeEventListener("mousemove", this.onMousemove);
|
165
|
+
document.removeEventListener("mouseup", this.onMouseup);
|
166
|
+
}
|
167
|
+
|
168
|
+
onTouchend(e) {
|
169
|
+
let touch = this.filterTouchEvent(e);
|
170
|
+
if (touch) {
|
171
|
+
this.onEnd(e);
|
172
|
+
document.removeEventListener("touchmove", this.onTouchmove);
|
173
|
+
document.removeEventListener("touchend", this.onTouchend);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
onStart(x, y) {
|
178
|
+
this.startX = x;
|
179
|
+
this.startY = y;
|
180
|
+
|
181
|
+
//获取元素当前的矩阵状态
|
182
|
+
this.startMatrix = new DOMMatrix(window.getComputedStyle(this.targetElement).transform);
|
183
|
+
|
184
|
+
//获取元素当前的位置
|
185
|
+
const {
|
186
|
+
left,
|
187
|
+
top,
|
188
|
+
bottom,
|
189
|
+
right,
|
190
|
+
} = this.targetElement.getBoundingClientRect();
|
191
|
+
|
192
|
+
//计算上下界限
|
193
|
+
const bounds = this.getBounds();
|
194
|
+
|
195
|
+
//计算上下界限
|
196
|
+
this.minLeft = bounds.left - left;
|
197
|
+
this.minTop = bounds.top - top;
|
198
|
+
this.maxLeft = bounds.right - right;
|
199
|
+
this.maxTop = bounds.bottom - bottom;
|
200
|
+
}
|
201
|
+
|
202
|
+
onMove(x, y) {
|
203
|
+
if (this.isMoving || this.isMove(x, y)) {
|
204
|
+
this.update(x, y);
|
205
|
+
if (!this.isMoving) {
|
206
|
+
this.isMoving = true;
|
207
|
+
this.onDragStart(this.targetElement);
|
208
|
+
}
|
209
|
+
this.onDrag(this.targetElement);
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
onEnd(e) {
|
214
|
+
//需等待 update 完成后
|
215
|
+
this.update.cancel();
|
216
|
+
if (this.isMoving) {
|
217
|
+
this.onDragEnd(this.targetElement, this.startMatrix, e);
|
218
|
+
} else {
|
219
|
+
this.onClick(this.targetElement);
|
220
|
+
}
|
221
|
+
this.isMoving = false;
|
222
|
+
}
|
223
|
+
|
224
|
+
update(X, Y) {
|
225
|
+
let moveX = Math.min(Math.max(X - this.startX, this.minLeft), this.maxLeft);
|
226
|
+
let moveY = Math.min(Math.max(Y - this.startY, this.minTop), this.maxTop);
|
227
|
+
//todo 这里若元素有放大旋转等操作会导致错误的移动
|
228
|
+
//这里距离需要进行重新计算
|
229
|
+
moveX = moveX / this.startMatrix.a;
|
230
|
+
moveY = moveY / this.startMatrix.d;
|
231
|
+
//旋转等操作待处理
|
232
|
+
|
233
|
+
const newMatrix = this.startMatrix.translate(moveX, moveY);
|
234
|
+
|
235
|
+
this.targetElement.style.transform = newMatrix.toString();
|
236
|
+
}
|
237
|
+
|
238
|
+
enable() {
|
239
|
+
this.dragElement.addEventListener("mousedown", this.onMousedown);
|
240
|
+
this.dragElement.addEventListener("touchstart", this.onTouchstart);
|
241
|
+
return this;
|
242
|
+
}
|
243
|
+
|
244
|
+
disable() {
|
245
|
+
this.dragElement.removeEventListener("mousedown", this.onMousedown);
|
246
|
+
this.dragElement.removeEventListener("touchstart", this.onTouchstart);
|
247
|
+
return this;
|
248
|
+
}
|
249
|
+
|
250
|
+
}
|
251
|
+
|
252
|
+
export {
|
253
|
+
ScxDrag,
|
254
|
+
getViewBounds,
|
255
|
+
};
|
package/Watermark.js
CHANGED
@@ -1,105 +1,105 @@
|
|
1
|
-
import {randomUUID} from "@scx-js/scx-common";
|
2
|
-
|
3
|
-
class Watermark {
|
4
|
-
text;
|
5
|
-
timer = null;
|
6
|
-
|
7
|
-
constructor(text = "SCX") {
|
8
|
-
this.text = text;
|
9
|
-
this.watermarkTextBgDomID = randomUUID();
|
10
|
-
this.watermarkImage = getWatermarkImage(this.text);
|
11
|
-
this.watermarkTextBg = document.createElement("div");
|
12
|
-
this.watermarkTextBg.id = this.watermarkTextBgDomID;
|
13
|
-
this.setDivStyle(this.watermarkTextBg);
|
14
|
-
|
15
|
-
this.createDom = this.createDom.bind(this);
|
16
|
-
}
|
17
|
-
|
18
|
-
|
19
|
-
setDivStyle(d) {
|
20
|
-
d.style.pointerEvents = "none";
|
21
|
-
d.style.top = "0px";
|
22
|
-
d.style.left = "0px";
|
23
|
-
d.style.position = "fixed";
|
24
|
-
d.style.zIndex = "990000";
|
25
|
-
d.style.width = "100%";
|
26
|
-
d.style.height = "100%";
|
27
|
-
d.style.background = "url(" + this.watermarkImage + ") left top repeat";
|
28
|
-
}
|
29
|
-
|
30
|
-
/**
|
31
|
-
* 开启水印
|
32
|
-
*/
|
33
|
-
openWatermark() {
|
34
|
-
this.createDom();
|
35
|
-
this.removeEvent();
|
36
|
-
this.createEvent();
|
37
|
-
}
|
38
|
-
|
39
|
-
/**
|
40
|
-
* 清除水印
|
41
|
-
*/
|
42
|
-
clearWatermark() {
|
43
|
-
this.removeDom();
|
44
|
-
this.removeEvent();
|
45
|
-
}
|
46
|
-
|
47
|
-
/**
|
48
|
-
* 添加水印
|
49
|
-
*/
|
50
|
-
createDom() {
|
51
|
-
const domId = document.getElementById(this.watermarkTextBgDomID);
|
52
|
-
if (domId) {
|
53
|
-
this.setDivStyle(domId);
|
54
|
-
} else {
|
55
|
-
document.body.appendChild(this.watermarkTextBg);
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
|
-
removeDom() {
|
60
|
-
const domId = document.getElementById(this.watermarkTextBgDomID);
|
61
|
-
if (domId) {
|
62
|
-
document.body.removeChild(domId);
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
createEvent() {
|
67
|
-
window.addEventListener("resize", this.createDom);
|
68
|
-
this.timer = setInterval(() => {
|
69
|
-
this.createDom();
|
70
|
-
}, 5000);
|
71
|
-
}
|
72
|
-
|
73
|
-
removeEvent() {
|
74
|
-
window.removeEventListener("resize", this.createDom);
|
75
|
-
clearInterval(this.timer);
|
76
|
-
this.timer = null;
|
77
|
-
}
|
78
|
-
|
79
|
-
}
|
80
|
-
|
81
|
-
|
82
|
-
function getWatermarkImage(text) {
|
83
|
-
|
84
|
-
const canvas = document.createElement("canvas");
|
85
|
-
canvas.width = 300;
|
86
|
-
canvas.height = 200;
|
87
|
-
|
88
|
-
const watermarkText = canvas.getContext("2d");
|
89
|
-
watermarkText.rotate((-20 * Math.PI) / 180);
|
90
|
-
watermarkText.font = "15px Arial";
|
91
|
-
const gradient = watermarkText.createLinearGradient(0, 0, 150, 0);
|
92
|
-
gradient.addColorStop(0, "magenta");
|
93
|
-
gradient.addColorStop(1.0, "blue");
|
94
|
-
watermarkText.fillStyle = gradient;
|
95
|
-
watermarkText.textAlign = "center";
|
96
|
-
watermarkText.textBaseline = "middle";
|
97
|
-
watermarkText.fillText(text, canvas.width * 0.3, canvas.height * 0.7);
|
98
|
-
|
99
|
-
return canvas.toDataURL("image/png", 1);
|
100
|
-
}
|
101
|
-
|
102
|
-
|
103
|
-
export {
|
104
|
-
Watermark,
|
105
|
-
};
|
1
|
+
import {randomUUID} from "@scx-js/scx-common";
|
2
|
+
|
3
|
+
class Watermark {
|
4
|
+
text;
|
5
|
+
timer = null;
|
6
|
+
|
7
|
+
constructor(text = "SCX") {
|
8
|
+
this.text = text;
|
9
|
+
this.watermarkTextBgDomID = randomUUID();
|
10
|
+
this.watermarkImage = getWatermarkImage(this.text);
|
11
|
+
this.watermarkTextBg = document.createElement("div");
|
12
|
+
this.watermarkTextBg.id = this.watermarkTextBgDomID;
|
13
|
+
this.setDivStyle(this.watermarkTextBg);
|
14
|
+
|
15
|
+
this.createDom = this.createDom.bind(this);
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
setDivStyle(d) {
|
20
|
+
d.style.pointerEvents = "none";
|
21
|
+
d.style.top = "0px";
|
22
|
+
d.style.left = "0px";
|
23
|
+
d.style.position = "fixed";
|
24
|
+
d.style.zIndex = "990000";
|
25
|
+
d.style.width = "100%";
|
26
|
+
d.style.height = "100%";
|
27
|
+
d.style.background = "url(" + this.watermarkImage + ") left top repeat";
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
* 开启水印
|
32
|
+
*/
|
33
|
+
openWatermark() {
|
34
|
+
this.createDom();
|
35
|
+
this.removeEvent();
|
36
|
+
this.createEvent();
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* 清除水印
|
41
|
+
*/
|
42
|
+
clearWatermark() {
|
43
|
+
this.removeDom();
|
44
|
+
this.removeEvent();
|
45
|
+
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* 添加水印
|
49
|
+
*/
|
50
|
+
createDom() {
|
51
|
+
const domId = document.getElementById(this.watermarkTextBgDomID);
|
52
|
+
if (domId) {
|
53
|
+
this.setDivStyle(domId);
|
54
|
+
} else {
|
55
|
+
document.body.appendChild(this.watermarkTextBg);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
removeDom() {
|
60
|
+
const domId = document.getElementById(this.watermarkTextBgDomID);
|
61
|
+
if (domId) {
|
62
|
+
document.body.removeChild(domId);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
createEvent() {
|
67
|
+
window.addEventListener("resize", this.createDom);
|
68
|
+
this.timer = setInterval(() => {
|
69
|
+
this.createDom();
|
70
|
+
}, 5000);
|
71
|
+
}
|
72
|
+
|
73
|
+
removeEvent() {
|
74
|
+
window.removeEventListener("resize", this.createDom);
|
75
|
+
clearInterval(this.timer);
|
76
|
+
this.timer = null;
|
77
|
+
}
|
78
|
+
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
function getWatermarkImage(text) {
|
83
|
+
|
84
|
+
const canvas = document.createElement("canvas");
|
85
|
+
canvas.width = 300;
|
86
|
+
canvas.height = 200;
|
87
|
+
|
88
|
+
const watermarkText = canvas.getContext("2d");
|
89
|
+
watermarkText.rotate((-20 * Math.PI) / 180);
|
90
|
+
watermarkText.font = "15px Arial";
|
91
|
+
const gradient = watermarkText.createLinearGradient(0, 0, 150, 0);
|
92
|
+
gradient.addColorStop(0, "magenta");
|
93
|
+
gradient.addColorStop(1.0, "blue");
|
94
|
+
watermarkText.fillStyle = gradient;
|
95
|
+
watermarkText.textAlign = "center";
|
96
|
+
watermarkText.textBaseline = "middle";
|
97
|
+
watermarkText.fillText(text, canvas.width * 0.3, canvas.height * 0.7);
|
98
|
+
|
99
|
+
return canvas.toDataURL("image/png", 1);
|
100
|
+
}
|
101
|
+
|
102
|
+
|
103
|
+
export {
|
104
|
+
Watermark,
|
105
|
+
};
|
package/index.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
export * from "./Download.js";
|
2
|
-
export * from "./FixedElement.js";
|
3
|
-
export * from "./HtmlToText.js";
|
4
|
-
export * from "./ScxDrag.js";
|
5
|
-
export * from "./Watermark.js";
|
1
|
+
export * from "./Download.js";
|
2
|
+
export * from "./FixedElement.js";
|
3
|
+
export * from "./HtmlToText.js";
|
4
|
+
export * from "./ScxDrag.js";
|
5
|
+
export * from "./Watermark.js";
|
package/package.json
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
{
|
2
|
-
"name": "@scx-js/scx-dom",
|
3
|
-
"version": "0.0.
|
4
|
-
"description": "SCX DOM",
|
5
|
-
"license": "MIT",
|
6
|
-
"author": "scx567888",
|
7
|
-
"main": "index.js",
|
8
|
-
"type": "module",
|
9
|
-
"repository": {
|
10
|
-
"type": "git",
|
11
|
-
"url": "https://github.com/scx567888/scx-js.git"
|
12
|
-
}
|
13
|
-
}
|
1
|
+
{
|
2
|
+
"name": "@scx-js/scx-dom",
|
3
|
+
"version": "0.0.2",
|
4
|
+
"description": "SCX DOM",
|
5
|
+
"license": "MIT",
|
6
|
+
"author": "scx567888",
|
7
|
+
"main": "index.js",
|
8
|
+
"type": "module",
|
9
|
+
"repository": {
|
10
|
+
"type": "git",
|
11
|
+
"url": "https://github.com/scx567888/scx-js.git"
|
12
|
+
}
|
13
|
+
}
|