Parcourir la source

Playground: Implement a splitscreen (#27784)

abc013 il y a 1 an
Parent
commit
066b6f7a17
3 fichiers modifiés avec 211 ajouts et 17 suppressions
  1. 44 2
      playground/NodeEditor.js
  2. 91 0
      playground/SplitscreenManager.js
  3. 76 15
      playground/index.html

+ 44 - 2
playground/NodeEditor.js

@@ -4,6 +4,7 @@ import { Canvas, CircleMenu, ButtonInput, StringInput, ContextMenu, Tips, Search
 import { FileEditor } from './editors/FileEditor.js';
 import { exportJSON } from './NodeEditorUtils.js';
 import { init, ClassLib, getNodeEditorClass, getNodeList } from './NodeEditorLib.js';
+import { SplitscreenManager } from './SplitscreenManager.js';
 
 init();
 
@@ -38,6 +39,7 @@ export class NodeEditor extends THREE.EventDispatcher {
 		this.domElement = domElement;
 
 		this._preview = false;
+		this._splitscreen = false;
 
 		this.search = null;
 
@@ -47,6 +49,7 @@ export class NodeEditor extends THREE.EventDispatcher {
 		this.nodesContext = null;
 		this.examplesContext = null;
 
+		this._initSplitview();
 		this._initUpload();
 		this._initTips();
 		this._initMenu();
@@ -55,7 +58,6 @@ export class NodeEditor extends THREE.EventDispatcher {
 		this._initExamplesContext();
 		this._initShortcuts();
 		this._initParams();
-
 	}
 
 	setSize( width, height ) {
@@ -113,6 +115,9 @@ export class NodeEditor extends THREE.EventDispatcher {
 
 		if ( value ) {
 
+			this._wasSplitscreen = this.splitscreen;
+			this.splitscreen = false;
+
 			this.menu.dom.remove();
 			this.canvas.dom.remove();
 			this.search.dom.remove();
@@ -129,6 +134,12 @@ export class NodeEditor extends THREE.EventDispatcher {
 
 			this.previewMenu.dom.remove();
 
+			if (this._wasSplitscreen == true) {
+
+				this.splitscreen = true;
+
+			}
+
 		}
 
 		this._preview = value;
@@ -141,6 +152,22 @@ export class NodeEditor extends THREE.EventDispatcher {
 
 	}
 
+	set splitscreen( value ) {
+
+		if ( this._splitscreen === value ) return;
+
+		this.splitview.setSplitview( value );
+
+		this._splitscreen = value;
+
+	}
+
+	get splitscreen() {
+
+		return this._splitscreen;
+
+	}
+
 	newProject() {
 
 		const canvas = this.canvas;
@@ -180,6 +207,12 @@ export class NodeEditor extends THREE.EventDispatcher {
 
 	}
 
+	_initSplitview() {
+
+		this.splitview = new SplitscreenManager( this );
+
+	}
+
 	_initUpload() {
 
 		const canvas = this.canvas;
@@ -231,6 +264,7 @@ export class NodeEditor extends THREE.EventDispatcher {
 		previewMenu.setAlign( 'top left' );
 
 		const previewButton = new ButtonInput().setIcon( 'ti ti-brand-threejs' ).setToolTip( 'Preview' );
+		const splitscreenButton = new ButtonInput().setIcon( 'ti ti-layout-sidebar-right-expand' ).setToolTip( 'Splitscreen' );
 		const menuButton = new ButtonInput().setIcon( 'ti ti-apps' ).setToolTip( 'Add' );
 		const examplesButton = new ButtonInput().setIcon( 'ti ti-file-symlink' ).setToolTip( 'Examples' );
 		const newButton = new ButtonInput().setIcon( 'ti ti-file' ).setToolTip( 'New' );
@@ -242,6 +276,13 @@ export class NodeEditor extends THREE.EventDispatcher {
 		previewButton.onClick( () => this.preview = true );
 		editorButton.onClick( () => this.preview = false );
 
+		splitscreenButton.onClick( () => {
+
+			this.splitscreen = !this.splitscreen;
+			splitscreenButton.setIcon(this.splitscreen ? 'ti ti-layout-sidebar-right-collapse' : 'ti ti-layout-sidebar-right-expand');
+
+		});
+
 		menuButton.onClick( () => this.nodesContext.open() );
 		examplesButton.onClick( () => this.examplesContext.open() );
 
@@ -289,6 +330,7 @@ export class NodeEditor extends THREE.EventDispatcher {
 		} );
 
 		menu.add( previewButton )
+			.add( splitscreenButton )
 			.add( newButton )
 			.add( examplesButton )
 			.add( openButton )
@@ -297,7 +339,7 @@ export class NodeEditor extends THREE.EventDispatcher {
 
 		previewMenu.add( editorButton );
 
-		this.domElement.append( menu.dom );
+		this.domElement.appendChild(menu.dom);
 
 		this.menu = menu;
 		this.previewMenu = previewMenu;

+ 91 - 0
playground/SplitscreenManager.js

@@ -0,0 +1,91 @@
+export class SplitscreenManager {
+
+    constructor( editor ) {
+
+        this.editor = editor;
+        this.renderer = editor.renderer;
+        this.composer = editor.composer;
+
+        this.gutter = null;
+        this.gutterMoving = false;
+        this.gutterOffset = 0.75;
+
+    }
+
+    setSplitview( value ) {
+
+		const nodeDOM = this.editor.domElement;
+		const rendererContainer = this.renderer.domElement.parentNode;
+
+		if ( value ) {
+
+			this.addGutter( rendererContainer, nodeDOM );
+
+		} else {
+
+			this.removeGutter( rendererContainer, nodeDOM );
+
+		}
+
+    }
+
+    addGutter( rendererContainer, nodeDOM ) {
+
+        rendererContainer.style[ "z-index" ] = 20;
+
+        this.gutter = document.createElement( "f-gutter" );
+
+        nodeDOM.parentNode.appendChild( this.gutter );
+
+        const onGutterMovement = () => {
+
+            const offset = this.gutterOffset;
+
+            this.gutter.style[ "left" ] = 100 * offset + '%';
+            rendererContainer.style[ "left" ] = 100 * offset + '%';
+            rendererContainer.style[ "width" ] = 100 * (1 - offset) + '%';
+            nodeDOM.style[ "width" ] = 100 * offset + '%';
+
+        }
+    
+        this.gutter.addEventListener( 'mousedown', ( event ) => {
+
+            this.gutterMoving = true;
+
+        } );
+
+        document.addEventListener( 'mousemove', ( event ) => {
+
+            if ( this.gutter && this.gutterMoving ) {
+
+                this.gutterOffset = Math.max(0, Math.min(1, event.clientX / window.innerWidth));
+                onGutterMovement();
+
+            }
+
+        } );
+
+        document.addEventListener( 'mouseup', ( event ) => {
+
+            this.gutterMoving = false;
+
+        });
+
+        onGutterMovement();
+
+    }
+
+    removeGutter( rendererContainer, nodeDOM ) {
+
+        rendererContainer.style[ "z-index" ] = 0;
+
+        this.gutter.remove();
+        this.gutter = null;
+    
+        rendererContainer.style[ "left" ] = '0%';
+        rendererContainer.style[ "width" ] = '100%';
+        nodeDOM.style[ "width" ] = '100%';
+
+    }
+
+}

+ 76 - 15
playground/index.html

@@ -17,6 +17,7 @@
 				margin: 0;
 				position: fixed;
 				overscroll-behavior: none;
+				background: #191919ed;
 			}
 
 			.renderer {
@@ -35,6 +36,7 @@
 				width: 100%;
 				box-shadow: inset 0 0 20px 0px #000000;
 				pointer-events: none;
+				overflow: hidden;
 			}
 
 			flow > * {
@@ -49,6 +51,43 @@
 				background: #191919ed;
 			}
 
+			flow f-menu {
+				white-space: nowrap;
+			}
+
+			node-editor {
+				position: relative;
+				width: 100%;
+				height: 100%;
+			}
+
+			f-preview {
+				display: block;
+				position: relative;
+				width: 100%;
+				height: 100%;
+			}
+
+			f-gutter {
+				position: absolute;
+				cursor: ew-resize;
+				height: 100%;
+				top: 0px;
+				width: 2px;
+				background-color: #191919ed;
+				border-style: none solid none solid;
+				border-width: 1px;
+				border-color: #aaaaaa;
+				box-shadow: 0 0 5px 0px #000000;
+				z-index: 30;
+			}
+
+			.panel {
+				position: absolute;
+				overflow: visible;
+				float: left;
+			}
+
 		</style>
 	</head>
 	<body>
@@ -86,6 +125,10 @@
 
 			async function init() {
 
+				const container = document.createElement( 'node-editor' );
+
+				document.body.appendChild( container );
+
 				const urlParams = new URLSearchParams( window.location.search );
 				const backend = urlParams.get( 'backend' );
 
@@ -128,9 +171,13 @@
 				renderer.setAnimationLoop( animate );
 				renderer.toneMapping = THREE.LinearToneMapping;
 				renderer.toneMappingExposure = 1;
-				document.body.appendChild( renderer.domElement );
 
-				renderer.domElement.className = 'renderer';
+				// Additional container required for determining accurate pixel dimensions of the canvas when resizing
+				const rendererContainer = document.createElement( 'f-preview' );
+				container.appendChild( rendererContainer );
+
+				rendererContainer.appendChild( renderer.domElement );
+				renderer.domElement.className = 'renderer panel';
 
 				//
 
@@ -140,13 +187,13 @@
 
 				window.addEventListener( 'resize', onWindowResize );
 
-				initEditor();
+				initEditor(container);
 
 				onWindowResize();
 
 			}
 
-			function initEditor() {
+			function initEditor(container) {
 
 				nodeEditor = new NodeEditor( scene, renderer, composer );
 
@@ -156,22 +203,15 @@
 
 				} );
 
-				document.body.appendChild( nodeEditor.domElement );
+				container.appendChild( nodeEditor.domElement );
+				nodeEditor.domElement.className = 'panel';
 
 			}
 
 			function onWindowResize() {
 
-				const width = window.innerWidth;
-				const height = window.innerHeight;
-
-				camera.aspect = width / height;
-				camera.updateProjectionMatrix();
-
-				renderer.setSize( width, height );
-				if ( composer ) composer.setSize( width, height );
-
-				nodeEditor.setSize( width, height );
+				checkResize();
+				nodeEditor.setSize( window.innerWidth, window.innerHeight );
 
 			}
 
@@ -187,11 +227,32 @@
 
 			function render() {
 
+				checkResize();
+
 				if ( composer && composer.passes.length > 1 ) composer.render();
 				else renderer.render( scene, camera );
 
 			}
 
+			function checkResize() {
+
+				const canvas = renderer.domElement;
+
+				const rendererContainer = canvas.parentNode;
+				const width = rendererContainer.clientWidth;
+				const height = rendererContainer.clientHeight;
+
+				if ( canvas.width !== width || canvas.height !== height ) {
+
+					camera.aspect = width / height;
+					camera.updateProjectionMatrix();
+
+					renderer.setSize( width , height );
+					if ( composer ) composer.setSize( width, height );
+
+				}
+			}
+
 		</script>
 
 	</body>