315 lines
12 KiB
JavaScript
315 lines
12 KiB
JavaScript
const BeansAPI = Object.freeze((() => {
|
|
const pointerNuke = pointer => {
|
|
if(pointer.element instanceof Element)
|
|
document.body.removeChild(pointer.element);
|
|
|
|
pointer.element = undefined;
|
|
pointer.elementHand = undefined;
|
|
pointer.elementGlow = undefined;
|
|
pointer.elementNumber = undefined;
|
|
};
|
|
|
|
const pointerDraw = pointer => {
|
|
pointer.fadeOutStart = false;
|
|
|
|
if(!pointer.visible) {
|
|
pointerNuke(pointer);
|
|
return;
|
|
}
|
|
|
|
if(!pointer.element) {
|
|
pointer.element = document.createElement('div');
|
|
pointer.element.style.width = '64px';
|
|
pointer.element.style.height = '64px';
|
|
pointer.element.style.pointerEvents = 'none';
|
|
pointer.element.style.borderWidth = '0';
|
|
pointer.element.style.boxStyle = 'border-box';
|
|
pointer.element.style.position = 'fixed';
|
|
pointer.element.style.zIndex = pointer.self ? '2147483647' : (10000000 + (pointer.connId ?? 0)).toString();
|
|
pointer.element.style.transform = 'scale(.5) rotate(-20deg)';
|
|
pointer.element.style.transformOrigin = '-18px 10px';
|
|
|
|
pointer.elementNumber = document.createElement('div');
|
|
pointer.elementNumber.style.position = 'absolute';
|
|
pointer.elementNumber.style.top = '28px';
|
|
pointer.elementNumber.style.left = '0';
|
|
pointer.elementNumber.style.zIndex = '30';
|
|
pointer.elementNumber.style.color = 'var(--pointer-colour, #00f)';
|
|
pointer.elementNumber.style.fontFamily = 'sans-serif';
|
|
pointer.elementNumber.style.fontSize = '24px';
|
|
pointer.elementNumber.style.fontWeight = '700';
|
|
pointer.elementNumber.style.letterSpacing = '-1px';
|
|
pointer.elementNumber.style.textAlign = 'center';
|
|
pointer.elementNumber.style.width = pointer.element.style.width;
|
|
pointer.elementNumber.style.height = pointer.element.style.height;
|
|
pointer.element.appendChild(pointer.elementNumber);
|
|
|
|
pointer.elementGlow = document.createElement('div');
|
|
pointer.elementGlow.style.position = 'absolute';
|
|
pointer.elementGlow.style.top = '0';
|
|
pointer.elementGlow.style.left = '0';
|
|
pointer.elementGlow.style.zIndex = '20';
|
|
pointer.elementGlow.style.backgroundColor = 'var(--pointer-colour, #00f)';
|
|
pointer.elementGlow.style.width = pointer.element.style.width;
|
|
pointer.elementGlow.style.height = pointer.element.style.height;
|
|
pointer.elementGlow.style.maskImage = 'url(//static.flash.moe/images/pointer.png)';
|
|
pointer.elementGlow.style.webkitMaskImage = 'url(//static.flash.moe/images/pointer.png)';
|
|
pointer.elementGlow.style.maskType = 'alpha';
|
|
pointer.elementGlow.style.webkitMaskType = 'alpha';
|
|
pointer.element.appendChild(pointer.elementGlow);
|
|
|
|
pointer.elementHand = document.createElement('div');
|
|
pointer.elementHand.style.position = 'absolute';
|
|
pointer.elementHand.style.top = '0';
|
|
pointer.elementHand.style.left = '0';
|
|
pointer.elementHand.style.zIndex = '10';
|
|
pointer.elementHand.style.width = pointer.element.style.width;
|
|
pointer.elementHand.style.height = pointer.element.style.height;
|
|
pointer.elementHand.style.backgroundColor = '#fff';
|
|
pointer.elementHand.style.maskImage = 'url(//static.flash.moe/images/pointer.png)';
|
|
pointer.elementHand.style.webkitMaskImage = 'url(//static.flash.moe/images/pointer.png)';
|
|
pointer.elementHand.style.maskType = 'alpha';
|
|
pointer.elementHand.style.webkitMaskType = 'alpha';
|
|
pointer.element.appendChild(pointer.elementHand);
|
|
|
|
document.body.appendChild(pointer.element);
|
|
}
|
|
|
|
const opacity = pointer.self ? .5 : 1;
|
|
pointer.elementNumber.textContent = pointer.userId ?? pointer.connId ?? 0;
|
|
pointer.element.style.setProperty('--pointer-colour', '#' + (pointer.colour ?? 0).toString(16).padStart(6, '0'));
|
|
pointer.element.style.opacity = opacity.toString();
|
|
pointer.element.style.left = Math.round(window.innerWidth * ((pointer.xPos ?? 0) / 0xFFFF)).toString() + 'px';
|
|
pointer.element.style.top = Math.round(window.innerHeight * ((pointer.yPos ?? 0) / 0xFFFF)).toString() + 'px';
|
|
|
|
if(pointer.click) {
|
|
pointer.elementHand.style.maskPosition = '-64px 0';
|
|
pointer.elementHand.style.webkitMaskPosition = '-64px 0';
|
|
pointer.elementGlow.style.maskPosition = '-64px -64px';
|
|
pointer.elementGlow.style.webkitMaskPosition = '-64px -64px';
|
|
} else {
|
|
pointer.elementHand.style.maskPosition = '0 0';
|
|
pointer.elementHand.style.webkitMaskPosition = '0 0';
|
|
pointer.elementGlow.style.maskPosition = '0 -64px';
|
|
pointer.elementGlow.style.webkitMaskPosition = '0 -64px';
|
|
}
|
|
|
|
pointer.fadeOutStart = undefined;
|
|
const fadeOut = time => {
|
|
if(pointer.fadeOutStart === false || !pointer.element) {
|
|
pointer.fadeOutStart = undefined;
|
|
return;
|
|
}
|
|
|
|
if(pointer.fadeOutStart === undefined)
|
|
pointer.fadeOutStart = time;
|
|
|
|
const elapsed = time - pointer.fadeOutStart;
|
|
const completion = Math.min(1, Math.max(0, elapsed / 30000));
|
|
const eased = completion === 0 ? 0 : Math.pow(2, 10 * completion - 10);
|
|
|
|
pointer.element.style.opacity = (opacity - (opacity * eased)).toString();
|
|
|
|
if(completion < 1)
|
|
requestAnimationFrame(fadeOut);
|
|
else
|
|
pointerNuke(pointer);
|
|
};
|
|
requestAnimationFrame(fadeOut);
|
|
};
|
|
|
|
const pointers = new Map;
|
|
let selfPointer;
|
|
let sock;
|
|
|
|
const pub = {};
|
|
let connected = false;
|
|
const runWhenConnected = [];
|
|
|
|
const connect = () => {
|
|
sock = new WebSocket(`${location.protocol.replace('http', 'ws')}//beans.flashii.net`);
|
|
sock.binaryType = 'arraybuffer';
|
|
sock.addEventListener('close', ev => {
|
|
connected = false;
|
|
sock = undefined;
|
|
setTimeout(() => { connect(); }, 1000);
|
|
});
|
|
sock.addEventListener('message', ev => {
|
|
if(!(ev.data instanceof ArrayBuffer))
|
|
return;
|
|
|
|
const view = new DataView(ev.data);
|
|
let offset = 0;
|
|
|
|
let connId = offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
connId <<= 8;
|
|
connId |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
connId <<= 8;
|
|
connId |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
connId <<= 8;
|
|
connId |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
|
|
const isSelf = !!(connId & 0x80000000);
|
|
connId &= 0x7FFFFFFF;
|
|
|
|
let pointer;
|
|
if(pointers.has(connId))
|
|
pointer = pointers.get(connId);
|
|
else
|
|
pointers.set(connId, pointer = { connId, visible: false, xPos: -1000, yPos: -1000, });
|
|
|
|
if(isSelf) {
|
|
if(selfPointer)
|
|
selfPointer.self = false;
|
|
|
|
selfPointer = pointer;
|
|
selfPointer.self = true;
|
|
|
|
connected = true;
|
|
for(const callback of runWhenConnected)
|
|
callback(pub);
|
|
}
|
|
|
|
const oflags = offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.visible = !!(oflags & 0x10);
|
|
pointer.click = !!(oflags & 0x20);
|
|
|
|
if(oflags & 0x01) {
|
|
pointer.userId = offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.userId <<= 8;
|
|
pointer.userId |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.userId <<= 8;
|
|
pointer.userId |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.userId <<= 8;
|
|
pointer.userId |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
}
|
|
|
|
if(oflags & 0x02) {
|
|
pointer.xPos = offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.xPos <<= 8;
|
|
pointer.xPos |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
}
|
|
|
|
if(oflags & 0x04) {
|
|
pointer.yPos = offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.yPos <<= 8;
|
|
pointer.yPos |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
}
|
|
|
|
if(oflags & 0x08) {
|
|
pointer.colour = offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.colour <<= 8;
|
|
pointer.colour |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
pointer.colour <<= 8;
|
|
pointer.colour |= offset < view.byteLength ? view.getUint8(offset++) : 0;
|
|
}
|
|
|
|
pointerDraw(pointer);
|
|
});
|
|
};
|
|
|
|
const pointerUpdate = ev => {
|
|
if(!connected)
|
|
return;
|
|
|
|
let iflags = 0;
|
|
let oflags = 0;
|
|
const buffer = [];
|
|
const xPos = Math.round((ev.clientX / window.innerWidth) * 0xFFFF);
|
|
const yPos = Math.round((ev.clientY / window.innerHeight) * 0xFFFF);
|
|
const click = !!(ev.buttons & 1);
|
|
|
|
if(!click !== !selfPointer.click) {
|
|
selfPointer.click = click;
|
|
iflags |= 0x20;
|
|
if(click) oflags |= 0x20;
|
|
buffer.push(click ? 0x78 : 0x58);
|
|
}
|
|
|
|
if(xPos !== selfPointer.xPos) {
|
|
selfPointer.xPos = xPos;
|
|
iflags |= 0x02;
|
|
buffer.push((xPos & 0xFF00) >> 8);
|
|
buffer.push(xPos & 0xFF);
|
|
}
|
|
|
|
if(yPos !== selfPointer.yPos) {
|
|
selfPointer.yPos = yPos;
|
|
iflags |= 0x04;
|
|
buffer.push((yPos & 0xFF00) >> 8);
|
|
buffer.push(yPos & 0xFF);
|
|
}
|
|
|
|
if(iflags) {
|
|
if(iflags & 0x01)
|
|
buffer.unshift(oflags);
|
|
buffer.unshift(iflags);
|
|
}
|
|
|
|
if(buffer.length > 0) {
|
|
sock.send(new Uint8Array(buffer));
|
|
pointerDraw(selfPointer);
|
|
}
|
|
};
|
|
|
|
window.addEventListener('pointermove', pointerUpdate);
|
|
window.addEventListener('pointerup', pointerUpdate);
|
|
window.addEventListener('pointerdown', pointerUpdate);
|
|
|
|
connect();
|
|
|
|
pub.runWhenConnected = callback => {
|
|
if(typeof callback !== 'function')
|
|
return;
|
|
|
|
runWhenConnected.push(callback);
|
|
if(connected)
|
|
callback(pub);
|
|
};
|
|
|
|
pub.setUserInfo = (id, colour) => {
|
|
if(!connected)
|
|
return;
|
|
|
|
if(typeof id === 'string')
|
|
id = parseInt(id);
|
|
if(typeof id !== 'number' || !Number.isInteger(id))
|
|
id = undefined;
|
|
|
|
if(typeof colour === 'string')
|
|
colour = colour.startsWith('#') ? parseInt(colour.substring(1), 16) : parseInt(colour);
|
|
if(typeof colour !== 'number' || !Number.isInteger(colour))
|
|
colour = undefined;
|
|
|
|
let iflags = 0x10;
|
|
const buffer = [0x10];
|
|
selfPointer.visible = true;
|
|
|
|
if(id !== undefined && id !== selfPointer.userId) {
|
|
selfPointer.userId = id;
|
|
iflags |= 0x01;
|
|
buffer.push((id & 0xFF000000) >> 24);
|
|
buffer.push((id & 0xFF0000) >> 16);
|
|
buffer.push((id & 0xFF00) >> 8);
|
|
buffer.push(id & 0xFF);
|
|
}
|
|
|
|
if(colour !== undefined && colour !== selfPointer.colour) {
|
|
selfPointer.colour = colour;
|
|
iflags |= 0x08;
|
|
buffer.push((colour & 0xFF0000) >> 16);
|
|
buffer.push((colour & 0xFF00) >> 8);
|
|
buffer.push(colour & 0xFF);
|
|
}
|
|
|
|
if(iflags)
|
|
buffer.unshift(iflags);
|
|
|
|
if(buffer.length > 0) {
|
|
sock.send(new Uint8Array(buffer));
|
|
pointerDraw(selfPointer);
|
|
}
|
|
};
|
|
|
|
return pub;
|
|
})());
|