mirror of
https://github.com/bgstaal/multipleWindow3dScene.git
synced 2026-03-12 17:51:34 +08:00
initial commit
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
node_modules
|
||||
build
|
||||
deploy
|
||||
screens*
|
||||
npm-debug.log
|
||||
*.png
|
||||
.env
|
||||
.DS_Store
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -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
WindowManager.js
Normal file
153
WindowManager.js
Normal file
@@ -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
index.html
Normal file
19
index.html
Normal file
@@ -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
main.js
Normal file
183
main.js
Normal file
@@ -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
three.r124.min.js
vendored
Normal file
2
three.r124.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user