Browse Source

First version

master
PrivateGER 2 years ago
commit
0e287c9d03
  1. 1
      .gitignore
  2. 15
      server/cursor.svg
  3. 282
      server/index.html
  4. 49
      server/index.js
  5. 1364
      server/package-lock.json
  6. 16
      server/package.json

1
.gitignore

@ -0,0 +1 @@
node_modules/

15
server/cursor.svg

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="35px" height="35px" viewBox="0 0 35 35" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs/>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="pointer">
<g id="bg" fill="#FFFFFF" opacity="0.00999999978">
<rect x="0" y="0" width="35" height="35"/>
</g>
<path d="M12,24.4219 L12,8.4069 L23.591,20.0259 L16.81,20.0259 L16.399,20.1499 L12,24.4219 Z" id="point-border" fill="#FFFFFF"/>
<path d="M21.0845,25.0962 L17.4795,26.6312 L12.7975,15.5422 L16.4835,13.9892 L21.0845,25.0962 Z" id="stem-border" fill="#FFFFFF"/>
<path d="M19.751,24.4155 L17.907,25.1895 L14.807,17.8155 L16.648,17.0405 L19.751,24.4155 Z" id="stem" fill="#000000"/>
<path d="M13,10.814 L13,22.002 L15.969,19.136 L16.397,18.997 L21.165,18.997 L13,10.814 Z" id="point" fill="#000000"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

282
server/index.html

@ -0,0 +1,282 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>mousetrack PoC</title>
</head>
<body>
<canvas style="pointer-events: none; position: fixed; z-index: 999999999999999; top: 0; left: 0; opacity: 1" id="mousecanvas"></canvas>
<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
<h1>HTML Ipsum Presents</h1>
<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
<h2>Header Level 2</h2>
<ol>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ol>
<blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
<h3>Header Level 3</h3>
<ul>
<li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
<li>Aliquam tincidunt mauris eu risus.</li>
</ul>
<pre><code>
#header h1 a {
display: block;
width: 300px;
height: 80px;
}
</code></pre>
<script src="https://cdn.socket.io/4.5.3/socket.io.min.js"></script>
<script>
const socket = io();
let lastX = -1;
let lastY = -1;
let mousePath = [];
let scanInterval = 200;
let lastScan = -1;
let startTime = Date.now();
let uuid = crypto.randomUUID();
function shouldScan() {
let now = Date.now();
if (now - lastScan > scanInterval) {
lastScan = now;
return true;
}
return false;
}
// scan mouse position every scanInterval milliseconds and add to path array
document.addEventListener('mousemove', onMouseMove);
document.onwheel = onScroll;
function onMouseMove(e) {
if (shouldScan()) {
let x = e.pageX;
let y = e.pageY;
console.log("scanned mouse, coords are: " + x + ", " + y);
// important to remember: these coordinates are relative to the PAGE, not the viewport
mousePath.push({
x: x,
y: y,
distance: Math.sqrt(Math.pow(x - lastX, 2) + Math.pow(y - lastY, 2)),
time: Date.now() - startTime,
path: window.location.origin + window.location.pathname,
uuid: uuid
});
lastX = x;
lastY = y;
}
}
// calculate the x and y from the delta values in the scroll event
function onScroll(e) {
if (lastX === -1 && lastY === -1) {
console.log("no mouse position to calculate from, skipping scroll event");
return;
}
let x = lastX + e.deltaX;
let y = lastY + e.deltaY;
console.log("scanned scroll, coords are: " + x + ", " + y + " (deltaX: " + e.deltaX + ", deltaY: " + e.deltaY + ")");
mousePath.push({
x: x,
y: y,
distance: Math.sqrt(Math.pow(x - lastX, 2) + Math.pow(y - lastY, 2)),
time: Date.now() - startTime,
uuid: uuid
});
lastX = x;
lastY = y;
}
// send path array to server in regular interval
setInterval(() => {
if (mousePath.length > 0) {
socket.emit('movement', mousePath);
mousePath = [];
}
}, 2000);
</script>
<script>
function linearInterpolate(x1, x2, y1, y2, travel_time, elapsed) {
if (elapsed > travel_time) {
return { x: x2, y: y2 };
}
let totalXtoCover = Math.abs(x2 - x1);
let totalYtoCover = Math.abs(y2 - y1);
let interpolatedX, interpolatedY;
if (x1 < x2) {
interpolatedX = x1 + (totalXtoCover / travel_time) * elapsed;
} else {
interpolatedX = x1 - (totalXtoCover / travel_time) * elapsed;
}
if (y1 < y2) {
interpolatedY = y1 + (totalYtoCover / travel_time) * elapsed;
} else {
interpolatedY = y1 - (totalYtoCover / travel_time) * elapsed;
}
return { x: interpolatedX, y: interpolatedY };
}
let userPaths = [];
let canvas = document.getElementById("mousecanvas");
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
socket.on("pathsync", function(paths) {
if (paths == null || paths === undefined) {
return;
}
console.log("received pathsync event, path count: " + Object.keys(paths).length);
userPaths = paths;
});
let cursorImage = new Image();
cursorImage.src = "/cursor.svg";
let ctx = canvas.getContext("2d");
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
let topOffset = canvas.getBoundingClientRect().top;
// iterate over paths, interpolate and build cursor positions for the current time (only one image per cursor)
let cursorPositions = [];
for (let key in userPaths) {
let path = userPaths[key];
let cursorPosition = { x: 0, y: 0, lastPoint: 0, path: path[0].path, uuid: path[0].uuid };
for (let j = 0; j < path.length; j++) {
let point = path[j];
let nextPoint = path[j + 1];
if (nextPoint && nextPoint.time < Date.now() - startTime) {
continue;
}
if (nextPoint) {
// get the elapsed time spent between the two points
let elapsed = Date.now() - startTime - point.time;
let travel_time = nextPoint.time - point.time;
let interpolated = linearInterpolate(point.x, nextPoint.x, point.y, nextPoint.y, travel_time, elapsed);
cursorPosition.x = interpolated.x;
cursorPosition.y = interpolated.y;
cursorPosition.lastPoint = nextPoint.time;
break
} else {
cursorPosition.x = point.x;
cursorPosition.y = point.y;
cursorPosition.lastPoint = point.time;
break
}
}
cursorPositions.push(cursorPosition);
}
// remove cursors with a timestamp older than 1 second
let filteredPaths = cursorPositions.filter(cursor => Date.now() - startTime - cursor.lastPoint < 1000);
// filter out any paths not matching the current path
filteredPaths = filteredPaths.filter(path => path.path === window.location.origin + window.location.pathname);
// filter out own cursor
filteredPaths = filteredPaths.filter(path => path.uuid !== uuid);
filteredPaths.forEach((cursor) => {
ctx.drawImage(cursorImage, cursor.x, cursor.y - topOffset);
});
}
setInterval(render, 1000 / 30);
</script>
</body>
</html>

49
server/index.js

@ -0,0 +1,49 @@
const express = require('express');
const app = express();
const http = require('http');
const cors = require("cors");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server, {
cors: {
origin: true,
}
});
app.use(cors());
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.get("/cursor.svg", (req, res) => {
res.sendFile(__dirname + "/cursor.svg");
});
let paths = {};
io.on('connection', (socket) => {
console.log('a user connected');
let userid = socket.id;
// take stream of movement packet, append as necessary
socket.on('movement', (data) => {
if (paths[userid] === undefined) {
paths[userid] = data;
} else {
paths[userid] = paths[userid].concat(data);
}
console.log(paths);
});
// send old paths to new user
setInterval(() => {
socket.emit('pathsync', paths);
}, 1000);
});
server.listen(3000, () => {
console.log('listening on *:3000');
});

1364
server/package-lock.json

File diff suppressed because it is too large

16
server/package.json

@ -0,0 +1,16 @@
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"socket.io": "^4.5.3"
}
}
Loading…
Cancel
Save