victor HF staff commited on
Commit
3f5af45
1 Parent(s): 41372f5
Files changed (5) hide show
  1. .DS_Store +0 -0
  2. app.js +30 -0
  3. index.html +79 -22
  4. multiplayer.js +141 -0
  5. style.css +0 -28
.DS_Store ADDED
Binary file (6.15 kB). View file
app.js ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const board = document.getElementById("board");
2
+ const parent = board.parentElement;
3
+
4
+ let transforms = { x: 0, y: 0, scale: 1 };
5
+
6
+ const panzoom = Panzoom(board, {
7
+ startScale: transforms.scale,
8
+ startX: transforms.x,
9
+ startY: transforms.y,
10
+ canvas: true,
11
+ });
12
+
13
+ parent.addEventListener("wheel", panzoom.zoomWithWheel);
14
+ // No function bind needed
15
+
16
+ // This demo binds to shift + wheel
17
+ parent.addEventListener("wheel", function (event) {
18
+ if (!event.shiftKey) return;
19
+ panzoom.zoomWithWheel(event);
20
+ });
21
+
22
+ board.addEventListener("panzoomchange", (event) => {
23
+ transforms = event.detail; // => { x: 0, y: 0, scale: 1 }
24
+ });
25
+
26
+ setTimeout(() => {
27
+ html2canvas(board, { useCORS: true }).then((canvas) => {
28
+ document.body.appendChild(canvas);
29
+ });
30
+ }, 4000);
index.html CHANGED
@@ -1,24 +1,81 @@
1
  <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>
13
- You can modify this app directly by editing <i>index.html</i> in the
14
- Files and versions tab.
15
- </p>
16
- <p>
17
- Also don't forget to check the
18
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank"
19
- >Spaces documentation</a
20
- >.
21
- </p>
22
- </div>
23
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  </html>
1
  <!DOCTYPE html>
2
+ <html lang="en">
3
+ <meta charset="UTF-8" />
4
+ <title>Stable Diffusion Multiplayer</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <script src="https://unpkg.com/@panzoom/panzoom@4.5.1/dist/panzoom.min.js"></script>
7
+ <script src="//unpkg.com/alpinejs" defer></script>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
10
+
11
+ <body>
12
+ <!-- UI -->
13
+ <div class="absolute z-10 p-8">
14
+ <h1 class="text-4xl font-bold">Stable Diffusion together</h1>
15
+
16
+ <p class="text-xl text-gray-500">
17
+ Infinite multiplayer canvas for Stable Diffusion
18
+ </p>
19
+ </div>
20
+ <div
21
+ class="fixed bottom-16 inset-x-0 flex items-center justify-center z-50 space-x-2"
22
+ >
23
+ <button
24
+ class="text-2xl bg-black text-white py-2 px-5 rounded-2xl hover:bg-gradient-to-br from-red-700 to-blue-700 shadow-2xl font-bold"
25
+ >
26
+ <span class="mr-2">✋</span> Move
27
+ </button>
28
+ <button
29
+ class="text-2xl bg-gray-400 text-gray-200 py-2 px-5 rounded-2xl hover:bg-gradient-to-br from-red-700 to-blue-700 shadow-2xl font-bold"
30
+ >
31
+ <span class="mr-2">✏️</span> Paint
32
+ </button>
33
+ </div>
34
+
35
+ <!-- BOARD -->
36
+ <div class="h-screen w-screen overflow-hidden bg-gray-200 relative">
37
+ <svg
38
+ class="cursor absolute"
39
+ id="cursor-template"
40
+ width="48"
41
+ height="72"
42
+ viewBox="0 0 24 36"
43
+ fill="transparent"
44
+ xmlns="http://www.w3.org/2000/svg"
45
+ >
46
+ <path
47
+ d="M5.65376 12.3673H5.46026L5.31717 12.4976L0.500002 16.8829L0.500002 1.19841L11.7841 12.3673H5.65376Z"
48
+ />
49
+ </svg>
50
+ <div
51
+ id="cursors-container"
52
+ class="w-[2560px] h-[2560px] absolute inset-0 z-20"
53
+ ></div>
54
+ <div id="text" class="absolute bg-black z-30 text-white"></div>
55
+ <div
56
+ id="board"
57
+ class="w-[2560px] bg-white h-[2560px] bg-whtie relative border-gray-300 border"
58
+ >
59
+ <div
60
+ class="top-[256px] left-[256px] h-[512px] w-[512px] absolute border-2 border-indigo-300 bg-gradient-to-t from-indigo-500/50 z-10 to-transparent text-indigo-600 text-2xl flex flex-col justify-between p-4"
61
+ >
62
+ <div class="self-end">User 12</div>
63
+ <div class="self-center font-semibold">Click to paint</div>
64
+ <div>a rabbit eating a red carrot aezaze azeaezaza aze azae</div>
65
+ </div>
66
+
67
+ <div
68
+ class="bg-[url('http://placekitten.com/300/300')] bg-cover top-[512px] left-[512px] h-[512px] w-[512px] absolute border-2 border-red-600 text-red-700 text-2xl flex flex-col justify-between overflow-hidden"
69
+ >
70
+ <!-- <img
71
+ @click="open = true"
72
+ src="http://placekitten.com/300/300"
73
+ class="w-full h-full"
74
+ /> -->
75
+ </div>
76
+ </div>
77
+ </div>
78
+ <script src="./app.js"></script>
79
+ <script src="./multiplayer.js" type="module"></script>
80
+ </body>
81
  </html>
multiplayer.js ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createClient } from "https://cdn.skypack.dev/@liveblocks/client";
2
+
3
+ let PUBLIC_KEY = "pk_test_L8JkCoBm0bYACwp5oOJQsj2n";
4
+ let roomId = "javascript-live-cursors";
5
+
6
+ overrideApiKeyAndRoomId();
7
+
8
+ if (!/^pk_(live|test)/.test(PUBLIC_KEY)) {
9
+ console.warn(
10
+ `Replace "${PUBLIC_KEY}" by your public key from https://liveblocks.io/dashboard/apikeys.\n` +
11
+ `Learn more: https://github.com/liveblocks/liveblocks/tree/main/examples/javascript-live-cursors#getting-started.`
12
+ );
13
+ }
14
+
15
+ const client = createClient({
16
+ publicApiKey: PUBLIC_KEY,
17
+ });
18
+
19
+ const room = client.enter(roomId, { initialPresence: { cursor: null } });
20
+
21
+ const cursorsContainer = document.getElementById("cursors-container");
22
+ const text = document.getElementById("text");
23
+
24
+ room.subscribe("my-presence", (presence) => {
25
+ const cursor = presence?.cursor ?? null;
26
+
27
+ text.innerHTML = cursor
28
+ ? `${cursor.x} × ${cursor.y}`
29
+ : "Move your cursor to broadcast its position to other people in the room.";
30
+ });
31
+
32
+ /**
33
+ * Subscribe to every others presence updates.
34
+ * The callback will be called if you or someone else enters or leaves the room
35
+ * or when someone presence is updated
36
+ */
37
+ room.subscribe("others", (others, event) => {
38
+ switch (event.type) {
39
+ case "reset": {
40
+ // Clear all cursors
41
+ cursorsContainer.innerHTML = "";
42
+ for (const user of others.toArray()) {
43
+ updateCursor(user);
44
+ }
45
+ break;
46
+ }
47
+ case "leave": {
48
+ deleteCursor(event.user);
49
+ break;
50
+ }
51
+ case "enter":
52
+ case "update": {
53
+ updateCursor(event.user);
54
+ break;
55
+ }
56
+ }
57
+ });
58
+
59
+ // get mouse position relative to an element
60
+ function getMousePosition(event, element) {
61
+ const rect = element.getBoundingClientRect();
62
+ return {
63
+ x: event.clientX - rect.left,
64
+ y: event.clientY - rect.top,
65
+ };
66
+ }
67
+
68
+ // Get mouse position related to a transform and a scale
69
+ // function getMousePosition(event, transform, scale) {
70
+ // console.log(scale);
71
+ // const rect = event.target.getBoundingClientRect();
72
+ // const x = (event.offsetX - transform.x) / scale;
73
+ // const y = (event.offsetY - transform.y) / scale;
74
+ // return { x, y };
75
+ // }
76
+
77
+ document.addEventListener("pointermove", (e) => {
78
+ e.preventDefault();
79
+ // const { offsetX, offsetY, clientX, clientY } = e;
80
+ // console.log(offsetX, offsetY, clientX, clientY);
81
+ // console.log(getMousePosition(e, board));
82
+ room.updatePresence({
83
+ cursor: getMousePosition(e, board),
84
+ });
85
+ });
86
+
87
+ document.addEventListener("pointerleave", (e) => {
88
+ room.updatePresence({ cursor: null });
89
+ });
90
+
91
+ const COLORS = ["#DC2626", "#D97706", "#059669", "#7C3AED", "#DB2777"];
92
+
93
+ // Update cursor position based on user presence
94
+ function updateCursor(user) {
95
+ const cursor = getCursorOrCreate(user.connectionId);
96
+
97
+ if (user.presence?.cursor) {
98
+ cursor.style.transform = `translateX(${user.presence.cursor.x}px) translateY(${user.presence.cursor.y}px)`;
99
+ cursor.style.opacity = "1";
100
+ } else {
101
+ cursor.style.opacity = "0";
102
+ }
103
+ }
104
+
105
+ function getCursorOrCreate(connectionId) {
106
+ let cursor = document.getElementById(`cursor-${connectionId}`);
107
+
108
+ if (cursor == null) {
109
+ cursor = document.getElementById("cursor-template").cloneNode(true);
110
+ cursor.id = `cursor-${connectionId}`;
111
+ cursor.style.fill = COLORS[connectionId % COLORS.length];
112
+ cursorsContainer.appendChild(cursor);
113
+ }
114
+
115
+ return cursor;
116
+ }
117
+
118
+ function deleteCursor(user) {
119
+ const cursor = document.getElementById(`cursor-${user.connectionId}`);
120
+ if (cursor) {
121
+ cursor.parentNode.removeChild(cursor);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * This function is used when deploying an example on liveblocks.io.
127
+ * You can ignore it completely if you run the example locally.
128
+ */
129
+ function overrideApiKeyAndRoomId() {
130
+ const query = new URLSearchParams(window?.location?.search);
131
+ const apiKey = query.get("apiKey");
132
+ const roomIdSuffix = query.get("roomId");
133
+
134
+ if (apiKey) {
135
+ PUBLIC_KEY = apiKey;
136
+ }
137
+
138
+ if (roomIdSuffix) {
139
+ roomId = `${roomId}-${roomIdSuffix}`;
140
+ }
141
+ }
style.css DELETED
@@ -1,28 +0,0 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
- }
5
-
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
- }
10
-
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
- }
17
-
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
- }
25
-
26
- .card p:last-child {
27
- margin-bottom: 0;
28
- }