Compare commits
4 Commits
8555e3f9e2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8344624e6b | |||
| 9cbd655a4a | |||
| 34050291a8 | |||
| 78877f1211 |
9
LICENSE
Normal file
9
LICENSE
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Copyright (c) 2021 Robin Steinberg, All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
16
README.md
Normal file
16
README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# HTML Canvas with WebSocket
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
This is a demo app that allows you to draw on an HTML canvas. Everythng that gets drawn will be
|
||||||
|
transmitted to a server using websocket and displayed by a receiver app.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This app needs Node.js and npm to build and run.
|
||||||
|
|
||||||
|
## Running the app
|
||||||
|
|
||||||
|
Open the command line in the project directory and run ```npm run launch```
|
||||||
|
|
||||||
|
Then open your browser and navigate to http://localhost:8080.
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
#canvas {
|
#canvas {
|
||||||
width: 500px;
|
width: 100%;
|
||||||
height: 500px;
|
height: 800px;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
"watch": "tsc -watch -p ./",
|
"watch": "tsc -watch -p ./",
|
||||||
"serve": "serve -l 8080 .",
|
"serve": "serve -l 8080 .",
|
||||||
"debug": "npm-run-all --parallel watch serve",
|
"debug": "npm-run-all --parallel watch serve",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"run-server": "cd server && npm run launch",
|
||||||
|
"launch": "npm install && npm-run-all --parallel run-server build serve"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
#canvas {
|
#canvas {
|
||||||
width: 500px;
|
width: 100%;
|
||||||
height: 500px;
|
height: 800px;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
102
server/.gitignore
vendored
Normal file
102
server/.gitignore
vendored
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
dist/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
.node_modules/
|
||||||
|
built/*
|
||||||
|
tests/cases/rwc/*
|
||||||
|
tests/cases/test262/*
|
||||||
|
tests/cases/perf/*
|
||||||
|
!tests/cases/webharness/compilerToString.js
|
||||||
|
test-args.txt
|
||||||
|
~*.docx
|
||||||
|
\#*\#
|
||||||
|
.\#*
|
||||||
|
tests/baselines/local/*
|
||||||
|
tests/baselines/local.old/*
|
||||||
|
tests/services/baselines/local/*
|
||||||
|
tests/baselines/prototyping/local/*
|
||||||
|
tests/baselines/rwc/*
|
||||||
|
tests/baselines/test262/*
|
||||||
|
tests/baselines/reference/projectOutput/*
|
||||||
|
tests/baselines/local/projectOutput/*
|
||||||
|
tests/baselines/reference/testresults.tap
|
||||||
|
tests/services/baselines/prototyping/local/*
|
||||||
|
tests/services/browser/typescriptServices.js
|
||||||
|
src/harness/*.js
|
||||||
|
src/compiler/diagnosticInformationMap.generated.ts
|
||||||
|
src/compiler/diagnosticMessages.generated.json
|
||||||
|
src/parser/diagnosticInformationMap.generated.ts
|
||||||
|
src/parser/diagnosticMessages.generated.json
|
||||||
|
rwc-report.html
|
||||||
|
*.swp
|
||||||
|
build.json
|
||||||
|
*.actual
|
||||||
|
tests/webTestServer.js
|
||||||
|
tests/webTestServer.js.map
|
||||||
|
tests/webhost/*.d.ts
|
||||||
|
tests/webhost/webtsc.js
|
||||||
|
tests/cases/**/*.js
|
||||||
|
!tests/cases/docker/*.js/
|
||||||
|
tests/cases/**/*.js.map
|
||||||
|
*.config
|
||||||
|
scripts/eslint/built/
|
||||||
|
scripts/debug.bat
|
||||||
|
scripts/run.bat
|
||||||
|
scripts/word2md.js
|
||||||
|
scripts/buildProtocol.js
|
||||||
|
scripts/ior.js
|
||||||
|
scripts/authors.js
|
||||||
|
scripts/configurePrerelease.js
|
||||||
|
scripts/configureLanguageServiceBuild.js
|
||||||
|
scripts/open-user-pr.js
|
||||||
|
scripts/open-cherry-pick-pr.js
|
||||||
|
scripts/processDiagnosticMessages.d.ts
|
||||||
|
scripts/processDiagnosticMessages.js
|
||||||
|
scripts/produceLKG.js
|
||||||
|
scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
|
||||||
|
scripts/generateLocalizedDiagnosticMessages.js
|
||||||
|
scripts/request-pr-review.js
|
||||||
|
scripts/*.js.map
|
||||||
|
scripts/typings/
|
||||||
|
coverage/
|
||||||
|
internal/
|
||||||
|
**/.DS_Store
|
||||||
|
.settings
|
||||||
|
**/.vs
|
||||||
|
**/.vscode/*
|
||||||
|
!**/.vscode/tasks.json
|
||||||
|
!**/.vscode/settings.template.json
|
||||||
|
!**/.vscode/launch.template.json
|
||||||
|
!**/.vscode/extensions.json
|
||||||
|
!tests/cases/projects/projectOption/**/node_modules
|
||||||
|
!tests/cases/projects/NodeModulesSearch/**/*
|
||||||
|
!tests/baselines/reference/project/nodeModules*/**/*
|
||||||
|
.idea
|
||||||
|
yarn.lock
|
||||||
|
yarn-error.log
|
||||||
|
.parallelperf.*
|
||||||
|
tests/cases/user/*/package-lock.json
|
||||||
|
tests/cases/user/*/node_modules/
|
||||||
|
tests/cases/user/*/**/*.js
|
||||||
|
tests/cases/user/*/**/*.js.map
|
||||||
|
tests/cases/user/*/**/*.d.ts
|
||||||
|
!tests/cases/user/zone.js/
|
||||||
|
!tests/cases/user/bignumber.js/
|
||||||
|
!tests/cases/user/discord.js/
|
||||||
|
tests/baselines/reference/dt
|
||||||
|
.failed-tests
|
||||||
|
TEST-results.xml
|
||||||
|
package-lock.json
|
||||||
|
tests/cases/user/npm/npm
|
||||||
|
tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter
|
||||||
|
tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter
|
||||||
|
tests/cases/user/TypeScript-React-Native-Starter/TypeScript-React-Native-Starter
|
||||||
|
tests/cases/user/TypeScript-Vue-Starter/TypeScript-Vue-Starter
|
||||||
|
tests/cases/user/TypeScript-WeChat-Starter/TypeScript-WeChat-Starter
|
||||||
|
tests/cases/user/create-react-app/create-react-app
|
||||||
|
tests/cases/user/fp-ts/fp-ts
|
||||||
|
tests/cases/user/webpack/webpack
|
||||||
|
tests/cases/user/puppeteer/puppeteer
|
||||||
|
tests/cases/user/axios-src/axios-src
|
||||||
|
tests/cases/user/prettier/prettier
|
||||||
|
.eslintcache
|
||||||
17
server/package.json
Normal file
17
server/package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "npm install && tsc -p ./",
|
||||||
|
"start": "node dist/app.js",
|
||||||
|
"launch": "npm-run-all build start"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/node16": "^1.0.2",
|
||||||
|
"@types/node": "^16.7.1",
|
||||||
|
"@types/ws": "^7.4.7",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "^8.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
102
server/ts/app.ts
Normal file
102
server/ts/app.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import WebSocket from 'ws';
|
||||||
|
import Coordinates from './coordinates';
|
||||||
|
|
||||||
|
const wss = new WebSocket.Server({ port: 8081 });
|
||||||
|
const receivers: WebSocket[] = [];
|
||||||
|
let sender: WebSocket | null = null;
|
||||||
|
|
||||||
|
const paths = [] as Coordinates[][];
|
||||||
|
|
||||||
|
let currentPath = [] as Coordinates[];
|
||||||
|
|
||||||
|
wss.on('connection', ws => {
|
||||||
|
console.log('CONNECTION incoming');
|
||||||
|
ws.once('message', message => {
|
||||||
|
const str = message.toString();
|
||||||
|
switch (str) {
|
||||||
|
case 'SENDER':
|
||||||
|
setSender(ws);
|
||||||
|
break;
|
||||||
|
case 'RECEIVER':
|
||||||
|
addReceiver(ws)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log('Unknown register code: ' + str);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('READY');
|
||||||
|
|
||||||
|
function addReceiver(ws: WebSocket) {
|
||||||
|
console.log('RECEIVER registered');
|
||||||
|
for (const path of paths) {
|
||||||
|
ws.send('START');
|
||||||
|
path.forEach(c => ws.send(JSON.stringify(c)));
|
||||||
|
ws.send('STOP');
|
||||||
|
}
|
||||||
|
|
||||||
|
receivers.push(ws);
|
||||||
|
|
||||||
|
ws.onclose = () => {
|
||||||
|
// Remove receiver when connection closes
|
||||||
|
for (const [i, recv] of receivers.entries()) {
|
||||||
|
if (ws === recv) {
|
||||||
|
receivers.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSender(ws: WebSocket) {
|
||||||
|
console.log('SENDER is being registered')
|
||||||
|
if (sender) {
|
||||||
|
console.log('Removing current sender...')
|
||||||
|
sender.close();
|
||||||
|
clearPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
sender = ws;
|
||||||
|
ws.on('message', msg => processMessage(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
function processMessage(message: WebSocket.Data) {
|
||||||
|
const text = message.toString();
|
||||||
|
switch (text) {
|
||||||
|
case 'START':
|
||||||
|
startPath();
|
||||||
|
break;
|
||||||
|
case 'STOP':
|
||||||
|
finishPath();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
processCoordinates(text);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function processCoordinates(text: string) {
|
||||||
|
console.log('COORDINATES received: ' + text);
|
||||||
|
|
||||||
|
console.log('SENDING to ' + receivers.length, 'clients');
|
||||||
|
receivers.forEach(r => r.send(text));
|
||||||
|
|
||||||
|
currentPath.push(JSON.parse(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPath() {
|
||||||
|
currentPath = [];
|
||||||
|
receivers.forEach(r => r.send('START'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishPath() {
|
||||||
|
paths.push(currentPath);
|
||||||
|
receivers.forEach(r => r.send("STOP"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearPaths() {
|
||||||
|
paths.splice(0, paths.length);
|
||||||
|
receivers.forEach(r => r.send('CLEAR'));
|
||||||
|
console.log('Paths have been cleared');
|
||||||
|
}
|
||||||
9
server/ts/coordinates.ts
Normal file
9
server/ts/coordinates.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export default class Coordinates {
|
||||||
|
public x: number;
|
||||||
|
public y: number;
|
||||||
|
|
||||||
|
constructor(x: number, y: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
server/tsconfig.json
Normal file
10
server/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node16/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"include": ["ts/**/*"],
|
||||||
|
"exclude": ["node_modules", "**/*.spec.ts"]
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import CanvasUtil from './canvas-util/canvasUtil.js';
|
import CanvasUtil from './canvas-util/canvasUtil.js';
|
||||||
import Coordinates from './canvas-util/coordinates.js';
|
import Coordinates from './canvas-util/coordinates.js';
|
||||||
|
import { getWebsocketUrl } from "./websocket/websocket.js";
|
||||||
|
|
||||||
class CanvasReceiverController {
|
class CanvasReceiverController {
|
||||||
private canvas!: HTMLCanvasElement;
|
|
||||||
private canvasDiv: HTMLDivElement;
|
private canvasDiv: HTMLDivElement;
|
||||||
private canvasUtil: CanvasUtil;
|
private canvasUtil: CanvasUtil;
|
||||||
private ws: WebSocket;
|
private ws: WebSocket;
|
||||||
@@ -23,7 +23,7 @@ class CanvasReceiverController {
|
|||||||
alert('Canvas API unavailable, drawing will not work!');
|
alert('Canvas API unavailable, drawing will not work!');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ws = new WebSocket('ws://localhost:8081', 'json');
|
this.ws = new WebSocket(getWebsocketUrl(), 'json');
|
||||||
|
|
||||||
this.ws.onmessage = message => this.processMessage(message);
|
this.ws.onmessage = message => this.processMessage(message);
|
||||||
this.ws.onopen = () => this.ws.send('RECEIVER');
|
this.ws.onopen = () => this.ws.send('RECEIVER');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import CanvasUtil from "./canvas-util/canvasUtil.js";
|
import CanvasUtil from "./canvas-util/canvasUtil.js";
|
||||||
import Coordinates from "./canvas-util/coordinates.js";
|
import Coordinates from "./canvas-util/coordinates.js";
|
||||||
|
import { getWebsocketUrl } from "./websocket/websocket.js";
|
||||||
|
|
||||||
class CanvasSenderController {
|
class CanvasSenderController {
|
||||||
private canvasUtil: CanvasUtil;
|
private canvasUtil: CanvasUtil;
|
||||||
@@ -28,7 +29,7 @@ class CanvasSenderController {
|
|||||||
canvas.addEventListener('touchmove', e => this.onMouseMove(e.touches[0]));
|
canvas.addEventListener('touchmove', e => this.onMouseMove(e.touches[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ws = new WebSocket('ws://localhost:8081', 'json');
|
this.ws = new WebSocket(getWebsocketUrl(), 'json');
|
||||||
this.ws.onopen = () => this.ws.send('SENDER');
|
this.ws.onopen = () => this.ws.send('SENDER');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
ts/websocket/websocket.ts
Normal file
9
ts/websocket/websocket.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export function getWebsocketUrl(): string {
|
||||||
|
let scheme = 'ws';
|
||||||
|
|
||||||
|
if (document.location.protocol === 'https:') {
|
||||||
|
scheme += 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
return scheme + '://' + document.location.hostname + ':8081';
|
||||||
|
}
|
||||||
@@ -8,4 +8,4 @@
|
|||||||
"extends": "@tsconfig/recommended/tsconfig.json",
|
"extends": "@tsconfig/recommended/tsconfig.json",
|
||||||
"include": ["ts/**/*"],
|
"include": ["ts/**/*"],
|
||||||
"exclude": ["node_modules", "**/*.spec.ts"]
|
"exclude": ["node_modules", "**/*.spec.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user