diff options
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | src/App.css | 89 | ||||
-rw-r--r-- | src/App.js | 61 | ||||
-rw-r--r-- | src/ColorIndicator.js | 11 | ||||
-rw-r--r-- | src/ConnectedIndicator.js | 3 | ||||
-rw-r--r-- | src/PaintArea.js | 17 | ||||
-rw-r--r-- | src/Palette.js | 34 | ||||
-rw-r--r-- | src/Toolkit.js | 20 |
8 files changed, 181 insertions, 61 deletions
@@ -4,3 +4,10 @@ Allows multiple people to paint at once & updates in real time. To run: `curl "https://raw.githubusercontent.com/MFAshby/unicornpaint/master/download_and_run.sh" | sh` Open your browser to http://<your raspberry IP address>:3001/ + +Alternatively, run with docker: +``` +docker pull mfashby/unicornpaint +docker run --device /dev/spidev0.0:/dev/spidev0.0 --publish 3001:3001 mfashby/unicornpaint +``` +Or use the example docker-compose.yml file
\ No newline at end of file diff --git a/src/App.css b/src/App.css index c436008..c9a3616 100644 --- a/src/App.css +++ b/src/App.css @@ -1,38 +1,81 @@ -.toolkit { - float: right; - background: lightblue; +body { + background: black; + color: aliceblue; + padding-left: 50px; + padding-right: 50px; } -.toolkititem { - padding: 5px; +.container { + display: grid; + grid-template-columns: 1fr 1fr; + + grid-template-areas: + "header header" + "paint liveFeed"; + grid-gap: 5px; + +} + +@media (max-width: 760px) { + .container { + display: grid; + grid-template-columns: 1fr; + + grid-template-areas: + "header" + "paint" + "liveFeed"; + } +} + +.header { + grid-area: header; } -.toolkititem.selected { - background: lightcoral; +.paint { + grid-area: paint; } -/* .paintarea { +.liveFeed { + grid-area: liveFeed; +} + +.paintContainer { + display: grid; + grid-template-columns: 1fr 1fr 1frpx; -} */ + grid-template-areas: + "paint paint tools" + "paintCol palette tools"; +} + +.paint { + grid-area: paint; +} -.paintareacell { - width: 20px; - height: 20px; - border: 1px solid grey; +.tools { + grid-area: tools; + display: flex; + flex-direction: column; + width: 45px; } -.paletteitem { - width: 30px; - height: 30px; - border: 3px solid black; - display: inline-block; +.palette { + grid-area: palette; + display: flex; + flex-direction: row; } -.paletteitem.selected { - border: 3px solid red; +.paintCol { + grid-area: paintCol; } -.colorIndicator { - width: 100px; - height: 100px; +.paintAreaCell { + width: "20px"; + height: "20px"; + min-width: "20px"; + min-height: "20px"; + max-width: "20px"; + max-height: "20px"; + border: "1px solid grey"; }
\ No newline at end of file @@ -144,8 +144,9 @@ class App extends Component { _connectWebsocket() { let webSocketProto = window.location.protocol === "https:" ? "wss:" : "ws:" - // let webSocketProto = "wss" - this._websocket = new WebSocket(`${webSocketProto}//${window.location.host}/ws`) + let host = window.location.host + // let host = "shinypi:3001" + this._websocket = new WebSocket(`${webSocketProto}//${host}/ws`) this._websocket.onmessage = this._onMessage this._websocket.onopen = this._onOpen this._websocket.onclose = this._onClose @@ -197,24 +198,44 @@ class App extends Component { render() { return ( - <div className="App"> - <ConnectedIndicator connected={this.state.connected} /> - <Toolkit - tools={tools} - selectedTool={this.state.selectedTool} - onSelectTool={this._selectTool} /> - <PaintArea - data={this.state.pixels} - onTool={this._applyTool} /> - <Palette - selectedColor={this.state.selectedColor} - onSelectColor={(color) => this.setState({ selectedColor: color })} /> - <ColorIndicator color={this.state.selectedColor} /> - {/* Embedded tweet showing live stream */} - <Timeline dataSource={{ - sourceType: 'profile', - screenName: 'UnicornPaint' - }}/> + <div className="container"> + <div className="header"> + <h1>Unicorn Paint!</h1> + <ConnectedIndicator connected={this.state.connected} /> + </div> + <div className="paintContainer"> + <div className="paint"> + <PaintArea + data={this.state.pixels} + onTool={this._applyTool} /> + </div> + <div className="tools"> + <Toolkit + tools={tools} + selectedTool={this.state.selectedTool} + onSelectTool={this._selectTool} /> + </div> + <div className="palette"> + <Palette + selectedColor={this.state.selectedColor} + onSelectColor={(color) => this.setState({ selectedColor: color })} /> + </div> + <div className="paintCol"> + <ColorIndicator color={this.state.selectedColor} /> + </div> + </div> + <div className="liveFeed"> + <Timeline + dataSource={{ + sourceType: 'profile', + screenName: 'UnicornPaint', + }} + options={{ + tweetLimit: 1, + chrome: "noheader nofooter noborders noscrollbar", + width: "300px" + }}/> + </div> <div> { this.state.showingLoad diff --git a/src/ColorIndicator.js b/src/ColorIndicator.js index e84a661..2ae15d1 100644 --- a/src/ColorIndicator.js +++ b/src/ColorIndicator.js @@ -19,12 +19,19 @@ export default class ColorIndicator extends Component { } return <div - className="colorIndicator" style={{ background: `rgb(${r},${g},${b})`, - color: foreground + color: foreground, + ...styles.colorIndicator }}> <span>{colorDesc}</span> </div> } +} + +const styles = { + colorIndicator: { + width: "100px", + height: "100px" + } }
\ No newline at end of file diff --git a/src/ConnectedIndicator.js b/src/ConnectedIndicator.js index d4e1686..3296ef9 100644 --- a/src/ConnectedIndicator.js +++ b/src/ConnectedIndicator.js @@ -5,7 +5,8 @@ export default class ConnectedIndicator extends Component { let connectedText = this.props.connected ? "Connected" : "Not connected" let color = this.props.connected ? "green" : "red" return <div><span style={{ - background:color + background:color, + display: "inline-block" }}>{connectedText}</span></div> } }
\ No newline at end of file diff --git a/src/PaintArea.js b/src/PaintArea.js index 3b64beb..ac7ce96 100644 --- a/src/PaintArea.js +++ b/src/PaintArea.js @@ -57,9 +57,9 @@ export default class PaintArea extends Component { cells.push(<td onMouseMove={() => this.handleMouseMove(ix, iy)} onClick={() => this.props.onTool(ix, iy)} - className="paintareacell" style={{ - background: `rgb(${r},${g},${b})` + background: `rgb(${r},${g},${b})`, + ...styles.paintAreaCell }} key={(ix * 100000) + iy}/>) } @@ -68,7 +68,6 @@ export default class PaintArea extends Component { return ( <table - className="paintarea" draggable={false}> <tbody> {rows} @@ -76,4 +75,16 @@ export default class PaintArea extends Component { </table> ) } +} + +const styles = { + paintAreaCell: { + width: "20px", + height: "20px", + minWidth: "20px", + minHeight: "20px", + maxWidth: "20px", + maxHeight: "20px", + border: "1px solid grey", + } }
\ No newline at end of file diff --git a/src/Palette.js b/src/Palette.js index ad0ad46..fd4776f 100644 --- a/src/Palette.js +++ b/src/Palette.js @@ -24,21 +24,39 @@ const colorPalette = [ export default class Palette extends Component { render() { let paletteListItems = colorPalette.map((item) => { - var className = "paletteitem" let { r, g, b } = rgb(item) let selected = rgb(this.props.selectedColor) - if (r === selected.r && g === selected.g && b === selected.b) { - className += " selected" - } + let isSelected = r === selected.r && g === selected.g && b === selected.b + + let style = Object.assign({}, + {background: `rgb(${r},${g},${b})`}, + styles.paletteItem, + isSelected && styles.selected + ) + return <div onClick={() => this.props.onSelectColor([r, g, b])} - style={{background: `rgb(${r},${g},${b})`}} - className={className} + style={style} key={r*10000+g*1000+b}/> }) + let list1 = paletteListItems.slice(0, paletteListItems.length / 2) + let list2 = paletteListItems.slice(paletteListItems.length / 2) return ( - <div className="palette"> - {paletteListItems} + <div> + {list1}<br/> + {list2} </div>) } + } + + const styles = { + paletteItem: { + width: "30px", + height: "30px", + border: "3px solid black", + display: "inline-block", + }, + selected: { + border: "3px solid red" + } }
\ No newline at end of file diff --git a/src/Toolkit.js b/src/Toolkit.js index 35d8759..b12f418 100644 --- a/src/Toolkit.js +++ b/src/Toolkit.js @@ -4,11 +4,11 @@ import './fontawesome-all.min.js' class ToolKitItem extends Component { render() { let iconClass = this.props.tool.icon + " fa-2x" - var divClass = "toolkititem" + let style = styles.toolkititem if (this.props.selected) { - divClass += " selected" + style = Object.assign({}, style, styles.selected) } - return <div className={divClass} + return <div style={style} onClick={() => this.props.onSelectTool(this.props.tool)}> <i className={iconClass}></i> </div> @@ -24,8 +24,20 @@ export default class Toolkit extends Component { selected={tool === this.props.selectedTool} onSelectTool={this.props.onSelectTool}/> }) - return <div className="toolkit"> + return <div style={styles.toolkit}> {toolComponents} </div> } } + +const styles = { + toolkit: { + background: "grey" + }, + toolkititem: { + padding: "5px", + }, + selected: { + background: "lightcoral" + } +}
\ No newline at end of file |