Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
bgstaal committed Nov 23, 2023
0 parents commit 1d55eff
Show file tree
Hide file tree
Showing 6 changed files with 368 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
build
deploy
screens*
npm-debug.log
*.png
.env
.DS_Store
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#3d scene spanning multiple windows using three.js and localStorage

A simple example showing how to setup a 3d scene across windows on the same origin using three.js and localStorage
153 changes: 153 additions & 0 deletions WindowManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
class WindowManager
{
#windows;
#count;
#id;
#winData;
#winShapeChangeCallback;
#winChangeCallback;

constructor ()
{
let that = this;

addEventListener("storage", (event) =>
{
//console.log(event)
if (event.key == "windows")
{
let newWindows = JSON.parse(event.newValue);
let winChange = that.#didWindowsChange(that.#windows, newWindows);

that.#windows = newWindows;

if (winChange)
{
//console.log("change");
//console.log("windows", that.#windows);
//console.log("newWindows", newWindows);
if (that.#winChangeCallback) that.#winChangeCallback();
}
}

//console.log(that.#windows);
});

window.addEventListener('beforeunload', function (e)
{
let index = that.getWindowIndexFromId(that.#id);

that.#windows.splice(index, 1);
that.updateWindowsLocalStorage();
});
}

#didWindowsChange (pWins, nWins)
{
if (pWins.length != nWins.length)
{
return true;
}
else
{
let c = false;

for (let i = 0; i < pWins.length; i++)
{
if (pWins[i].id != nWins[i].id) c = true;
}

return c;
}
}


init (metaData)
{
this.#windows = JSON.parse(localStorage.getItem("windows")) || [];
this.#count= localStorage.getItem("count") || 0;
this.#count++;

this.#id = this.#count;
let shape = this.getWinShape();
this.#winData = {id: this.#id, shape: shape, metaData: metaData};
this.#windows.push(this.#winData);

localStorage.setItem("count", this.#count);
this.updateWindowsLocalStorage();
}

getWinShape ()
{
let shape = {x: window.screenLeft, y: window.screenTop, w: window.innerWidth, h: window.innerHeight};
return shape;
}

getWindowIndexFromId (id)
{
let index = -1;

for (let i = 0; i < this.#windows.length; i++)
{
if (this.#windows[i].id == id) index = i;
}

return index;
}

updateWindowsLocalStorage ()
{
localStorage.setItem("windows", JSON.stringify(this.#windows));
}

update ()
{
//console.log(step);
let winShape = this.getWinShape();

//console.log(winShape.x, winShape.y);

if (winShape.x != this.#winData.shape.x ||
winShape.y != this.#winData.shape.y ||
winShape.w != this.#winData.shape.w ||
winShape.h != this.#winData.shape.h)
{

this.#winData.shape = winShape;

let index = this.getWindowIndexFromId(this.#id);
this.#windows[index].shape = winShape;

//console.log(windows);
if (this.#winShapeChangeCallback) this.#winShapeChangeCallback();
this.updateWindowsLocalStorage();
}
}

setWinShapeChangeCallback (callback)
{
this.#winShapeChangeCallback = callback;
}

setWinChangeCallback (callback)
{
this.#winChangeCallback = callback;
}

getWindows ()
{
return this.#windows;
}

getThisWindowData ()
{
return this.#winData;
}

getThisWindowID ()
{
return this.#id;
}
}

export default WindowManager;
19 changes: 19 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<html>
<head>
<title>3d example using three.js and multiple windows</title>
<script type="text/javascript" src="three.r124.min.js"></script>
<style type="text/css">

*
{
margin: 0;
padding: 0;
}

</style>
</head>
<body>

<script type="module" src="main.js"></script>
</body>
</html>
183 changes: 183 additions & 0 deletions main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import WindowManager from './WindowManager.js'

const t = THREE;
let camera, scene, renderer, world;
let near, far;
let pixR = window.devicePixelRatio ? window.devicePixelRatio : 1;
let cubes = [];
let sceneOffsetTarget = {x: 0, y: 0};
let sceneOffset = {x: 0, y: 0};

let today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
today.setMilliseconds(0);
today = today.getTime();

let internalTime = getTime();
let windowManager;
let initialized = false;


// this code is essential to circumvent that some browser preload the content of some pages before you actually hit the url
document.addEventListener("visibilitychange", () =>
{
if (document.visibilityState != 'hidden' && !initialized)
{
init();
}
});

window.onload = () => {
if (document.visibilityState != 'hidden')
{
init();
}
};

function init ()
{
// add a short timeout because window.offsetX reports wrong values before a short period
setTimeout(() => {
setupScene();
setupWindowManager();
resize();
updateWindowShape(false);
render();
window.addEventListener('resize', resize);
}, 500)
}

// get time in seconds since beginning of the day (so that all windows use the same time)
function getTime ()
{
return (new Date().getTime() - today) / 1000.0;
}


function setupWindowManager ()
{
windowManager = new WindowManager();
windowManager.setWinShapeChangeCallback(updateWindowShape);
windowManager.setWinChangeCallback(windowsUpdated);

// here you can add your custom metadata to each windows instance
let metaData = {foo: "bar"};

// this will init the windowmanager and add this window to the centralised pool of windows
windowManager.init(metaData);

// call update windows initially (it will later be called by the win change callback)
windowsUpdated();
}

function windowsUpdated ()
{
let wins = windowManager.getWindows();

updateNumberOfCubes();
}

function updateNumberOfCubes ()
{
let wins = windowManager.getWindows();

// remove all cubes
cubes.forEach((c) => {
world.remove(c);
})

cubes = [];

// add new cubes
for (let i = 0; i < wins.length; i++)
{
let win = wins[i];

let c = new t.Color();
c.setHSL(i * .1, 1.0, .5);

let s = 100 + i * 50;
let cube = new t.Mesh(new t.BoxGeometry(s, s, s), new t.MeshBasicMaterial({color: c , wireframe: true}));
cube.position.x = win.shape.x + (win.shape.w * .5);
cube.position.y = win.shape.y + (win.shape.h * .5);

world.add(cube);
cubes.push(cube);
}
}

function updateWindowShape (easing = true)
{
sceneOffsetTarget = {x: -window.screenX, y: -window.screenY};
if (!easing) sceneOffset = sceneOffsetTarget;
}

function setupScene ()
{
camera = new t.OrthographicCamera(0, 0, window.innerWidth, window.innerHeight, -10000, 10000);

camera.position.z = 2.5;
near = camera.position.z - .5;
far = camera.position.z + 0.5;

scene = new t.Scene();
scene.background = new t.Color(0.0);
scene.add( camera );

renderer = new t.WebGLRenderer({antialias: true, depthBuffer: true});
renderer.setPixelRatio(pixR);

world = new t.Object3D();
scene.add(world);

renderer.domElement.setAttribute("id", "scene");
document.body.appendChild( renderer.domElement );
}


function render ()
{
let t = getTime();

windowManager.update();

let falloff = .05;
sceneOffset.x = sceneOffset.x + ((sceneOffsetTarget.x - sceneOffset.x) * falloff);
sceneOffset.y = sceneOffset.y + ((sceneOffsetTarget.y - sceneOffset.y) * falloff);

world.position.x = sceneOffset.x;
world.position.y = sceneOffset.y;

let wins = windowManager.getWindows();


for (let i = 0; i < cubes.length; i++)
{
let cube = cubes[i];
let win = wins[i];
let _t = t;// + i * .2;

let posTarget = {x: win.shape.x + (win.shape.w * .5), y: win.shape.y + (win.shape.h * .5)}

cube.position.x = cube.position.x + (posTarget.x - cube.position.x) * falloff;
cube.position.y = cube.position.y + (posTarget.y - cube.position.y) * falloff;
cube.rotation.x = _t * .5;
cube.rotation.y = _t * .3;
};

renderer.render(scene, camera);
requestAnimationFrame(render);
}


function resize ()
{
let width = window.innerWidth;
let height = window.innerHeight

camera = new t.OrthographicCamera(0, width, 0, height, -10000, 10000);
camera.updateProjectionMatrix();
renderer.setSize( width, height );
}
2 changes: 2 additions & 0 deletions three.r124.min.js

Large diffs are not rendered by default.

0 comments on commit 1d55eff

Please sign in to comment.