Sfoglia il codice sorgente

Move (#97)

* Add mover skeleton

Atm it is just the eraser with new icons and renamed variables

* The mover not detect the object to move

* We can move ellipses and rectangles

* The mover now moves all types of objects

* Add the move everything functionality

* Aesthetic changes

* Replace the use of const and let with classic var

* Undo few commits

* Start the transform-translate implementation

* Mover now moves with transform translate

* Put the function to get the translation matrix in the Tools

* Shapes (ellipse, line, pencil, rect, and text) now properly load translate coords

* Add the transform-translate to the createSVG functions

* Done movement using transform-translate

* Fix parenthesization bug in the SVG create

* Fix comment about mover states

* Undo translation loading from Ellipse, Mover, Pencil, and Text tools

* Makes the board send update message to the mover as needed

* Remove the old code from the line tool too

* Simplify the mover tool

See #86

* update the mover icon

* Merge the mover and the hand tools

* Improve preview for pre-rendered elements

* v1.6.0

Co-authored-by: Paolo Bolzoni <paolo.bolzoni1@studenti.unipr.it>
dev_h
Ophir LOJKINE 5 anni fa
parent
commit
50da719bda
Nessun account collegato all'indirizzo email del committer

+ 1
- 1
client-data/board.html Vedi File

101
 	<script src="../js/canvascolor.js"></script>
101
 	<script src="../js/canvascolor.js"></script>
102
 </body>
102
 </body>
103
 
103
 
104
-</html>
104
+</html>

+ 7
- 0
client-data/js/board.js Vedi File

314
 function messageForTool(message) {
314
 function messageForTool(message) {
315
 	var name = message.tool,
315
 	var name = message.tool,
316
 		tool = Tools.list[name];
316
 		tool = Tools.list[name];
317
+
317
 	if (tool) {
318
 	if (tool) {
318
 		Tools.applyHooks(Tools.messageHooks, message);
319
 		Tools.applyHooks(Tools.messageHooks, message);
319
 		tool.draw(message, false);
320
 		tool.draw(message, false);
323
 		if (!Tools.pendingMessages[name]) Tools.pendingMessages[name] = [message];
324
 		if (!Tools.pendingMessages[name]) Tools.pendingMessages[name] = [message];
324
 		else Tools.pendingMessages[name].push(message);
325
 		else Tools.pendingMessages[name].push(message);
325
 	}
326
 	}
327
+
328
+	if (message.tool !== 'Hand' && message.deltax != null && message.deltay != null) {
329
+		//this message has special info for the mover
330
+		messageForTool({ tool: 'Hand', type: 'update', deltax: message.deltax || 0, deltay: message.deltay || 0, id: message.id });
331
+	}
326
 }
332
 }
327
 
333
 
328
 // Apply the function to all arguments by batches
334
 // Apply the function to all arguments by batches
612
 	};
618
 	};
613
 })();
619
 })();
614
 
620
 
621
+
615
 //Scale the canvas on load
622
 //Scale the canvas on load
616
 Tools.svg.width.baseVal.value = document.body.clientWidth;
623
 Tools.svg.width.baseVal.value = document.body.clientWidth;
617
 Tools.svg.height.baseVal.value = document.body.clientHeight;
624
 Tools.svg.height.baseVal.value = document.body.clientHeight;

+ 102
- 21
client-data/tools/hand/hand.js Vedi File

1
 /**
1
 /**
2
- *                        WHITEBOPHIR
2
+ *						  WHITEBOPHIR
3
  *********************************************************
3
  *********************************************************
4
  * @licstart  The following is the entire license notice for the 
4
  * @licstart  The following is the entire license notice for the 
5
- *  JavaScript code in this page.
5
+ *	JavaScript code in this page.
6
  *
6
  *
7
  * Copyright (C) 2013  Ophir LOJKINE
7
  * Copyright (C) 2013  Ophir LOJKINE
8
  *
8
  *
24
  * @licend
24
  * @licend
25
  */
25
  */
26
 
26
 
27
-(function () { //Code isolation
27
+(function hand() { //Code isolation
28
+	var selected = null;
29
+	var last_sent = 0;
28
 
30
 
29
-	var orig = { x: 0, y: 0 };
30
-	var pressed = false;
31
-	function press(x, y, evt, isTouchEvent) {
31
+
32
+	function startMovingElement(x, y, evt) {
33
+		//Prevent the press from being interpreted by the browser
34
+		evt.preventDefault();
35
+		if (!evt.target || !Tools.drawingArea.contains(evt.target)) return;
36
+		var tmatrix = get_translate_matrix(evt.target);
37
+		selected = { x: x - tmatrix.e, y: y - tmatrix.f, elem: evt.target };
38
+	}
39
+
40
+	function moveElement(x, y) {
41
+		if (!selected) return;
42
+		var deltax = x - selected.x;
43
+		var deltay = y - selected.y;
44
+		var msg = { type: "update", id: selected.elem.id, deltax: deltax, deltay: deltay };
45
+		var now = performance.now();
46
+		if (now - last_sent > 70) {
47
+			last_sent = now;
48
+			Tools.drawAndSend(msg);
49
+		} else {
50
+			draw(msg);
51
+		}
52
+	}
53
+
54
+	function get_translate_matrix(elem) {
55
+		// Returns the first translate or transform matrix or makes one
56
+		var translate = null;
57
+		for (var i = 0; i < elem.transform.baseVal.numberOfItems; ++i) {
58
+			var baseVal = elem.transform.baseVal[i];
59
+			// quick tests showed that even if one changes only the fields e and f or uses createSVGTransformFromMatrix
60
+			// the brower may add a SVG_TRANSFORM_MATRIX instead of a SVG_TRANSFORM_TRANSLATE
61
+			if (baseVal.type === SVGTransform.SVG_TRANSFORM_TRANSLATE || baseVal.type === SVGTransform.SVG_TRANSFORM_MATRIX) {
62
+				translate = baseVal;
63
+				break;
64
+			}
65
+		}
66
+		if (translate == null) {
67
+			translate = elem.transform.baseVal.createSVGTransformFromMatrix(Tools.svg.createSVGMatrix());
68
+			elem.transform.baseVal.appendItem(translate);
69
+		}
70
+		return translate.matrix;
71
+	}
72
+
73
+	function draw(data) {
74
+		switch (data.type) {
75
+			case "update":
76
+				var elem = Tools.svg.getElementById(data.id);
77
+				if (!elem) throw new Error("Mover: Tried to move an element that does not exist.");
78
+				var tmatrix = get_translate_matrix(elem);
79
+				tmatrix.e = data.deltax || 0;
80
+				tmatrix.f = data.deltay || 0;
81
+				break;
82
+
83
+			default:
84
+				throw new Error("Mover: 'move' instruction with unknown type. ", data);
85
+		}
86
+	}
87
+
88
+	function startHand(x, y, evt, isTouchEvent) {
32
 		if (!isTouchEvent) {
89
 		if (!isTouchEvent) {
33
-			pressed = true;
34
-			orig.x = document.documentElement.scrollLeft + evt.clientX;
35
-			orig.y = document.documentElement.scrollTop + evt.clientY;
90
+			selected = {
91
+				x: document.documentElement.scrollLeft + evt.clientX,
92
+				y: document.documentElement.scrollTop + evt.clientY,
93
+			}
36
 		}
94
 		}
37
 	}
95
 	}
38
-	function move(x, y, evt, isTouchEvent) {
39
-		if (pressed && !isTouchEvent) { //Let the browser handle touch to scroll
40
-			window.scrollTo(orig.x - evt.clientX, orig.y - evt.clientY);
96
+	function moveHand(x, y, evt, isTouchEvent) {
97
+		if (selected && !isTouchEvent) { //Let the browser handle touch to scroll
98
+			window.scrollTo(selected.x - evt.clientX, selected.y - evt.clientY);
41
 		}
99
 		}
42
 	}
100
 	}
43
-	function release() {
44
-		pressed = false;
101
+
102
+	function press(x, y, evt, isTouchEvent) {
103
+		if (!handTool.secondary.active) startHand(x, y, evt, isTouchEvent);
104
+		else startMovingElement(x, y, evt, isTouchEvent);
105
+	}
106
+
107
+
108
+	function move(x, y, evt, isTouchEvent) {
109
+		if (!handTool.secondary.active) moveHand(x, y, evt, isTouchEvent);
110
+		else moveElement(x, y, evt, isTouchEvent);
111
+	}
112
+
113
+	function release(x, y, evt, isTouchEvent) {
114
+		move(x, y, evt, isTouchEvent);
115
+		selected = null;
45
 	}
116
 	}
46
 
117
 
47
-	Tools.add({ //The new tool
118
+	function switchTool() {
119
+		selected = null;
120
+	}
121
+
122
+	var handTool = { //The new tool
48
 		"name": "Hand",
123
 		"name": "Hand",
49
 		"shortcut": "h",
124
 		"shortcut": "h",
50
 		"listeners": {
125
 		"listeners": {
51
 			"press": press,
126
 			"press": press,
52
 			"move": move,
127
 			"move": move,
53
-			"release": release
128
+			"release": release,
129
+		},
130
+		"secondary": {
131
+			"name": "Mover",
132
+			"icon": "tools/hand/mover.svg",
133
+			"active": false,
134
+			"switch": switchTool,
54
 		},
135
 		},
55
-		"icon": "tools/hand/icon.svg",
136
+		"draw": draw,
137
+		"icon": "tools/hand/hand.svg",
56
 		"mouseCursor": "move",
138
 		"mouseCursor": "move",
57
 		"showMarker": true,
139
 		"showMarker": true,
58
-	});
59
-
60
-	//The hand tool is selected by default
61
-	Tools.change("Hand");
140
+	};
141
+	Tools.add(handTool);
142
+	Tools.change("Hand"); // Use the hand tool by default
62
 })(); //End of code isolation
143
 })(); //End of code isolation

client-data/tools/hand/icon.svg → client-data/tools/hand/hand.svg Vedi File

1
-<svg viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
1
+<svg viewBox="0 0 70 70" xmlns="http://www.w3.org/2000/svg">
2
     <g fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
2
     <g fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
3
         <path d="M18.66 19.24a3.53 3.53 0 10-5.57 4.25l11.54 15.1 2.69 3.39-7.9-10.33a3.53 3.53 0 10-5.56 4.24l7.9 10.34 6.26 7.9c5.47 6.27 14.52 5.93 20.79.46a19.62 19.62 0 006.51-12.31c.39-4.23.81-15.3.81-15.3-.18-2.6-3.13-4.52-3.51-3.18l-4.9 9.76-3.36-4.23 3.36 4.23-3.36-4.23-13.47-17.2a3.53 3.53 0 10-5.56 4.24l4.25 5.57L36 30.42 22.58 12.74a3.53 3.53 0 10-5.56 4.25L31.69 36"/>
3
         <path d="M18.66 19.24a3.53 3.53 0 10-5.57 4.25l11.54 15.1 2.69 3.39-7.9-10.33a3.53 3.53 0 10-5.56 4.24l7.9 10.34 6.26 7.9c5.47 6.27 14.52 5.93 20.79.46a19.62 19.62 0 006.51-12.31c.39-4.23.81-15.3.81-15.3-.18-2.6-3.13-4.52-3.51-3.18l-4.9 9.76-3.36-4.23 3.36 4.23-3.36-4.23-13.47-17.2a3.53 3.53 0 10-5.56 4.24l4.25 5.57L36 30.42 22.58 12.74a3.53 3.53 0 10-5.56 4.25L31.69 36"/>
4
         <path stroke-miterlimit="10" d="M11.67 42.87c0 2.57 1.75 4.64 3.9 4.64M7.06 42.44c0 5.6 3.81 10.12 8.52 10.12M45.26 21.24c0-2.57-1.75-4.65-3.9-4.65M49.87 21.67c0-5.6-3.8-10.12-8.51-10.12"/>
4
         <path stroke-miterlimit="10" d="M11.67 42.87c0 2.57 1.75 4.64 3.9 4.64M7.06 42.44c0 5.6 3.81 10.12 8.52 10.12M45.26 21.24c0-2.57-1.75-4.65-3.9-4.65M49.87 21.67c0-5.6-3.8-10.12-8.51-10.12"/>

+ 10
- 0
client-data/tools/hand/mover.svg Vedi File

1
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 70 70" >
2
+    <path id="arrow" stroke="black" fill="none" stroke-width="2" stroke-linecap="round"
3
+        d="M 8 28 h 54
4
+           M 8 42 h 54
5
+           M 57 22 L 70 35 L 57 48
6
+        " />
7
+    <use href="#arrow" transform="rotate(90 35 35)"/>
8
+    <use href="#arrow" transform="rotate(180 35 35)"/>
9
+    <use href="#arrow" transform="rotate(-90 35 35)"/>
10
+</svg>

+ 1
- 1
package.json Vedi File

1
 {
1
 {
2
    "name": "whitebophir",
2
    "name": "whitebophir",
3
    "description": "Online collaborative whiteboard",
3
    "description": "Online collaborative whiteboard",
4
-   "version": "1.5.0",
4
+   "version": "1.6.0",
5
    "keywords": [
5
    "keywords": [
6
       "collaborative",
6
       "collaborative",
7
       "whiteboard"
7
       "whiteboard"

+ 4
- 1
server/boardData.js Vedi File

70
 
70
 
71
 /** Update the data in the board
71
 /** Update the data in the board
72
  * @param {string} id - Identifier of the data to update.
72
  * @param {string} id - Identifier of the data to update.
73
- * @param {object} data - Object containing the the values to update.
73
+ * @param {object} data - Object containing the values to update.
74
  * @param {boolean} create - True if the object should be created if it's not currently in the DB.
74
  * @param {boolean} create - True if the object should be created if it's not currently in the DB.
75
 */
75
 */
76
 BoardData.prototype.update = function (id, data, create) {
76
 BoardData.prototype.update = function (id, data, create) {
77
+	delete data.type;
78
+	delete data.tool;
79
+
77
 	var obj = this.board[id];
80
 	var obj = this.board[id];
78
 	if (typeof obj === "object") {
81
 	if (typeof obj === "object") {
79
 		for (var i in data) {
82
 		for (var i in data) {

+ 10
- 3
server/createSVG.js Vedi File

11
 			case '>': return '&gt;';
11
 			case '>': return '&gt;';
12
 			case '&': return '&amp;';
12
 			case '&': return '&amp;';
13
 			case '"': return '&quot;';
13
 			case '"': return '&quot;';
14
-			case "'": return '&#39;'; }});
14
+			case "'": return '&#39;';
15
+		}
16
+	});
15
 }
17
 }
16
 
18
 
17
 function renderPath(el, pathstring) {
19
 function renderPath(el, pathstring) {
23
 			('opacity="' + parseFloat(el.opacity) + '" ') : '') +
25
 			('opacity="' + parseFloat(el.opacity) + '" ') : '') +
24
 		'stroke="' + htmlspecialchars(el.color) + '" ' +
26
 		'stroke="' + htmlspecialchars(el.color) + '" ' +
25
 		'd="' + pathstring + '" ' +
27
 		'd="' + pathstring + '" ' +
28
+		(el.deltax || el.deltay ?
29
+			('transform="translate(' + (+el.deltax) + ',' + (+el.deltay) + ')"') : '') +
26
 		'/>';
30
 		'/>';
27
 }
31
 }
28
 
32
 
37
 			'y="' + (el.y | 0) + '" ' +
41
 			'y="' + (el.y | 0) + '" ' +
38
 			'font-size="' + (el.size | 0) + '" ' +
42
 			'font-size="' + (el.size | 0) + '" ' +
39
 			'fill="' + htmlspecialchars(el.color || "#000") + '" ' +
43
 			'fill="' + htmlspecialchars(el.color || "#000") + '" ' +
44
+			(el.deltax || el.deltay ? ('transform="translate(' + (el.deltax || 0) + ',' + (el.deltay || 0) + ')"') : '') +
40
 			'>' + htmlspecialchars(el.txt || "") + '</text>';
45
 			'>' + htmlspecialchars(el.txt || "") + '</text>';
41
 	},
46
 	},
42
 	/**
47
 	/**
65
 			'height="' + (el.y2 - el.y) + '" ' +
70
 			'height="' + (el.y2 - el.y) + '" ' +
66
 			'stroke="' + htmlspecialchars(el.color) + '" ' +
71
 			'stroke="' + htmlspecialchars(el.color) + '" ' +
67
 			'stroke-width="' + (el.size | 0) + '" ' +
72
 			'stroke-width="' + (el.size | 0) + '" ' +
73
+			(el.deltax || el.deltay ? ('transform="translate(' + (el.deltax || 0) + ',' + (el.deltay || 0) + ')"') : '') +
68
 			'/>';
74
 			'/>';
69
 	},
75
 	},
70
 	/**
76
 	/**
100
 	const margin = 400;
106
 	const margin = 400;
101
 	const elems = Object.values(obj);
107
 	const elems = Object.values(obj);
102
 	const dim = elems.reduce(function (dim, elem) {
108
 	const dim = elems.reduce(function (dim, elem) {
109
+		if (elem._children) elem = elem._children[0];
103
 		return [
110
 		return [
104
-			Math.max(elem.x + margin | 0, dim[0]),
105
-			Math.max(elem.y + margin | 0, dim[1]),
111
+			Math.max(elem.x + margin + elem.deltax | 0, dim[0]),
112
+			Math.max(elem.y + margin + elem.deltay | 0, dim[1]),
106
 		]
113
 		]
107
 	}, [margin, margin]);
114
 	}, [margin, margin]);
108
 	writeable.write(
115
 	writeable.write(

+ 0
- 1
server/sockets.js Vedi File

140
 			if (id) board.delete(id);
140
 			if (id) board.delete(id);
141
 			break;
141
 			break;
142
 		case "update":
142
 		case "update":
143
-			delete message.type;
144
 			if (id) board.update(id, message);
143
 			if (id) board.update(id, message);
145
 			break;
144
 			break;
146
 		case "child":
145
 		case "child":

+ 10
- 2
server/translations.json Vedi File

15
         "click_to_toggle": "click to toggle",
15
         "click_to_toggle": "click to toggle",
16
         "menu": "Menu",
16
         "menu": "Menu",
17
         "text": "Text",
17
         "text": "Text",
18
+        "mover": "Mover",
18
         "straight_line": "Straight line",
19
         "straight_line": "Straight line",
19
         "pencil": "Pencil",
20
         "pencil": "Pencil",
20
         "grid": "Grid",
21
         "grid": "Grid",
54
         "eraser": "Gomme",
55
         "eraser": "Gomme",
55
         "white-out": "Blanco",
56
         "white-out": "Blanco",
56
         "hand": "Main",
57
         "hand": "Main",
58
+        "mover": "Déplacer un élément",
57
         "straight_line": "Ligne droite",
59
         "straight_line": "Ligne droite",
58
         "grid": "Grille",
60
         "grid": "Grille",
59
         "keyboard_shortcut": "raccourci clavier",
61
         "keyboard_shortcut": "raccourci clavier",
73
     },
75
     },
74
     "de": {
76
     "de": {
75
         "hand": "Hand",
77
         "hand": "Hand",
78
+        "mover": "Verschiebung",
76
         "loading": "Lädt",
79
         "loading": "Lädt",
77
         "tagline": "Ein freies quelloffenes kollaboratives Zeichentool. Zeichnet eure Ideen zusammen auf WBO!",
80
         "tagline": "Ein freies quelloffenes kollaboratives Zeichentool. Zeichnet eure Ideen zusammen auf WBO!",
78
         "configuration": "Konfiguration",
81
         "configuration": "Konfiguration",
110
     },
113
     },
111
     "ja": {
114
     "ja": {
112
         "hand": "手のひらツール",
115
         "hand": "手のひらツール",
116
+        "mover": "変位",
113
         "loading": "読み込み中",
117
         "loading": "読み込み中",
114
         "tagline": "無料でオープンソースの協同作業できるオンラインホワイトボード。WBOでアイディアを共有しましょう!",
118
         "tagline": "無料でオープンソースの協同作業できるオンラインホワイトボード。WBOでアイディアを共有しましょう!",
115
         "configuration": "設定",
119
         "configuration": "設定",
157
         "text": "Текст",
161
         "text": "Текст",
158
         "eraser": "Ластик",
162
         "eraser": "Ластик",
159
         "white-out": "Корректор",
163
         "white-out": "Корректор",
160
-        "hand": "Движение",
164
+        "hand": "Рука",
161
         "straight_line": "Прямая линия",
165
         "straight_line": "Прямая линия",
162
         "rectangle": "Прямоугольник",
166
         "rectangle": "Прямоугольник",
163
         "square": "Квадрат",
167
         "square": "Квадрат",
165
         "ellipse": "Эллипс",
169
         "ellipse": "Эллипс",
166
         "click_to_toggle": "нажмите, чтобы переключиться",
170
         "click_to_toggle": "нажмите, чтобы переключиться",
167
         "zoom": "Лупа",
171
         "zoom": "Лупа",
172
+        "mover": "Сдвинуть объект",
168
         "grid": "Сетка",
173
         "grid": "Сетка",
169
-        "configuration": "Настроики",
174
+        "configuration": "Настройки",
170
         "keyboard_shortcut": "горячая клавиша",
175
         "keyboard_shortcut": "горячая клавиша",
171
         "mousewheel": "колёсико мыши ",
176
         "mousewheel": "колёсико мыши ",
172
         "tagline": "Бесплатная и открытая доска для совместной работы в интернете. Рисуете свои идеи вместе в WBO !",
177
         "tagline": "Бесплатная и открытая доска для совместной работы в интернете. Рисуете свои идеи вместе в WBO !",
200
         "eraser": "橡皮",
205
         "eraser": "橡皮",
201
         "white-out": "修正液",
206
         "white-out": "修正液",
202
         "hand": "移动",
207
         "hand": "移动",
208
+        "mover": "平移",
203
         "straight_line": "直线",
209
         "straight_line": "直线",
204
         "configuration": "刷设置",
210
         "configuration": "刷设置",
205
         "keyboard_shortcut": "键盘快捷键",
211
         "keyboard_shortcut": "键盘快捷键",
220
     },
226
     },
221
     "es": {
227
     "es": {
222
         "hand": "Mano",
228
         "hand": "Mano",
229
+        "mover": "Desplazamiento",
223
         "loading": "Cargando",
230
         "loading": "Cargando",
224
         "tagline": "Una herramienta de dibujo colaborativa en línea gratuita y de código abierto. Esboce nuevas ideas en la pizarra colaborativa WBO !",
231
         "tagline": "Una herramienta de dibujo colaborativa en línea gratuita y de código abierto. Esboce nuevas ideas en la pizarra colaborativa WBO !",
225
         "configuration": "Configuration",
232
         "configuration": "Configuration",
257
     },
264
     },
258
     "it": {
265
     "it": {
259
         "hand": "Mano",
266
         "hand": "Mano",
267
+        "mover": "Spostamento",
260
         "loading": "Caricamento in corso",
268
         "loading": "Caricamento in corso",
261
         "tagline": "Uno strumento collaborativo per disegnare online, gratuito e open source. Disegniamo insieme nuove idee su WBO!",
269
         "tagline": "Uno strumento collaborativo per disegnare online, gratuito e open source. Disegniamo insieme nuove idee su WBO!",
262
         "configuration": "Configurazione",
270
         "configuration": "Configurazione",

Loading…
Annulla
Salva