picture_edit 2.1.0 → 2.1.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.
@@ -1,539 +0,0 @@
1
- import { G } from './global'
2
- import * as U from './utils'
3
- import svgRotate from '../assets/rotate.svg'
4
- import svgPainting from '../assets/painting.svg'
5
- import svgPaintingCancel from '../assets/painting_cancel.svg'
6
-
7
- let _initDistance = 0
8
- let _tempScale = 1
9
-
10
-
11
- let _operateBorderDom = null
12
- let _operateInfo = null
13
-
14
- let _lineElement = null
15
-
16
- let _lineInfo = {
17
- coefficient: 1,
18
- left: 0,
19
- top: 0,
20
- }
21
-
22
- let _textPosition = {
23
- id: undefined,
24
- x: 0,
25
- y: 0,
26
- scale: 1,
27
- left: 0,
28
- top: 0,
29
- }
30
-
31
- let _rotateInfo = {
32
- x: 0,
33
- y: 0,
34
- centerX: 0,
35
- centerY: 0,
36
- initialRotation: 0,
37
- startAngle: 0,
38
- }
39
-
40
- let _scaleInfo = {
41
- x: 0,
42
- y: 0,
43
- initFontSize: 14,
44
- centerX: 0,
45
- centerY: 0,
46
- initialScale: 1,
47
- initLength: 0,
48
- }
49
-
50
- function initListener() {
51
- initOperater()
52
- G.canvas.addEventListener('touchstart', (e) => {
53
- if (e.touches.length === 1) {
54
- if (G.operateType === 1) {
55
- U.simpleEvent(e)
56
- const touch = e.touches[0]
57
- G.canvasContext.beginPath()
58
- G.canvasContext.lineWidth = G.initOptions.fontLineWidth * G.device.dpr
59
- G.canvasContext.lineCap = 'round' // 线条末端添加圆形线帽,减少线条的生硬感
60
- G.canvasContext.lineJoin = 'round' // 线条交汇时为原型边角
61
- G.canvasContext.shadowBlur = G.initOptions.fontShadowBlur // 利用阴影,消除锯齿
62
- G.canvasContext.shadowColor = '#000000'
63
- const _bounding = G.canvas.getBoundingClientRect()
64
- const x = touch.clientX - _bounding.left
65
- const y = touch.clientY - _bounding.top
66
- const coefficient = G.canvas.width / _bounding.width
67
- _lineInfo = {
68
- coefficient,
69
- left: _bounding.left,
70
- top: _bounding.top,
71
- }
72
- const finalX = x * coefficient
73
- const finalY = y * coefficient
74
- G.canvasContext.moveTo(finalX, finalY)
75
- G.steps.push({ type: 'line', start: { x: finalX, y: finalY }, moves: [] })
76
- }
77
- } else if (e.touches.length === 2) {
78
- U.simpleEvent(e)
79
- resetLineImg()
80
- U.lockBox() // 防止缩放速度过快,屏幕出现之前的滚动条
81
- Object.keys(G.textsInfo).forEach(key => {
82
- const info = G.textsInfo[key]
83
- info.dom.style.cssText = `${info.dom.style.cssText}; display: none;`
84
- })
85
- G.touchType = 'scaleCanvas'
86
- G.preScale = G.currentScale
87
- _tempScale = 1
88
- _initDistance = U.getDistance(e.touches)
89
- const touch1 = e.touches[0]
90
- const touch2 = e.touches[1]
91
-
92
- // 计算两个触摸点的中心点坐标(相对于视口)
93
- const centerX = (touch1.clientX + touch2.clientX) / 2
94
- const centerY = (touch1.clientY + touch2.clientY) / 2
95
- const rect = G.canvas.getBoundingClientRect()
96
- const rectBox = G.canvasBox.getBoundingClientRect()
97
- G.screenPosition = {
98
- x: centerX,
99
- y: centerY,
100
- }
101
- G.outerPosition = {
102
- x: centerX - rectBox.left,
103
- y: centerY - rectBox.top,
104
- }
105
- G.canvas.style.transformOrigin = `${centerX - rect.left}px ${centerY - rect.top}px`
106
- }
107
- });
108
-
109
- function moveEvent(e) {
110
- const currentDistance = U.getDistance(e.touches)
111
- const scaleChange = currentDistance / _initDistance // 计算缩放变化
112
- _tempScale = scaleChange
113
- G.canvas.style.transform = `scale(${scaleChange})`
114
- }
115
-
116
- const throttledScroll = U.throttle((e) => {
117
- moveEvent(e)
118
- }, 16); // 60hz刷新率为准,16.67毫秒刷新一次
119
-
120
- G.canvas.addEventListener('touchmove', (e) => {
121
- if (e.touches.length === 1) {
122
- if (G.operateType === 1) {
123
- U.simpleEvent(e)
124
- const touch = e.touches[0]
125
- const x = touch.clientX - _lineInfo.left
126
- const y = touch.clientY - _lineInfo.top
127
- const finalX = x * _lineInfo.coefficient
128
- const finalY = y * _lineInfo.coefficient
129
- G.canvasContext.lineTo(finalX, finalY)
130
- G.canvasContext.stroke()
131
- let historyLength = G.steps.length - 1
132
- G.steps[historyLength].moves.push({ x: finalX, y: finalY })
133
- }
134
- } else if (e.touches.length === 2 && G.operateType !== 1 && G.touchType === 'scaleCanvas') {
135
- U.simpleEvent(e)
136
- throttledScroll(e)
137
- }
138
- });
139
-
140
- G.canvas.addEventListener('touchend', (e) => {
141
- if (e.touches.length === 0 && G.touchType === 'scaleCanvas') {
142
- _initDistance = 0
143
- G.touchType = ''
144
- const boxRect = G.canvasBox.getBoundingClientRect()
145
- const _preWidth = boxRect.width
146
- const _preHeight = boxRect.height
147
- const _lastWidth = _preWidth * _tempScale
148
- const _lastHeight = _preHeight * _tempScale
149
-
150
- const rect = G.canvas.getBoundingClientRect()
151
- // 此处需要先设置canvas的宽高,再设置canvasBox的宽高,不然缩小的时候,滚动条会变大
152
- G.canvas.style.cssText = `width: ${rect.width}px; height: ${rect.height}; transform: scale(1); transform-origin: center`
153
- G.canvasBox.style.cssText = `width: ${_lastWidth}px; height: ${_lastHeight}px`
154
- G.pedEdit.scrollTo(_lastWidth / _preWidth * G.outerPosition.x - G.screenPosition.x, _lastHeight / _preHeight * G.outerPosition.y - G.screenPosition.y)
155
- G.currentScale = _lastWidth / G.editSize.width
156
-
157
- const _scale = G.currentScale / G.preScale
158
- Object.keys(G.textsInfo).forEach(key => {
159
- const info = G.textsInfo[key]
160
- info.x = info.x * _scale
161
- info.y = info.y * _scale
162
- info.fontSize = info.fontSize * _scale
163
- info.width = info.width * _scale
164
- info.height = info.height * _scale
165
- info.dom.style.cssText = `${info.dom.style.cssText};
166
- font-size: ${info.fontSize}px;
167
- line-height: ${info.fontSize * G.initOptions.fontLineHeight}px;
168
- padding: ${G.initOptions.fontPaddingLeft * G.currentScale}px ${G.initOptions.fontPaddingTop * G.currentScale}px;
169
- border-radius: ${G.initOptions.fontRaduis * G.currentScale}px;
170
- transform: translate(${info.x}px, ${info.y}px) rotate(${info.rotate}deg);
171
- display: block;`
172
- })
173
- }
174
- U.unLockBox()
175
- });
176
- }
177
-
178
- function handleText() {
179
- if (G.textInput.innerText.length) {
180
- let textArray = G.textInput.innerText.split(/[\n\r]/)
181
- const fontSize = G.initOptions.fontSize * G.currentScale
182
- let textDom = `<div style="position: absolute;
183
- left: 0;
184
- top: 0;
185
- user-select: none;
186
- color: ${G.initOptions.fontColor};
187
- font-size: ${fontSize}px;
188
- line-height: ${fontSize * G.initOptions.fontLineHeight}px;
189
- background-color: ${G.initOptions.fontBgColor};
190
- padding: ${G.initOptions.fontPaddingLeft * G.currentScale}px ${G.initOptions.fontPaddingTop * G.currentScale}px;
191
- border-radius: ${G.initOptions.fontRaduis * G.currentScale}px;
192
- white-space: nowrap;
193
- z-index: ${G.textId}">`
194
- for (let i = 0; i < textArray.length; i++) {
195
- textDom = textDom + textArray[i] + `${i == textArray.length - 1 ? '' : '</br>'}`
196
- }
197
- textDom += `</div>`
198
- let dom = U.stringToNode(textDom)
199
- G.canvasBox.appendChild(dom)
200
- // 获取宽高以居中显示
201
- const boxRect = G.canvasBox.getBoundingClientRect()
202
- const domRect = dom.getBoundingClientRect()
203
- const top = G.editSize.height / 2 - boxRect.top - domRect.height / 2
204
- const left = G.editSize.width / 2 - boxRect.left - domRect.width / 2
205
- dom.style.cssText = `${dom.style.cssText}; transform: translate(${left}px, ${top}px) rotate(0deg);`
206
- const id = G.textId++
207
- dom.dataset.pedTextId = id
208
- G.textsInfo[id] = {
209
- textArray,
210
- x: left,
211
- y: top,
212
- rotate: 0,
213
- fontSize,
214
- fontColor: G.initOptions.fontColor,
215
- fontBgColor: G.initOptions.fontBgColor,
216
- width: domRect.width,
217
- height: domRect.height,
218
- dom,
219
- }
220
- G.steps.push({
221
- type: 'creat_text',
222
- textId: id
223
- });
224
- dom.addEventListener("touchstart", addTextBox)
225
- dom.addEventListener("touchmove", moveTextBox)
226
- dom.addEventListener("touchend", endTextBox)
227
- dom.addEventListener("click", handleTextClick)
228
- G.textInput.innerText = ""
229
- }
230
- G.textInput.removeEventListener("blur", handleText)
231
- G.textContent.style.display = "none"
232
- }
233
-
234
- function handleTextClick(e) {
235
- U.simpleEvent(e)
236
- handleCreateOperate(e.currentTarget)
237
- }
238
-
239
- function handleCreateOperate(target) {
240
- handleRemoveOperate()
241
- const id = target.dataset.pedTextId
242
- _operateInfo = G.textsInfo[id]
243
- const element = `<div style="position: absolute;
244
- width: ${_operateInfo.width + G.initOptions.operatePaddingLeft * 2}px;
245
- height: ${_operateInfo.height + G.initOptions.operatePaddingTop * 2}px;
246
- left: ${_operateInfo.x - G.initOptions.operatePaddingLeft}px;
247
- top: ${_operateInfo.y - G.initOptions.operatePaddingTop}px;
248
- border: 2px solid ${G.initOptions.textOperateColor};
249
- box-sizing: border-box;
250
- border-radius: 2px;
251
- background: transparent;
252
- transform: rotate(${_operateInfo.rotate}deg);">
253
- </div>`
254
- _operateBorderDom = U.stringToNode(element)
255
- const rotateElement = `<img src="${svgRotate}" class="operate-rotate" />`
256
- const _rotateDom = U.stringToNode(rotateElement)
257
- _rotateDom.addEventListener("touchstart", addTextRotate)
258
- _rotateDom.addEventListener("touchmove", moveTextRotate)
259
- _rotateDom.addEventListener("touchend", endTextRotate)
260
-
261
- const scaleElement = `<div class="operate-scale"></div>`
262
- const _scaleDom = U.stringToNode(scaleElement)
263
- _scaleDom.addEventListener("touchstart", addTextScale)
264
- _scaleDom.addEventListener("touchmove", moveTextScale)
265
- _scaleDom.addEventListener('touchend', endTextScale)
266
-
267
- _operateBorderDom.appendChild(_rotateDom)
268
- _operateBorderDom.appendChild(_scaleDom)
269
- G.canvasBox.appendChild(_operateBorderDom)
270
- }
271
-
272
- function handleRemoveOperate() {
273
- if (_operateBorderDom) _operateBorderDom.remove()
274
- }
275
-
276
- function addTextBox(e) {
277
- if (e.touches.length === 1) {
278
- const touche = e.touches[0]
279
- const dom = e.currentTarget
280
- const id = dom.dataset.pedTextId
281
- let { x, y } = G.textsInfo[id]
282
- _textPosition = {
283
- id,
284
- x: touche.clientX,
285
- y: touche.clientY,
286
- scale: G.currentScale,
287
- left: x,
288
- top: y,
289
- }
290
- }
291
- }
292
-
293
- function moveTextBox(e) {
294
- U.simpleEvent(e)
295
- if (e.touches.length === 1) {
296
- G.touchType = 'scaleCanvas'
297
- handleRemoveOperate()
298
- const target = e.currentTarget
299
- const id = target.dataset.pedTextId
300
- const lessX = e.touches[0].clientX - _textPosition.x + _textPosition.left
301
- const lessY = e.touches[0].clientY - _textPosition.y + _textPosition.top
302
- target.style.cssText = `${target.style.cssText}; transform: translate(${lessX}px, ${lessY}px) rotate(${G.textsInfo[id].rotate}deg);`
303
- G.textsInfo[id].x = lessX
304
- G.textsInfo[id].y = lessY
305
- }
306
- }
307
-
308
- function endTextBox() {
309
- if (G.touchType === 'scaleCanvas') {
310
- G.touchType = ''
311
- G.steps.push({
312
- type: 'move_text',
313
- textId: _textPosition.id,
314
- scale: _textPosition.scale,
315
- x: _textPosition.left,
316
- y: _textPosition.top,
317
- });
318
- }
319
- }
320
-
321
- function calculateAngle(x, y) {
322
- // 弧度转度数
323
- return Math.atan2(y, x) * (180 / Math.PI)
324
- }
325
-
326
- function addTextRotate(e) {
327
- U.simpleEvent(e)
328
- if (e.touches.length === 1) {
329
- const rect = _operateInfo.dom.getBoundingClientRect()
330
- const touche = e.touches[0]
331
- const x = touche.clientX
332
- const y = touche.clientY
333
- const centerX = rect.left + rect.width / 2
334
- const centerY = rect.top + rect.height / 2
335
- _rotateInfo = {
336
- x,
337
- y,
338
- centerX,
339
- centerY,
340
- initialRotation: _operateInfo.rotate,
341
- startAngle: calculateAngle(x - centerX, y - centerY)
342
- }
343
- }
344
- }
345
-
346
- function moveTextRotate(e) {
347
- U.simpleEvent(e)
348
- if (e.touches.length === 1) {
349
- _operateBorderDom.remove()
350
- G.touchType = 'addRotateLog'
351
- const touche = e.touches[0]
352
- const rotate = calculateAngle(touche.clientX - _rotateInfo.centerX, touche.clientY - _rotateInfo.centerY) + _rotateInfo.initialRotation - _rotateInfo.startAngle
353
- _operateInfo.dom.style.cssText = `${_operateInfo.dom.style.cssText}; transform: translate(${_operateInfo.x}px, ${_operateInfo.y}px) rotate(${rotate}deg)`;
354
- _operateInfo.rotate = rotate
355
- }
356
- }
357
-
358
- function endTextRotate(e) {
359
- U.simpleEvent(e)
360
- if (G.touchType === 'addRotateLog') {
361
- G.touchType = ''
362
- G.steps.push({
363
- type: 'rotate_text',
364
- textId: _operateInfo.dom.dataset.pedTextId,
365
- rotate: _rotateInfo.initialRotation,
366
- });
367
- }
368
- }
369
-
370
- function addTextScale(e) {
371
- U.simpleEvent(e)
372
- if (e.touches.length === 1) {
373
- const rect = _operateInfo.dom.getBoundingClientRect()
374
- const touche = e.touches[0]
375
- _scaleInfo = {
376
- x: touche.clientX,
377
- y: touche.clientY,
378
- centerX: rect.left + rect.width / 2,
379
- centerY: rect.top + rect.height / 2,
380
- initFontSize: _operateInfo.fontSize,
381
- initialScale: _operateInfo.fontSize / G.initOptions.fontSize,
382
- initLength: U.getDistance([touche, {
383
- clientX: rect.left + rect.width / 2,
384
- clientY: rect.top + rect.height / 2,
385
- }]),
386
- }
387
- }
388
- }
389
-
390
- function moveTextScale(e) {
391
- U.simpleEvent(e)
392
- if (e.touches.length === 1) {
393
- G.touchType = 'addScaleLog'
394
- handleRemoveOperate()
395
- const touche = e.touches[0]
396
- const _length = U.getDistance([touche, {
397
- clientX: _scaleInfo.centerX,
398
- clientY: _scaleInfo.centerY,
399
- }])
400
- const scale = _length / _scaleInfo.initLength * _scaleInfo.initialScale
401
- const fontSize = G.initOptions.fontSize * scale
402
- _operateInfo.dom.style.cssText = `${_operateInfo.dom.style.cssText};
403
- font-size: ${fontSize}px;
404
- line-height: ${fontSize * G.initOptions.fontLineHeight}px;
405
- border-radius: ${G.initOptions.fontRaduis * scale}px;
406
- padding: ${G.initOptions.fontPaddingLeft * scale}px ${G.initOptions.fontPaddingTop * scale}px;`
407
- }
408
- }
409
-
410
- function endTextScale(e) {
411
- U.simpleEvent(e)
412
- if (G.touchType = 'addScaleLog') {
413
- G.touchType = ''
414
- // 缩放文本,需要更新宽高等信息了
415
- _operateInfo.width = _operateInfo.dom.offsetWidth
416
- _operateInfo.height = _operateInfo.dom.offsetHeight
417
- _operateInfo.fontSize = parseFloat(_operateInfo.dom.style.fontSize)
418
-
419
- G.steps.push({
420
- type: 'scale_text',
421
- textId: _operateInfo.dom.dataset.pedTextId,
422
- scale: G.currentScale,
423
- fontSize: _scaleInfo.initFontSize,
424
- })
425
- }
426
- }
427
-
428
- function outPutCanvas() {
429
- const canvasRect = G.canvas.getBoundingClientRect()
430
- const canvasScale = G.canvas.width / canvasRect.width
431
- Object.keys(G.textsInfo).forEach((id) => {
432
- const textInfo = G.textsInfo[id]
433
- const textDom = textInfo.dom
434
- textDom.style.cssText = `${textDom.style.cssText}; transform: translate(${textInfo.x}px, ${textInfo.y}px) rotate(0deg)`
435
- const textReact = textDom.getBoundingClientRect()
436
- U.drawPanel(canvasScale, textInfo, textReact, canvasRect)
437
- })
438
- }
439
-
440
- function resetImg() {
441
- const context = G.canvasContext
442
- context.clearRect(0, 0, G.canvas.width, G.canvas.height)
443
- context.drawImage(G.imgInstance, 0, 0, G.img.width, G.img.height, 0, 0, G.canvas.width, G.canvas.height)
444
- }
445
-
446
- function resetLineImg() {
447
- G.operateType = 0
448
- U.unLockBox()
449
- if (_lineElement) _lineElement.src = svgPaintingCancel
450
- }
451
-
452
- function initOperater() {
453
- G.ped.addEventListener('click', (e) => {
454
- const operateType = e.target.getAttribute('operate')
455
- if (operateType) {
456
- if (operateType === 'cancel') {
457
- U.removePed()
458
- } else if (operateType === 'save') {
459
- outPutCanvas()
460
- const dataUrl = G.canvas.toDataURL(G.initOptions.outType, G.initOptions.encoderOptions)
461
- G.initOptions.getDataURL(dataUrl)
462
- U.removePed()
463
- } else if (operateType === 'line') {
464
- if (G.operateType === 1) {
465
- G.operateType = 0
466
- U.unLockBox()
467
- } else {
468
- G.operateType = 1
469
- U.lockBox()
470
- }
471
- _lineElement = e.target.tagName === 'DIV' ? e.target.firstElementChild : e.target
472
- _lineElement.src = G.operateType === 0 ? svgPaintingCancel : svgPainting
473
- } else if (operateType === 'text') {
474
- resetLineImg()
475
- G.textContent.style.display = "block"
476
- G.textInput.focus()
477
- G.textInput.addEventListener("blur", handleText)
478
- } else if (operateType === 'back') {
479
- resetLineImg()
480
- if (G.steps.length) {
481
- const lastInfo = G.steps.pop()
482
- if (lastInfo.type === 'line') {
483
- resetImg()
484
- const context = G.canvasContext
485
- context.lineWidth = G.initOptions.fontLineWidth * G.device.dpr
486
- context.lineCap = 'round'
487
- context.lineJoin = 'round'
488
- context.shadowBlur = G.initOptions.fontShadowBlur
489
- context.shadowColor = '#000000'
490
- G.steps.forEach((item) => {
491
- if (item.type === 'line') {
492
- context.beginPath()
493
- context.moveTo(item.start.x, item.start.y)
494
- item.moves.forEach(move => {
495
- context.lineTo(move.x, move.y)
496
- context.stroke()
497
- })
498
- }
499
- })
500
- } else if (lastInfo.type === 'creat_text') {
501
- const textId = lastInfo.textId
502
- G.textsInfo[textId].dom.remove()
503
- delete G.textsInfo[textId]
504
- } else if (lastInfo.type === 'move_text') {
505
- const textId = lastInfo.textId
506
- const finalX = lastInfo.x * G.currentScale / lastInfo.scale
507
- const finalY = lastInfo.y * G.currentScale / lastInfo.scale
508
- G.textsInfo[textId].x = finalX
509
- G.textsInfo[textId].y = finalY
510
- G.textsInfo[textId].dom.style.cssText = `${G.textsInfo[textId].dom.style.cssText}; transform: translate(${finalX}px, ${finalY}px) rotate(${G.textsInfo[textId].rotate}deg);`
511
- } else if (lastInfo.type === 'scale_text') {
512
- const textId = lastInfo.textId
513
- const textInfo = G.textsInfo[textId]
514
- const finalFontSize = lastInfo.fontSize * G.currentScale / lastInfo.scale
515
- textInfo.fontSize = finalFontSize
516
- textInfo.dom.style.cssText = `${textInfo.dom.style.cssText};
517
- font-size: ${finalFontSize}px;
518
- line-height: ${finalFontSize * G.initOptions.fontLineHeight}px;
519
- border-radius: ${G.initOptions.fontRaduis * (finalFontSize / G.initOptions.fontSize)}px;
520
- padding: ${G.initOptions.fontPaddingLeft * (finalFontSize / G.initOptions.fontSize)}px ${G.initOptions.fontPaddingTop * (finalFontSize / G.initOptions.fontSize)}px;`
521
- const rect = textInfo.dom.getBoundingClientRect()
522
- textInfo.width = rect.width
523
- textInfo.height = rect.height
524
- } else if (lastInfo.type === 'rotate_text') {
525
- const textId = lastInfo.textId
526
- const textInfo = G.textsInfo[textId]
527
- textInfo.rotate = lastInfo.rotate
528
- textInfo.dom.style.cssText = `${textInfo.dom.style.cssText};
529
- transform: translate(${textInfo.x}px, ${textInfo.y}px) rotate(${lastInfo.rotate}deg);`
530
- }
531
- }
532
- } else if (operateType === 'clear') {
533
- U.removePed()
534
- }
535
- }
536
- })
537
- }
538
-
539
- export { initListener }