|
|
|
# Icebreaker Toolkit
|
|
|
|
Dem Moderator stehen durch das Icebreaker-Toolkit vier verschiedene Ansichten zur Verfügung, mittels welcher sich die Teammitglieder miteinander vertraut machen können und Unterhaltungen angestoßen werden sollen. Sie alle bedienen sich der Metapher eines Runden Tisches, an dem alle zusammenkommen.
|
|
|
|
In Accelerator wurden zwei dieser Ansichten bereits implementiert.
|
|
|
|
|
|
|
|
## Weltkarte
|
|
|
|
Durch eine Karte mit Markierungen der Teilnehmer wird ein Bewusstsein dafür
|
|
|
|
geschaffen, wo sich die Gesprächsteilnehmer befinden.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
#### Implementierungsdetails:
|
|
|
|
Die Weltkarte wurde mit den folgenden Bibliotheken umgesetzt:
|
|
|
|
|
|
|
|
|
|
|
|
| Bibliothek | Beschreibung |
|
|
|
|
| -------- | -------- |
|
|
|
|
| Leaflet | Leaflet ist eine freie JavaScript Bibliothek, mit der dynamische Onlinekarten umgesetzt werden können. Leaflet verwendet hierfür HTML5 und CSS3 und unterstützt dadurch die neusten desktop- und mobilen Browser. |
|
|
|
|
| Mapbox | Mapbox bietet Onlinekarten-Daten für Anwendungen an. In Verbindung mit Leaflet können diese Kartendaten interaktiv verwendet und gestaltet werden. |
|
|
|
|
| Aloglia | Aloglia ist eine schnelle, zuverlässige und moderne Suche für unterschiedliche Daten. Mit Hilfe von Aloglia können Städt der ganzen Welt gefunden werden und biete eine Auto-Vervollständigung während der Suche. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Implementierung in server.js:
|
|
|
|
Die Funktion `socket.on('setUserPosition', function(data) {...}` wird aufgerufen, wenn ein Nutzer seinen Standort eingegeben hat und diesen absendet. Die Server.js bekommt den Standort inklusive Land und die zugehörige socketID des Nutzers. Der Standort wird mit Hilfe des Node.js Pakets „mapbox-geocoding“ in die dazugeörigen Geografischen Koordinaten umgewandelt. Die Koordinaten werden zur zugehörigen SocketID in der Variable: `rooms[roomName]["user"][{socketID}]["userPos"]` gespeichert, sodass diese auch für alle anderen Benutzer zur Verfügung steht. Abschließend wird der eingegebene Standort an alle Teilnehmer des Raums gesendet: `sendToHoleRoom(roomName, 'showUserPositions', userPositions)`
|
|
|
|
|
|
|
|
Auch wenn ein neuer Teilnehmer einen Raum betritt `socket.on('join', function (content)` oder verlässt `socket.on('disconnect', function (content)` werden alle Daten aktualisiert und an den gesamten Raum gesendet.
|
|
|
|
|
|
|
|
**Codeauszug der server.js:**
|
|
|
|
```
|
|
|
|
socket.on('setUserPosition', function(data) {
|
|
|
|
var user = data.socketId;
|
|
|
|
var userPos = data.userPos;
|
|
|
|
|
|
|
|
geo.geocode('mapbox.places', userPos, function (err, geoData) {
|
|
|
|
for(var i=0;i<rooms[roomName]["user"].length;i++) {
|
|
|
|
if(rooms[roomName]["user"][i]["socketId"] == user) {
|
|
|
|
rooms[roomName]["user"][i]["userPos"] = JSON.stringify(geoData.features[0].center.reverse());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var userPositions = [];
|
|
|
|
for(var i=0;i<rooms[roomName]["user"].length;i++) {
|
|
|
|
userPositions.push({"username": rooms[roomName]["user"][i]["username"], "userPos" : rooms[roomName]["user"][i]["userPos"]})
|
|
|
|
}
|
|
|
|
|
|
|
|
sendToHoleRoom(roomName, 'showUserPositions', userPositions);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Implementierung in uiHelper.js:
|
|
|
|
Die Weltkarte wird initialisiert (`worldMap = L.map('circleWorld)`) und dem Div `circleWorld` zugeordnet. Außerdem wird der Zoomfaktor auf 7 limitiert `maxZoom: 7`, sodass die Privatsphäre der Nutzer gewährt wird und nicht detailreich in die Weltkarte gezoomt werden kann.
|
|
|
|
|
|
|
|
**Codeauszug: Weltkarte initialisieren**
|
|
|
|
```
|
|
|
|
worldMap = L.map('circleWorld').setView([51.505, -0.09], 4);
|
|
|
|
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
|
|
|
|
attribution: '',
|
|
|
|
maxZoom: 7,
|
|
|
|
id: 'mapbox.streets',
|
|
|
|
accessToken: 'pk.eyJ1IjoidG9iMjgiLCJhIjoiY2p1emVpcDZvMTgycjQ0bDZndTNyeHd0bSJ9.BDS126tkUQipkS3rq2ZI0g'
|
|
|
|
}).addTo(worldMap);
|
|
|
|
|
|
|
|
setTimeout(function () {
|
|
|
|
worldMap.invalidateSize();
|
|
|
|
}, 800);
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### Implementierung in uiEvents.js:
|
|
|
|
Aloglia-Autocomplete wird initialisiert `var placesAutocomplete = places({…}` und einem Inputfeld zugeordnet `container: document.querySelector('#inputUserPosition')`.
|
|
|
|
|
|
|
|
Ein Click-Listener wird dem Absenden-Button der User-Position zugewiesen `$("#userPositionOkBtn").click(function(){…}`.
|
|
|
|
|
|
|
|
**Codeauszug uiEvents; initialisieren von Autocomplete und Button-Click-Listener**
|
|
|
|
```
|
|
|
|
var placesAutocomplete = places({
|
|
|
|
appId: "plPJQSWUMPAP",
|
|
|
|
apiKey: "c60c0dfbe924871cfcd2e5767a9fba33",
|
|
|
|
container: document.querySelector('#inputUserPosition'),
|
|
|
|
language: 'de', // Receives results in German
|
|
|
|
type: 'city', // Search only for cities names
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#userPositionOkBtn").click(function(){
|
|
|
|
$('#userPositionModal').modal('hide');
|
|
|
|
var userPos = $('#inputUserPosition').val();
|
|
|
|
var data = {"userPos":userPos, "socketId":ownSocketId}
|
|
|
|
sendUserPosition(data);
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### Implementierung in connect.js:
|
|
|
|
In der connect.js werden alle Daten empfangen, die über das Backend gesendet wurden. Die Funktion `signaling_socket.on('showUserPositions', function (data) {…}` aktualisiert alle Marker der Benutzer auf der Weltkarte.
|
|
|
|
|
|
|
|
Über die Funktion `sendUserPosition(position) {…}` werden die Positionsdaten eines Nutzers an das Backend (server.js) gesendet und dort gespeichert.
|
|
|
|
|
|
|
|
**Codeauszug connect.js**
|
|
|
|
```
|
|
|
|
signaling_socket.on('showUserPositions', function (data) {
|
|
|
|
for(var i = 0; i < mapMarkers.length; i++){
|
|
|
|
worldMap.removeLayer(mapMarkers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var i=0;i<data.length;i++) {
|
|
|
|
var userPosStr = data[i]["userPos"];
|
|
|
|
var userPos = JSON.parse(userPosStr);
|
|
|
|
var username = data[i]["username"];
|
|
|
|
try{
|
|
|
|
var marker = L.marker(userPos).addTo(worldMap);
|
|
|
|
marker.bindPopup("<b>"+username+"</b>").openPopup();
|
|
|
|
mapMarkers.push(marker);
|
|
|
|
}catch(e){}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Farbfelder
|
|
|
|
Jeder Teilnehmer bekommt ein Kreissegment zugeordnet. Über einen Regler wird eine
|
|
|
|
Farbe ausgewählt, die für alle sichtbar auf dem Kreis abgebildet wird.
|
|
|
|
Beispielanwendungen:
|
|
|
|
* Auswahl der Lieblingsfarbe
|
|
|
|
* Ausdrücken der eigenen Stimmung durch eine Farbe
|
|
|
|
* Meinungsbild: Zustimmung oder Ablehnung einer Aussage
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
#### Implementierung in server.js:
|
|
|
|
Die Funktion `socket.on('setUserCircleColor', function(data) {...}` wird aufgerufen, wenn ein Nutzer seinen Farbabschnitt ändert und diese absendet. Die Server.js bekommt Farbe und die zugehörige socketID des Nutzers. Die Farbe wird zur zugehörigen SocketID in der Variable: `rooms[roomName]["user"][{socketID}]["userCircleColor"]` gespeichert, sodass diese auch für alle anderen Benutzer zur Verfügung steht. Abschließend wird die ausgewählte Farbe an alle Teilnehmer des Raums gesendet: `sendToHoleRoom(roomName, 'showUserCircleColors', userCircleColors)`
|
|
|
|
|
|
|
|
Auch wenn ein neuer Teilnehmer einen Raum betritt `socket.on('join', function (content)` oder verlässt (socket.on('disconnect', function (content) ) werden alle Daten aktualisiert und an den gesamten Raum gesendet.
|
|
|
|
|
|
|
|
**Codeauszug der server.js**
|
|
|
|
```
|
|
|
|
socket.on('setUserCircleColor', function(data) {
|
|
|
|
var user = data.socketId;
|
|
|
|
var userColor = data.userColor;
|
|
|
|
|
|
|
|
for(var i=0;i<rooms[roomName]["user"].length;i++) {
|
|
|
|
if(rooms[roomName]["user"][i]["socketId"] == user) {
|
|
|
|
rooms[roomName]["user"][i]["userCircleColor"] = userColor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var userCircleColors = [];
|
|
|
|
for(var i=0;i<rooms[roomName]["user"].length;i++) {
|
|
|
|
userCircleColors.push({"username": rooms[roomName]["user"][i]["username"], "userCircleColor" : rooms[roomName]["user"][i]["userCircleColor"], "userColor" : rooms[roomName]["user"][i]["color"]})
|
|
|
|
}
|
|
|
|
|
|
|
|
sendToHoleRoom(roomName, 'showUserCircleColors', userCircleColors);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Implementierung in uiHelper.js:
|
|
|
|
Die Funktion `drawColorCircle(userColors,userNames,idColors) {…}` zeichnet einen Kreis und ordnet jeweils ein Segment einem Benutzer zu. Jeder Benutzer kann nur sein eigenes Kreissegment verändern.
|
|
|
|
|
|
|
|
**Funktion für die Kreiszeichnung**
|
|
|
|
```
|
|
|
|
function drawColorCircle(userColors,userNames,idColors) {
|
|
|
|
user = idColors.length;
|
|
|
|
var canvas = document.getElementById('userColorCircle');
|
|
|
|
var context = canvas.getContext('2d');
|
|
|
|
|
|
|
|
//center the drawing
|
|
|
|
var x = canvas.width / 2;
|
|
|
|
var y = canvas.height / 2;
|
|
|
|
|
|
|
|
//size of circle
|
|
|
|
var radius = 210;
|
|
|
|
|
|
|
|
//angle of segments
|
|
|
|
var segmentWidth = 360 / user;
|
|
|
|
|
|
|
|
// size of a pie : it is an angle in radians
|
|
|
|
var pieAngle = 2 * Math.PI / user;
|
|
|
|
|
|
|
|
var color = document.getElementById('favcol').style.backgroundColor;
|
|
|
|
|
|
|
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
|
|
//display idColors
|
|
|
|
for (var i = 0; i < user; i++) {
|
|
|
|
context.beginPath();
|
|
|
|
context.moveTo(x, y);
|
|
|
|
context.arc(x, y, radius+10, i*pieAngle, (i+1)*pieAngle, false);
|
|
|
|
// context.lineWidth = segmentDepth;
|
|
|
|
//var hueValue = i * 15;
|
|
|
|
// context.fillStyle = 'black';
|
|
|
|
context.fillStyle = idColors[i]; //use this line, when idColor input works
|
|
|
|
context.fill();
|
|
|
|
context.lineWidth = 0.5;
|
|
|
|
context.strokeStyle = 'black';
|
|
|
|
context.stroke();
|
|
|
|
}
|
|
|
|
|
|
|
|
//display chosen colors
|
|
|
|
for (var i = 0; i < userColors.length; i++) {
|
|
|
|
context.beginPath();
|
|
|
|
context.moveTo(x, y);
|
|
|
|
context.arc(x, y, radius, i*pieAngle, (i+1)*pieAngle, false);
|
|
|
|
// context.lineWidth = segmentDepth;
|
|
|
|
//var hueValue = i * 15;
|
|
|
|
context.fillStyle = userColors[i];
|
|
|
|
context.fill();
|
|
|
|
context.lineWidth = 0.5;
|
|
|
|
context.strokeStyle = 'black';
|
|
|
|
context.stroke();
|
|
|
|
|
|
|
|
//user
|
|
|
|
context.textBaseline = "middle";
|
|
|
|
context.textAlign = "center";
|
|
|
|
context.fillStyle = 'white';
|
|
|
|
context.font = '16px Verdana';
|
|
|
|
// context.fillText(userNames[i],x+((radius + 20) *Math.cos(pieAngle*i + pieAngle/2)),y+((radius + 20) *Math.sin(pieAngle*i + pieAngle/2)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Implementierung in uiEvents.js:
|
|
|
|
Ein Click-Listener wird dem Absenden-Button der Farbwahl zugewiesen `$("#setCircleColorBtn").click(function(){…}`
|
|
|
|
|
|
|
|
**Button-Click-Listener**
|
|
|
|
```
|
|
|
|
$("#setCircleColorBtn").click(function(){
|
|
|
|
// var userPos = $('#inputUserPosition').val();
|
|
|
|
var color = document.getElementById('favcol').style.backgroundColor;
|
|
|
|
var data = {"userColor":color,"username":username, "socketId":ownSocketId}
|
|
|
|
sendUserCircleColor(data);
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### Implementierung in connect.js:
|
|
|
|
In der connect.js werden alle Daten empfangen, die über das Backend gesendet wurden. Die Funktion `signaling_socket.on('showUserCircleColors', function (data) {…}` bekommt alle Farben der Benutzer eines Raumes, die an die Datei uiHelper.js weitergegeben werden, um die Kreissegmente zu zeichnen.
|
|
|
|
|
|
|
|
Über die Funktion `sendUserCircleColor(color) {…}` werden die Farbdaten eines Nutzers an das Backend (server.js) gesendet und dort gespeichert.
|
|
|
|
|
|
|
|
**Codeauszug connect.js**
|
|
|
|
```
|
|
|
|
signaling_socket.on('showUserCircleColors', function (data) {
|
|
|
|
userCircleColors = [];
|
|
|
|
userColors = [];
|
|
|
|
userNames = [];
|
|
|
|
for(var i=0;i<data.length;i++) {
|
|
|
|
var userCircleColor = data[i]["userCircleColor"];
|
|
|
|
userCircleColors.push(userCircleColor)
|
|
|
|
|
|
|
|
var userColor = data[i]["userColor"];
|
|
|
|
userColors.push(userColor)
|
|
|
|
|
|
|
|
var username = data[i]["username"];
|
|
|
|
userNames.push(username);
|
|
|
|
}
|
|
|
|
drawColorCircle(userCircleColors,userNames,userColors);
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
## Draggables
|
|
|
|
Diese Ansicht wurde ausschließlich prototypisch umgesetzt.
|
|
|
|

|
|
|
|
|
|
|
|
## Bildfelder
|
|
|
|
Diese Ansicht wurde ausschließlich prototypisch umgesetzt.
|
|
|
|
 |
|
|
|
\ No newline at end of file |