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