unclean SPARC
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
/**
|
||||
* Magic Chalkboard demo app
|
||||
*
|
||||
* A simple app to share an editable canvas between bus users.
|
||||
* Demonstrates :
|
||||
* - subscription to a custom channel
|
||||
* - burst event send/receive of custom event types
|
||||
* - lightspeed response time of events broadcasting
|
||||
* - accuracy and reliability of event queuing
|
||||
* - implementation of app design with bus connected/disconnected.
|
||||
*
|
||||
* @author Michael Fallise
|
||||
* @version 1.0
|
||||
*/
|
||||
class ChalkboardView extends EICDomContent {
|
||||
|
||||
options = {
|
||||
background: 'rgba(0,0,0,1)',
|
||||
busChannel: 'collaborative:chalkboard'
|
||||
}
|
||||
|
||||
DOMContentLoaded() {
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
|
||||
this.useBus = false;
|
||||
this.busUsers = [];
|
||||
|
||||
this.toolbox = new Form(this.find('.toolbox'));
|
||||
|
||||
let colors = this.findAll('.color-sample');
|
||||
for(let color of colors) color.addEventListener('click', this.onColorChange.bind(this));
|
||||
|
||||
let pens = this.findAll('.pen-sample');
|
||||
for(let pen of pens) pen.addEventListener('click', this.onPenChange.bind(this));
|
||||
|
||||
let dl = components.find(item => item.el.classList.contains('dl-board'));
|
||||
dl.addEventListener('click', this.onBoardDownload.bind(this));
|
||||
|
||||
let clear = components.find(item => item.el.classList.contains('clear-board'));
|
||||
clear.addEventListener('click', this.onBoardClear.bind(this));
|
||||
|
||||
this.board = this.find('canvas');
|
||||
this.board.style.background = this.options.background;
|
||||
this.ctx = this.board.getContext("2d");
|
||||
this.ctx.lineCap = 'round';
|
||||
|
||||
this.drawing = this.eraser = false;
|
||||
this.mouseCurrent = { x: 0, y: 0 };
|
||||
this.mousePrev = { x: 0, y: 0 };
|
||||
|
||||
// referencing computed event handlers
|
||||
this.mouseDownHandler = this.onChalkStart.bind(this);
|
||||
this.mouseMoveHandler = this.onChalkMove.bind(this);
|
||||
this.mouseUpHandler = this.onChalkEnd.bind(this);
|
||||
this.busDrawHandler = this.onBusDraw.bind(this);
|
||||
this.busClearHandler = this.onBusClear.bind(this);
|
||||
this.busPingHandler = this.onBusPing.bind(this);
|
||||
this.busJoinHandler = this.onBusJoin.bind(this);
|
||||
this.busLeaveHandler = this.onBusLeave.bind(this);
|
||||
|
||||
this.setBrush();
|
||||
this.setBoardSize();
|
||||
this.clear();
|
||||
this.initListeners();
|
||||
}
|
||||
|
||||
DOMContentRemoved() { this.closeListeners(); }
|
||||
|
||||
DOMContentResized() {
|
||||
let data = this.board.toDataURL('image/png');
|
||||
this.setBoardSize();
|
||||
let img = new Image;
|
||||
let self = this
|
||||
img.onload = function() { self.ctx.drawImage(img, 0, 0); };
|
||||
img.src = data;
|
||||
|
||||
}
|
||||
|
||||
setBoardSize() {
|
||||
let rect = this.board.getBoundingClientRect()
|
||||
this.board.width = rect.width;
|
||||
this.board.height = rect.height;
|
||||
}
|
||||
|
||||
/* Nike to Mike : As planned, made higher-level addBusListener & removeBusListener
|
||||
* Changes : Includes channel filtering (important to avoid chan injections by script-kiddies)
|
||||
*
|
||||
* You can replace old-style :
|
||||
* app.events.addEvent('MessageBus.XXX', handler, scope)
|
||||
* app.events.removeEvent('MessageBus.XXX', handler, scope)
|
||||
* => handler receives pure JS event, and probably uses e.detail.payload
|
||||
*
|
||||
* by new-style:
|
||||
* app.MessageBus.addBusListener('XXX', [this.busChannel], handler, scope)
|
||||
* app.MessageBus.removeBusListener('XXX', handler, scope)
|
||||
* => handler receives (realChan, payload, sender)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
initListeners() {
|
||||
this.board.addEventListener('mousedown', this.mouseDownHandler);
|
||||
this.board.addEventListener('mousemove', this.mouseMoveHandler);
|
||||
this.board.addEventListener('mouseup', this.mouseUpHandler);
|
||||
// disabling context menu on canvas in order to use right button for eraser
|
||||
this.board.oncontextmenu = function() { return false;}
|
||||
|
||||
if(app.MessageBus.connected) {
|
||||
console.log("USING BUS")
|
||||
app.MessageBus.subscribe([this.options.busChannel])
|
||||
.then( payload => {
|
||||
app.MessageBus.addBusListener('board_draw', ['collaborative:chalkboard'], this.busDrawHandler, 'chalkboard');
|
||||
app.MessageBus.addBusListener('board_clear', ['collaborative:chalkboard'], this.busClearHandler, 'chalkboard');
|
||||
app.MessageBus.addBusListener('board_ping', ['collaborative:chalkboard'], this.busPingHandler, 'chalkboard');
|
||||
app.MessageBus.addBusListener('board_join', ['collaborative:chalkboard'], this.busJoinHandler, 'chalkboard');
|
||||
app.MessageBus.addBusListener('board_leave', ['collaborative:chalkboard'], this.busLeaveHandler, 'chalkboard');
|
||||
this.useBus = true;
|
||||
this.pingUsers();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
closeListeners() {
|
||||
if(this.useBus) {
|
||||
app.MessageBus.removeBusListener('board_draw', this.busDrawHandler, 'chalkboard');
|
||||
app.MessageBus.removeBusListener('board_clear', this.busClearHandler, 'chalkboard');
|
||||
app.MessageBus.removeBusListener('board_join', this.busJoinHandler, 'chalkboard');
|
||||
app.MessageBus.removeBusListener('board_ping', this.busPingHandler, 'chalkboard');
|
||||
app.MessageBus.removeBusListener('board_leave', this.busLeaveHandler, 'chalkboard');
|
||||
this.leaveApp();
|
||||
this.useBus = false;
|
||||
}
|
||||
}
|
||||
|
||||
setBrush() { this.brush = this.toolbox.value; }
|
||||
|
||||
draw(brush, from, to) {
|
||||
this.ctx.fillStyle = brush.color;
|
||||
this.ctx.strokeStyle = brush.color;
|
||||
this.ctx.lineWidth = brush.width;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(from.x, from.y);
|
||||
this.ctx.lineTo(to.x, to.y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.ctx.fillStyle = this.options.background;
|
||||
this.ctx.fillRect(0, 0, this.board.width, this.board.height);
|
||||
this.ctx.fillStyle = this.brush.color;
|
||||
}
|
||||
|
||||
onChalkStart(event) {
|
||||
this.drawing = true;
|
||||
this.eraser = event.button == 2,
|
||||
|
||||
this.mousePrev.x = this.mouseCurrent.x;
|
||||
this.mousePrev.y = this.mouseCurrent.y;
|
||||
|
||||
this.setBrush();
|
||||
}
|
||||
|
||||
onChalkMove(event) {
|
||||
|
||||
let rect = event.target.getBoundingClientRect();
|
||||
|
||||
this.mouseCurrent.x = event.pageX - rect.left;
|
||||
this.mouseCurrent.y = event.pageY - rect.top;
|
||||
|
||||
if(this.drawing) {
|
||||
|
||||
this.board.classList.add('drawing');
|
||||
|
||||
let brush = this.eraser ? { color: this.options.background, width: 20 }: this.brush;
|
||||
|
||||
if(this.useBus) {
|
||||
app.MessageBus.sendEvent(this.options.busChannel,'board_draw', {
|
||||
uid: app.User.identity.uuid,
|
||||
brush: brush,
|
||||
from: this.mousePrev,
|
||||
to: this.mouseCurrent,
|
||||
});
|
||||
} else {
|
||||
this.draw(brush, this.mousePrev, this.mouseCurrent);
|
||||
}
|
||||
|
||||
this.mousePrev.x = this.mouseCurrent.x;
|
||||
this.mousePrev.y = this.mouseCurrent.y;
|
||||
}
|
||||
}
|
||||
|
||||
onChalkEnd(event) {
|
||||
this.drawing = this.eraser = false;
|
||||
this.board.classList.remove('drawing');
|
||||
}
|
||||
|
||||
onColorChange(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.find('.color-sample.selected').classList.remove('selected');
|
||||
|
||||
let color = event.currentTarget;
|
||||
color.classList.add('selected');
|
||||
this.find('input[name="color"]').value = color.dataset.value;
|
||||
}
|
||||
|
||||
onPenChange(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.find('.pen-sample.selected').classList.remove('selected');
|
||||
|
||||
let pen = event.currentTarget;
|
||||
pen.classList.add('selected');
|
||||
this.find('input[name="width"]').value = pen.dataset.value;
|
||||
}
|
||||
|
||||
onBusPing(event) {
|
||||
let payload = {
|
||||
uid: app.User.identity.uuid,
|
||||
name: app.User.identity.firstname + ' ' + app.User.identity.lastname
|
||||
}
|
||||
|
||||
app.MessageBus.sendEvent(this.options.busChannel,'board_join', payload);
|
||||
}
|
||||
|
||||
joinApp() {
|
||||
let payload = {
|
||||
uid: app.User.identity.uuid,
|
||||
name: app.User.identity.firstname + ' ' + app.User.identity.lastname
|
||||
}
|
||||
|
||||
app.MessageBus.sendEvent(this.options.busChannel,'board_join', payload);
|
||||
}
|
||||
|
||||
leaveApp() { app.MessageBus.sendEvent(this.options.busChannel,'board_leave'); }
|
||||
|
||||
pingUsers() {
|
||||
this.busUsers = [];
|
||||
|
||||
let container = this.find('.users');
|
||||
container.innerHTML = '';
|
||||
|
||||
app.MessageBus.sendEvent(this.options.busChannel,'board_ping');
|
||||
}
|
||||
|
||||
refreshUsers() {
|
||||
let container = this.find('.users');
|
||||
console.log('', this.busUsers)
|
||||
for(let user of this.busUsers) {
|
||||
if(!user.tag) {
|
||||
let tag = new Chip(null, {
|
||||
label: user.name,
|
||||
icon: 'icon-edit',
|
||||
size: 'small',
|
||||
severity: 'secondary'
|
||||
});
|
||||
container.append(tag.el)
|
||||
user.tag = tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onBusDraw(chan, payload, sender) {
|
||||
|
||||
/*
|
||||
for(let item of this.busUsers) {
|
||||
item.tag.severity = 'secondary';
|
||||
}
|
||||
|
||||
let user = this.busUsers.find(item => item.uid == payload.uid);
|
||||
user.tag.severity = 'primary';
|
||||
*/
|
||||
this.draw(payload.brush, payload.from, payload.to);
|
||||
}
|
||||
|
||||
onBusClear(chan, payload, sender) { this.clear(); }
|
||||
|
||||
onBusJoin(chan, payload, sender) {
|
||||
let user = payload;
|
||||
let index = this.busUsers.findIndex(item => item.uid == user.uid);
|
||||
console.log('index', index, this.busUsers)
|
||||
console.log('user', user)
|
||||
if(index == -1) {
|
||||
this.busUsers.push(user);
|
||||
this.refreshUsers();
|
||||
}
|
||||
}
|
||||
|
||||
onBusLeave(chan, payload, sender) { this.pingUsers(); }
|
||||
|
||||
onBoardDownload(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let data = this.board.toDataURL('image/png');
|
||||
|
||||
let link = document.createElement('a');
|
||||
link.download = 'chalkboard.png';
|
||||
link.href = data;
|
||||
link.click();
|
||||
}
|
||||
|
||||
onBoardClear(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if(this.useBus) {
|
||||
app.MessageBus.sendEvent(this.options.busChannel,'board_clear', {});
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('ChalkboardView',ChalkboardView);
|
||||
Reference in New Issue
Block a user