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.