├── README.md
├── draw.js
├── draw.php
├── global.php
├── index.js
├── index.php
└── save.py
/README.md:
--------------------------------------------------------------------------------
1 | # Big Canvas Demo
2 | Multiplayer drawing web app (similar to the "Million Dollar Homepage" or Reddit's "Place") in Javascript, Firebase, PHP, Python, and Linux/Apache.
3 |
4 | YouTube video walkthrough: https://youtu.be/t1aXuJkmTg8
5 | Demo: http://techleadpro.com/bigcanvas/
6 |
7 | Looking for more coding tips?
8 | Join ex-Google/ex-Facebook engineers at http://coderpro.com for 100+ coding video explanations for Google, Facebook, and FANG companies.
9 |
10 | # Installation
11 | If you wish to deploy your own instance, here are a few changes necessary:
12 | - Create a Firebase project and make a file `creds.json` for your credentials.
13 | - Bring in the color-picker https://github.com/Simonwep/pickr using `git clone`.
14 | - Comment out the validation check in `draw.php`.
15 | - Create a folder "tmp/" and run `chmod 777 tmp/`
16 |
--------------------------------------------------------------------------------
/draw.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function () {
2 | let canvas = $("#mycanvas");
3 | let ctx = canvas.get(0).getContext("2d");
4 | let canvasWidth = canvas.width();
5 | let canvasHeight = canvas.height();
6 | let PIXELSIZE = canvasWidth / DIMENSION;
7 | let selectedColor = '#222244';
8 | let enabled = true;
9 | let filledPixels = {};
10 |
11 | ctx.strokeStyle = 'rgba(0,0,0,0.1)';
12 | for (let i = 0; i < DIMENSION; ++i) {
13 | x = Math.floor(i * canvasWidth / DIMENSION);
14 | ctx.beginPath();
15 | ctx.moveTo(x, 0);
16 | ctx.lineTo(x, canvasHeight);
17 | ctx.stroke();
18 |
19 | y = Math.floor(i * canvasHeight / DIMENSION);
20 | ctx.beginPath();
21 | ctx.moveTo(0, y);
22 | ctx.lineTo(canvasWidth, y);
23 | ctx.stroke();
24 | }
25 |
26 | // firefox canvas mousedown fix
27 | let firefoxmd = false;
28 |
29 | window.addEventListener('mousedown', e => {
30 | firefoxmd = true;
31 | });
32 |
33 | window.addEventListener('mouseup', e => {
34 | if (firefoxmd === true) {
35 | firefoxmd = false;
36 | }
37 | });
38 |
39 | canvas.on('mousemove touchmove touchstart mousedown', mouseFill);
40 | function mouseFill(e) {
41 | e.preventDefault(); // Disables scrolling for touch events.
42 |
43 | var touchstart = e.type === 'touchstart' || e.type === 'touchmove';
44 | e = touchstart ? e.originalEvent : e;
45 | var rect = $("#mycanvas");
46 | var offsetX = touchstart ? e.targetTouches[0].pageX - rect.offset().left : e.offsetX;
47 | var offsetY = touchstart ? e.targetTouches[0].pageY - rect.offset().top : e.offsetY;
48 |
49 | if (!enabled) return;
50 | if (!firefoxmd) return;
51 | if (e.which != 1 && !touchstart) return;
52 |
53 | pixel = [Math.floor(offsetX / PIXELSIZE), Math.floor(offsetY / PIXELSIZE)];
54 | fillPixel(pixel);
55 | }
56 |
57 | function fillPixel(pixel) {
58 | let key = pixel[0] + ',' + pixel[1];
59 | filledPixels[key] = selectedColor;
60 |
61 | ctx.fillStyle = selectedColor;
62 | ctx.fillRect(pixel[0] * PIXELSIZE, pixel[1] * PIXELSIZE, PIXELSIZE - 1, PIXELSIZE - 1);
63 | }
64 |
65 | const PICKR = Pickr.create({
66 | el: '#picker',
67 | comparison: false,
68 | components: {
69 | opacity: false,
70 | hue: true,
71 | palette: true,
72 | interaction: {
73 | input: true,
74 | }
75 | }
76 | });
77 |
78 | PICKR.on('init', function () {
79 | PICKR.setColor(selectedColor);
80 | });
81 | PICKR.on('show', function () {
82 | enabled = false;
83 | });
84 | PICKR.on('hide', function () {
85 | setTimeout(function () {
86 | enabled = true;
87 | }, 300);
88 | });
89 | PICKR.on('change', function () {
90 | selectedColor = PICKR.getColor().toHEXA().toString();
91 | });
92 |
93 | window.save = function (x, y) {
94 | var data = {};
95 | data['x'] = x;
96 | data['y'] = y;
97 | data['data'] = filledPixels;
98 | $("#saveButton").attr('disabled', 'true');
99 | $("#spinner").html("Saving...");
100 |
101 | $.post('draw.php?submit=1', data, function (rsp) {
102 | $('body').append(rsp);
103 | $("#saveButton").attr('disabled', false);
104 | });
105 | }
106 | window.PICKR = PICKR;
107 | });
108 |
--------------------------------------------------------------------------------
/draw.php:
--------------------------------------------------------------------------------
1 | $data";
17 | return;
18 | }
19 |
20 | # Save to file.
21 | $key = "$x,$y";
22 | $filename = "tmp/" . $key . '-' . rand()%100;
23 | file_put_contents($filename, json_encode($data));
24 |
25 | # Send to firestore.
26 | $result = trim(shell_exec("python save.py '$x' '$y' '$filename' 2>&1"));
27 | if ($result != 1) {
28 | die("Error saving. $result
69 | 70 | | 71 |72 | 73 | | 74 |