PrivateGER
2 years ago
commit
0e287c9d03
6 changed files with 1727 additions and 0 deletions
@ -0,0 +1 @@ |
|||||
|
node_modules/ |
After Width: | Height: | Size: 1.0 KiB |
@ -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> |
@ -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'); |
||||
|
}); |
File diff suppressed because it is too large
@ -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…
Reference in new issue