jellies-draw 0.1.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.
@@ -0,0 +1,74 @@
1
+ import Properties from '../properties'
2
+ import Canvas from '../canvas'
3
+ import Konva from 'konva'
4
+ import Histories from '../histories'
5
+ export default {
6
+ add(file) {
7
+ Properties.tool = 'selector'
8
+ const reader = new FileReader()
9
+ reader.onload = () => {
10
+ this.generate({
11
+ x: 10,
12
+ y: 10,
13
+ imageUrl: reader.result,
14
+ needToRecord: true
15
+ })
16
+ }
17
+ reader.readAsDataURL(file)
18
+ },
19
+ bindEvents(newImage) {
20
+ newImage.off('transformend')
21
+ newImage.off('dragend')
22
+ newImage.on('transformend', Histories.record)
23
+ newImage.on('dragend', Histories.record)
24
+ },
25
+ generate({ x, y, scaleX, scaleY, skewX, rotation, imageUrl, needToRecord = false }) {
26
+ const image = new Image()
27
+ image.onload = () => {
28
+ const { width, height } = this.getImageSize(scaleX, scaleY, image)
29
+ const newImage = new Konva.Image({
30
+ x,
31
+ y,
32
+ width,
33
+ height,
34
+ scaleX,
35
+ scaleY,
36
+ skewX,
37
+ rotation,
38
+ image,
39
+ imageUrl,
40
+ name: 'node',
41
+ nodeType: 'image',
42
+ draggable: true
43
+ })
44
+ this.bindEvents(newImage)
45
+ Canvas.layer.add(newImage)
46
+ if (needToRecord) {
47
+ Histories.record()
48
+ }
49
+ }
50
+ image.src = imageUrl
51
+ },
52
+ getImageSize(scaleX, scaleY, image) {
53
+ if (!scaleX && !scaleY) {
54
+ return {}
55
+ }
56
+ if (image.width > Canvas.stage.width() || image.height > Canvas.stage.height()) {
57
+ const ratio = image.width / image.height
58
+ if (image.width > image.height) {
59
+ return {
60
+ width: Canvas.stage.width(),
61
+ height: Canvas.stage.width() / ratio
62
+ }
63
+ }
64
+ return {
65
+ width: Canvas.stage.height() * ratio,
66
+ height: Canvas.stage.height()
67
+ }
68
+ }
69
+ return {
70
+ width: image.width,
71
+ height: image.height
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,23 @@
1
+ import Rectangle from './rectangle';
2
+ import Ellipse from './ellipse';
3
+ import Image from './image';
4
+ import Line from './line';
5
+ import Arrow from './arrow';
6
+ import Pen from './pen';
7
+ import Eraser from './eraser';
8
+ import Text from './text';
9
+ import Selector from './selector';
10
+ import Clear from './clear';
11
+
12
+ export default {
13
+ rectangle: Rectangle,
14
+ ellipse: Ellipse,
15
+ image: Image,
16
+ line: Line,
17
+ arrow: Arrow,
18
+ pen: Pen,
19
+ eraser: Eraser,
20
+ text: Text,
21
+ selector: Selector,
22
+ clear: Clear
23
+ }
@@ -0,0 +1,56 @@
1
+ import Konva from 'konva'
2
+ import Properties from '../properties'
3
+ import Canvas from '../canvas'
4
+ import Histories from '../histories'
5
+ export default {
6
+ temporalShape: null,
7
+ startX: 0,
8
+ startY: 0,
9
+ show({ offsetX, offsetY }) {
10
+ Canvas.isDrawing = true;
11
+ this.startX = offsetX
12
+ this.startY = offsetY
13
+ const stroke = Properties.strokeColor
14
+ const strokeWidth = Properties.strokeWidth
15
+ const newLine = this.generate({
16
+ points: [offsetX, offsetY, offsetX, offsetY],
17
+ stroke,
18
+ strokeWidth
19
+ })
20
+ this.temporalShape = newLine
21
+ Canvas.layer.add(this.temporalShape)
22
+ },
23
+ change({ offsetX, offsetY }) {
24
+ if (Canvas.isDrawing) {
25
+ this.temporalShape.points([this.startX, this.startY, offsetX, offsetY]);
26
+ }
27
+ },
28
+ finish() {
29
+ Properties.tool = 'selector'
30
+ this.temporalShape = null
31
+ },
32
+ generate({ x, y, scaleX, scaleY, points, stroke, strokeWidth }) {
33
+ const newLine = new Konva.Line({
34
+ x,
35
+ y,
36
+ scaleX,
37
+ scaleY,
38
+ points,
39
+ stroke,
40
+ strokeWidth,
41
+ name: 'node',
42
+ nodeType: 'line',
43
+ lineCap: 'round',
44
+ bezier: false,
45
+ strokeScaleEnabled: false
46
+ })
47
+ this.bindEvents(newLine)
48
+ return newLine
49
+ },
50
+ bindEvents(newLine) {
51
+ newLine.off('transformend')
52
+ newLine.off('dragend')
53
+ newLine.on('transformend', Histories.record)
54
+ newLine.on('dragend', Histories.record)
55
+ }
56
+ }
@@ -0,0 +1,61 @@
1
+ import Konva from 'konva'
2
+ import Properties from '../properties'
3
+ import Canvas from '../canvas'
4
+ import Histories from '../histories'
5
+ export default {
6
+ temporalCurveTrajectory: null,
7
+ temporalShape: null,
8
+ startX: 0,
9
+ startY: 0,
10
+ show({ offsetX, offsetY }) {
11
+ Canvas.isDrawing = true;
12
+ this.startX = offsetX
13
+ this.startY = offsetY
14
+ this.temporalCurveTrajectory = null
15
+ const stroke = Properties.strokeColor
16
+ const strokeWidth = Properties.strokeWidth
17
+ const newCurve = this.generate({
18
+ points: [offsetX, offsetY, offsetX, offsetY],
19
+ stroke,
20
+ strokeWidth
21
+ })
22
+ this.temporalShape = newCurve
23
+ Canvas.layer.add(this.temporalShape)
24
+ },
25
+ change({ offsetX, offsetY }) {
26
+ if (Canvas.isDrawing) {
27
+ if (!this.temporalCurveTrajectory) {
28
+ this.temporalCurveTrajectory = [this.startX, this.startY]
29
+ }
30
+ this.temporalCurveTrajectory.push(offsetX, offsetY)
31
+ this.temporalShape.points(this.temporalCurveTrajectory);
32
+ }
33
+ },
34
+ finish() {
35
+ this.temporalShape = null
36
+ },
37
+ generate({ x, y, scaleX, scaleY, points, stroke, strokeWidth }) {
38
+ const newLine = new Konva.Line({
39
+ x,
40
+ y,
41
+ scaleX,
42
+ scaleY,
43
+ points,
44
+ stroke,
45
+ strokeWidth,
46
+ name: 'node',
47
+ nodeType: 'pen',
48
+ lineCap: 'round',
49
+ bezier: false,
50
+ strokeScaleEnabled: false
51
+ })
52
+ this.bindEvents(newLine)
53
+ return newLine
54
+ },
55
+ bindEvents(newLine) {
56
+ newLine.off('transformend')
57
+ newLine.off('dragend')
58
+ newLine.on('transformend', Histories.record)
59
+ newLine.on('dragend', Histories.record)
60
+ }
61
+ }
@@ -0,0 +1,71 @@
1
+ import Konva from 'konva'
2
+ import Properties from '../properties'
3
+ import Canvas from '../canvas'
4
+ import Histories from '../histories'
5
+ export default {
6
+ temporalShape: null,
7
+ startX: 0,
8
+ startY: 0,
9
+ show({ offsetX, offsetY }) {
10
+ Canvas.isDrawing = true;
11
+ this.startX = offsetX
12
+ this.startY = offsetY
13
+ const fill = Properties.fillColor
14
+ const stroke = Properties.strokeColor
15
+ const strokeWidth = Properties.strokeWidth
16
+ const newRectangle = this.generate({
17
+ x: offsetX,
18
+ y: offsetY,
19
+ width: 1,
20
+ height: 1,
21
+ fill,
22
+ stroke,
23
+ strokeWidth
24
+ })
25
+ this._checkRectangleAndChangeToCell(newRectangle)
26
+ this.temporalShape = newRectangle
27
+ Canvas.layer.add(this.temporalShape)
28
+ },
29
+ change({ offsetX, offsetY }) {
30
+ if (Canvas.isDrawing) {
31
+ this.temporalShape.width(offsetX - this.startX)
32
+ this.temporalShape.height(offsetY - this.startY)
33
+ }
34
+ },
35
+ finish() {
36
+ Properties.tool = 'selector'
37
+ this.temporalShape = null
38
+ },
39
+ _checkRectangleAndChangeToCell(newRectangle) {
40
+ setTimeout(() => {
41
+ if (!this.temporalShape && newRectangle.width() < 5 && newRectangle.height() < 5) {
42
+ newRectangle.width(30)
43
+ newRectangle.height(30)
44
+ }
45
+ }, 200)
46
+ },
47
+ generate({ x, y, scaleX, scaleY, width, height, fill, stroke, strokeWidth }) {
48
+ const newRectangle = new Konva.Rect({
49
+ x,
50
+ y,
51
+ scaleX,
52
+ scaleY,
53
+ width,
54
+ height,
55
+ fill,
56
+ stroke,
57
+ strokeWidth,
58
+ name: 'node',
59
+ nodeType: 'rectangle',
60
+ strokeScaleEnabled: false
61
+ })
62
+ this.bindEvents(newRectangle)
63
+ return newRectangle
64
+ },
65
+ bindEvents(newRectangle) {
66
+ newRectangle.off('transformend')
67
+ newRectangle.off('dragend')
68
+ newRectangle.on('transformend', Histories.record)
69
+ newRectangle.on('dragend', Histories.record)
70
+ }
71
+ }
@@ -0,0 +1,47 @@
1
+ import Konva from 'konva'
2
+ import Canvas from '../canvas'
3
+ import Transformer from '../transformer'
4
+ export default {
5
+ startX: 0,
6
+ startY: 0,
7
+ selector: null,
8
+ show({ offsetX, offsetY }) {
9
+ this.selector = new Konva.Rect({
10
+ fill: 'rgba(176,188,207,0.3)',
11
+ visible: true,
12
+ x: offsetX,
13
+ y: offsetY,
14
+ width: 0,
15
+ height: 0
16
+ });
17
+ Canvas.layer.add(this.selector);
18
+ this.startX = offsetX;
19
+ this.startY = offsetY;
20
+ },
21
+ change({ offsetX, offsetY }) {
22
+ if (this.selector) {
23
+ this.selector.setAttrs({
24
+ x: Math.min(this.startX, offsetX),
25
+ y: Math.min(this.startY, offsetY),
26
+ width: Math.abs(this.startX - offsetX),
27
+ height: Math.abs(this.startY - offsetY)
28
+ });
29
+ }
30
+ },
31
+ finish() {
32
+ if (this.selector) {
33
+ Transformer.selectNodes(Canvas.stage.find('.node').filter(node => this._isInSelector(node)));
34
+ this.selector.destroy();
35
+ this.selector = null
36
+ }
37
+ },
38
+ isSelecting() {
39
+ return this.selector !== null
40
+ },
41
+ _isInSelector(node) {
42
+ return Konva.Util.haveIntersection(
43
+ this.selector.getClientRect(),
44
+ node.getClientRect()
45
+ );
46
+ }
47
+ }
@@ -0,0 +1,178 @@
1
+ import Konva from 'konva'
2
+ import Properties from '../properties'
3
+ import Canvas from '../canvas'
4
+ import Transformer from '../transformer'
5
+ import Histories from '../histories'
6
+ export default {
7
+ temporalText: null,
8
+ textarea: null,
9
+ textareaEditingHandler: null,
10
+ disableTextareaHandler: null,
11
+ handleTextareaTimer: null,
12
+ show({ offsetX, offsetY }) {
13
+ if (!Canvas.isEditingText) {
14
+ const newText = this.generate({
15
+ text: '',
16
+ x: offsetX,
17
+ y: offsetY,
18
+ fontSize: Properties.fontSize,
19
+ width: 200,
20
+ fill: Properties.strokeColor
21
+ })
22
+ this.temporalText = newText
23
+ Canvas.layer.add(this.temporalText)
24
+ clearTimeout(this.handleTextareaTimer)
25
+ this.handleTextareaTimer = setTimeout(() => {
26
+ this._handleEnableTextarea(newText)
27
+ }, 200)
28
+ }
29
+ },
30
+ bindEvents(newText) {
31
+ newText.off('dblclick dbltap')
32
+ newText.off('transform')
33
+ newText.off('transformend')
34
+ newText.off('dragend')
35
+ newText.on('dblclick dbltap', () => {
36
+ this._handleEnableTextarea(newText)
37
+ })
38
+ newText.on('transform', () => {
39
+ newText.setAttrs({
40
+ width: newText.width() * newText.scaleX(),
41
+ height: newText.height() * newText.scaleY(),
42
+ scaleX: 1,
43
+ scaleY: 1
44
+ })
45
+ })
46
+ newText.on('transformend', Histories.record)
47
+ newText.on('dragend', Histories.record)
48
+ },
49
+ change() {
50
+ return
51
+ },
52
+ finish() {
53
+ return
54
+ },
55
+ _handleEnableTextarea(newText) {
56
+ Canvas.isEditingText = true;
57
+ Properties.tool = 'text'
58
+ this.temporalText = newText;
59
+ newText.hide();
60
+ Transformer.deselectAllNodes();
61
+
62
+ const textPosition = newText.absolutePosition();
63
+ const areaPosition = {
64
+ x: Canvas.stage.container().offsetLeft + textPosition.x,
65
+ y: Canvas.stage.container().offsetTop + textPosition.y
66
+ };
67
+
68
+ if (this.textarea) {
69
+ this.textarea.removeEventListener('keydown', this.textareaEditingHandler);
70
+ }
71
+
72
+ this.textarea = document.createElement('textarea');
73
+ document.body.appendChild(this.textarea);
74
+
75
+ this.textarea.value = newText.text();
76
+ this.textarea.style.position = 'absolute';
77
+ this.textarea.style.top = `${areaPosition.y}px`;
78
+ this.textarea.style.left = `${areaPosition.x}px`;
79
+ this.textarea.style.width = `${newText.width() - newText.padding() * 2}px`;
80
+ this.textarea.style.height =
81
+ `${ newText.height() - newText.padding() * 2 + 5 }px`;
82
+ this.textarea.style.fontSize = `${ newText.fontSize() }px`;
83
+ this.textarea.style.border = 'none';
84
+ this.textarea.style.padding = '0';
85
+ this.textarea.style.margin = '0';
86
+ this.textarea.style.overflow = 'hidden';
87
+ this.textarea.style.background = 'none';
88
+ this.textarea.style.outline = 'none';
89
+ this.textarea.style.resize = 'none';
90
+ this.textarea.style.lineHeight = newText.lineHeight();
91
+ this.textarea.style.fontFamily = newText.fontFamily();
92
+ this.textarea.style.transformOrigin = 'left top';
93
+ this.textarea.style.textAlign = newText.align();
94
+ this.textarea.style.color = newText.fill();
95
+ const rotation = newText.rotation();
96
+ let transform = '';
97
+ if (rotation) {
98
+ transform += `rotateZ(${ rotation }deg)`;
99
+ }
100
+
101
+ let px = 0;
102
+
103
+ const isFirefox =
104
+ navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
105
+ if (isFirefox) {
106
+ px += 2 + Math.round(newText.fontSize() / 20);
107
+ }
108
+ transform += `translateY(-${ px }px)`;
109
+
110
+ this.textarea.style.transform = transform;
111
+
112
+ this.textarea.style.height = 'auto';
113
+ this.textarea.style.height = `${this.textarea.scrollHeight + 3}px`;
114
+
115
+ this.textarea.focus();
116
+
117
+ this.textareaEditingHandler = this._handleTextareaEditing.bind(this);
118
+ this.disableTextareaHandler = this._handleDisableTextarea.bind(this);
119
+ this.textarea.addEventListener('keydown', this.textareaEditingHandler);
120
+ setTimeout(() => {
121
+ window.addEventListener('mousedown', this.disableTextareaHandler);
122
+ });
123
+ },
124
+ _handleDisableTextarea(event) {
125
+ if (event.target !== this.textarea) {
126
+ this.temporalText.text(this.textarea.value)
127
+ this.textarea.remove()
128
+ window.removeEventListener('mousedown', this.disableTextareaHandler)
129
+ this.temporalText.show()
130
+ Properties.tool = 'selector'
131
+ }
132
+ },
133
+ _handleTextareaEditing() {
134
+ const scale = this.temporalText.getAbsoluteScale().x;
135
+ this._setTextareaWidth(this.temporalText.width() * scale);
136
+ this.textarea.style.height = 'auto';
137
+ this.textarea.style.height =
138
+ `${this.textarea.scrollHeight + this.temporalText.fontSize() }px`;
139
+ },
140
+ _setTextareaWidth(newWidth) {
141
+ if (!newWidth) {
142
+ newWidth = this.textNode.placeholder.length * this.temporalText.fontSize();
143
+ }
144
+ const isSafari = /^((?!chrome|android).)*safari/i.test(
145
+ navigator.userAgent
146
+ );
147
+ const isFirefox =
148
+ navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
149
+ if (isSafari || isFirefox) {
150
+ newWidth = Math.ceil(newWidth);
151
+ }
152
+
153
+ const isEdge =
154
+ document.documentMode || /Edge/.test(navigator.userAgent);
155
+ if (isEdge) {
156
+ newWidth += 1;
157
+ }
158
+ this.textarea.style.width = `${ newWidth }px`;
159
+ },
160
+ generate({ text, x, y, fill, fontSize, width, height, scaleX, scaleY }) {
161
+ const newText = new Konva.Text({
162
+ text,
163
+ x,
164
+ y,
165
+ fill,
166
+ fontSize,
167
+ width,
168
+ height,
169
+ scaleX,
170
+ scaleY,
171
+ name: 'node',
172
+ nodeType: 'text',
173
+ strokeEnabled: false
174
+ });
175
+ this.bindEvents(newText)
176
+ return newText
177
+ }
178
+ }