Skip to content

Commit

Permalink
Update painter example
Browse files Browse the repository at this point in the history
  • Loading branch information
jnordberg committed Apr 30, 2017
1 parent 275b451 commit f2a9a24
Show file tree
Hide file tree
Showing 18 changed files with 463 additions and 1,155 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Expand Up @@ -2,6 +2,6 @@ node_modules/
lib3/
lib6/
coverage/
protocol/*
!protocol/*.proto
**/protocol/*
!**/protocol/*.proto
.nyc_output/
17 changes: 9 additions & 8 deletions examples/painter/Makefile
Expand Up @@ -3,21 +3,22 @@ SHELL := /bin/bash
PATH := ./node_modules/.bin:$(PATH)

.PHONY: preview
preview: node_modules proto
node server.js &
wintersmith preview
preview: node_modules protocol/service.d.ts protocol/service.js
wintersmith preview --chdir client

.PHONY: proto
proto: node_modules
pbjs -t static-module -w commonjs service.proto -o contents/protocol.js
pbts -o contents/protocol.d.ts contents/protocol.js
protocol/service.js: node_modules protocol/service.proto
pbjs -t static-module -w commonjs protocol/service.proto -o protocol/service.js

protocol/service.d.ts: node_modules protocol/service.js
pbts -o protocol/service.d.ts protocol/service.js

node_modules:
npm install

.PHONY: clean
clean:
rm -f contents/protocol.js
rm -f protocol/service.js
rm -f protocol/service.d.ts

.PHONY: distclean
distclean: clean
Expand Down
Binary file added examples/painter/canvas.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/painter/client/canvas.js
@@ -0,0 +1 @@
module.exports = window
@@ -1,5 +1,6 @@
{
"browserify": {
"watchify": false,
"extensions": [".js", ".ts"],
"plugins": ["tsify"]
},
Expand Down
File renamed without changes.
250 changes: 250 additions & 0 deletions examples/painter/client/contents/paint.ts
@@ -0,0 +1,250 @@

import {Client} from 'wsrpc'
import {Painter, PaintEvent, StatusEvent} from './../../protocol/service'
import * as zlib from 'browserify-zlib-next'
import * as shared from './../../shared/paint'

interface Position {
x: number
y: number
timestamp: number
}

interface DrawEvent {
pos: Position
lastPos?: Position
color: number
}

const colors = [
0x467588,
0xFFFFFF,
0xFCE5BC,
0xFDCD92,
0xFCAC96,
0xDD8193,
]

function randomColor() {
return colors[Math.floor(colors.length * Math.random())]
}

let now: () => number
if (window.performance) {
now = () => window.performance.now()
} else {
now = () => Date.now()
}

const client = new Client('ws://192.168.1.33:4242', Painter, {
sendTimeout: 5000,
eventTypes: {
paint: PaintEvent,
status: StatusEvent,
}
})

client.on('open', () => {
document.documentElement.classList.add('connected')
})

client.on('close', () => {
document.documentElement.classList.remove('connected')
})

window.addEventListener('DOMContentLoaded', async () => {
const status = document.createElement('div')
status.className = 'status'
status.innerHTML = 'Connecting...'
document.body.appendChild(status)

client.on('event status', (event: StatusEvent) => {
status.innerHTML = `Users: ${ event.users }`
})

client.on('close', () => {
status.innerHTML = 'Disconnected'
})

client.on('error', (error) => {
console.warn('client error', error)
})

let activeColor: number = colors[0]

const colorWells: HTMLSpanElement[] = []
const colorPicker = document.createElement('div')
colorPicker.className = 'picker'
for (const color of colors) {
const well = document.createElement('span')
const cssColor = '#' + color.toString(16)
well.style.backgroundColor = cssColor
well.style.outlineColor = cssColor
well.addEventListener('click', (event) => {
event.preventDefault()
colorWells.forEach((el) => el.classList.remove('active'))
well.classList.add('active')
activeColor = color
console.log(activeColor)
})
colorWells.push(well)
colorPicker.appendChild(well)
}
document.body.appendChild(colorPicker)

colorWells[0].classList.add('active')

const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')

canvas.width = window.innerWidth
canvas.height = window.innerHeight

client.on('event paint', (event: PaintEvent) => {
shared.paint(event, ctx)
})

async function fetchCanvas() {
const request = {
width: Math.min(window.innerWidth, 2048),
height: Math.min(window.innerHeight, 2048),
}
console.log('loading canvas...', request)
const response = await client.service.getCanvas(request)

console.log(`response size: ${ ~~(response.image.length / 1024) }kb`)

const arr = response.image
let buffer = Buffer.from(arr.buffer)
buffer = buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength)

const data = await new Promise<Buffer>((resolve, reject) => {
zlib.gunzip(buffer, (error, result) => {
if (error) { reject(error) } else { resolve(result) }
})
})

console.log(`decompressed: ${ ~~(data.length / 1024) }kb`)

const imageData = ctx.createImageData(request.width, request.height)
imageData.data.set(new Uint8ClampedArray(data.buffer))
ctx.putImageData(imageData, 0, 0)
}

let debounceTimer
window.addEventListener('resize', () => {
if (window.innerWidth <= canvas.width && window.innerHeight <= canvas.height) {
const data = ctx.getImageData(0, 0, canvas.width, canvas.height)
canvas.width = window.innerWidth
canvas.height = window.innerHeight
ctx.putImageData(data, 0, 0)
} else {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
clearTimeout(debounceTimer)
setTimeout(fetchCanvas, 1000)
}
})

await fetchCanvas()

function draw(event: DrawEvent) {
let velocity = 0
if (event.lastPos) {
const dx = event.lastPos.x - event.pos.x
const dy = event.lastPos.y - event.pos.y
const dt = event.pos.timestamp - event.lastPos.timestamp
velocity = Math.sqrt(dx*dx + dy*dy) / dt
}
client.service.paint({
x: event.pos.x,
y: event.pos.y,
color: event.color,
size: 20 + velocity * 20,
}).catch((error: Error) => {
console.warn('error drawing', error.message)
})
}

let mouseDraw: DrawEvent|undefined

canvas.addEventListener('mousedown', (event) => {
mouseDraw = {
pos: {
x: event.x,
y: event.y,
timestamp: event.timeStamp || now(),
},
color: activeColor,
}
draw(mouseDraw)
event.preventDefault()
})

canvas.addEventListener('mousemove', (event) => {
if (mouseDraw) {
mouseDraw.lastPos = mouseDraw.pos
mouseDraw.pos = {
x: event.x,
y: event.y,
timestamp: event.timeStamp || now(),
}
draw(mouseDraw)
}
})

const mouseup = (event) => {
mouseDraw = undefined
}
canvas.addEventListener('mouseup', mouseup)
canvas.addEventListener('mouseleave', mouseup)

let fingerDraw: {[id: number]: DrawEvent} = {}

canvas.addEventListener('touchstart', (event) => {
for (var i = 0; i < event.touches.length; i++) {
const touch = event.touches[i]
fingerDraw[touch.identifier] = {
pos: {
x: touch.screenX,
y: touch.screenY,
timestamp: event.timeStamp || now(),
},
color: activeColor
}
draw(fingerDraw[touch.identifier])
}
event.preventDefault()
})

canvas.addEventListener('touchmove', (event) => {
for (var i = 0; i < event.touches.length; i++) {
const touch = event.touches[i]
const drawEvent = fingerDraw[touch.identifier]
if (drawEvent) {
drawEvent.lastPos = drawEvent.pos
drawEvent.pos = {
x: touch.screenX,
y: touch.screenY,
timestamp: event.timeStamp || now(),
}
draw(drawEvent)
}
}
event.preventDefault()
})

const touchend = (event: TouchEvent) => {
for (var i = 0; i < event.touches.length; i++) {
const touch = event.touches[i]
delete fingerDraw[touch.identifier]
}
event.preventDefault()
}
canvas.addEventListener('touchend', touchend)
canvas.addEventListener('touchcancel', touchend)
})

console.log(' ;-) ')
window['colors'] = colors
window['client'] = client
Expand Up @@ -2,25 +2,27 @@
* {
margin: 0; padding: 0; border: 0; line-height: 1;
user-select: none; -webkit-user-select: none;
box-sizing: border-box;
}

body {
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, sans-serif;
font-size: 14px;
font-weight: 300;
background: #0b1215;
background: black;
color: #f5f5f5;
}

.connected body {
background: #467588;
background: #ffffff;
}

a {
color: #f5f5f5;
}

a, .status {
background: rgba(0, 0, 0, 0.45);
position: fixed;
top: 0;
right: 0;
Expand All @@ -42,4 +44,22 @@ canvas {
position: absolute;
top: 0;
left: 0;
}

.picker {
position: absolute;
bottom: 0;
}

.picker span {
display: block;
width: 5vh;
height: 5vh;
position: relative;
}

.picker span.active {
outline: 0.2em solid white;
border-left: 0.2em solid black;
z-index: 1;
}
File renamed without changes.

0 comments on commit f2a9a24

Please sign in to comment.