|
|
@@ -0,0 +1,201 @@
|
|
|
+<!DOCTYPE html>
|
|
|
+<html>
|
|
|
+ <head>
|
|
|
+ <title>three.js css3d - mixed</title>
|
|
|
+ <meta charset="utf-8">
|
|
|
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
|
+ <link type="text/css" rel="stylesheet" href="main.css">
|
|
|
+ <style>
|
|
|
+ body {
|
|
|
+ background-color: #ffffff;
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+ <script type="importmap">
|
|
|
+ {
|
|
|
+ "imports": {
|
|
|
+ "three": "../build/three.module.js",
|
|
|
+ "three/addons/": "./jsm/"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <script type="module">
|
|
|
+
|
|
|
+ import * as THREE from 'three';
|
|
|
+
|
|
|
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
+ import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';
|
|
|
+
|
|
|
+ let camera, scene, rendererCSS3D, rendererWebGL;
|
|
|
+ let controls;
|
|
|
+
|
|
|
+ init();
|
|
|
+
|
|
|
+ function init() {
|
|
|
+
|
|
|
+ rendererCSS3D = new CSS3DRenderer();
|
|
|
+ rendererCSS3D.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ document.body.appendChild( rendererCSS3D.domElement );
|
|
|
+
|
|
|
+ rendererWebGL = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
|
|
|
+ rendererWebGL.domElement.style.position = 'absolute';
|
|
|
+ rendererWebGL.domElement.style.top = '0';
|
|
|
+ rendererWebGL.setPixelRatio( window.devicePixelRatio );
|
|
|
+ rendererWebGL.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ rendererWebGL.toneMapping = THREE.NeutralToneMapping;
|
|
|
+ rendererWebGL.setAnimationLoop( animate );
|
|
|
+ document.body.appendChild( rendererWebGL.domElement );
|
|
|
+
|
|
|
+ camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10000 );
|
|
|
+ camera.position.set( - 1000, 500, 1500 );
|
|
|
+
|
|
|
+ scene = new THREE.Scene();
|
|
|
+ scene.background = new THREE.Color( 0xf0f0f0 );
|
|
|
+
|
|
|
+ // Add room
|
|
|
+ const roomGeometry = new THREE.EdgesGeometry( new THREE.BoxGeometry( 4000, 2000, 4000, 10, 5, 10 ) );
|
|
|
+ const roomMaterial = new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2, transparent: true } );
|
|
|
+ const room = new THREE.LineSegments( roomGeometry, roomMaterial );
|
|
|
+ scene.add( room );
|
|
|
+
|
|
|
+ // Add light
|
|
|
+ const hemisphereLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 4 );
|
|
|
+ hemisphereLight.position.set( - 25, 100, 50 );
|
|
|
+ scene.add( hemisphereLight );
|
|
|
+
|
|
|
+ // Add cutout mesh
|
|
|
+ const geometry = new THREE.PlaneGeometry( 1024, 768 );
|
|
|
+ const material = new THREE.MeshBasicMaterial( {
|
|
|
+ color: 0xff0000,
|
|
|
+ blending: THREE.NoBlending,
|
|
|
+ opacity: 0,
|
|
|
+ premultipliedAlpha: true
|
|
|
+ } );
|
|
|
+ const mesh = new THREE.Mesh( geometry, material );
|
|
|
+ mesh.name = 'cutout';
|
|
|
+ scene.add( mesh );
|
|
|
+
|
|
|
+ // Add frame
|
|
|
+ const frame = buildFrame( 1024, 768, 50 );
|
|
|
+ scene.add( frame );
|
|
|
+
|
|
|
+ // Add CSS3D element
|
|
|
+ const iframe = document.createElement( 'iframe' );
|
|
|
+ iframe.style.width = '1028px';
|
|
|
+ iframe.style.height = '768px';
|
|
|
+ iframe.style.border = '0px';
|
|
|
+ iframe.src = 'https://threejs.org/examples/#webgl_animation_keyframes';
|
|
|
+ scene.add( new CSS3DObject( iframe ) );
|
|
|
+
|
|
|
+ // Add controls
|
|
|
+ controls = new OrbitControls( camera );
|
|
|
+ controls.connect( rendererWebGL.domElement );
|
|
|
+ controls.enableDamping = true;
|
|
|
+
|
|
|
+ // Track OrbitControls state
|
|
|
+ let isDragging = false;
|
|
|
+ controls.addEventListener( 'start', () => isDragging = true );
|
|
|
+ controls.addEventListener( 'end', () => isDragging = false );
|
|
|
+
|
|
|
+ // raycast to find CSS3DObject
|
|
|
+ const raycaster = new THREE.Raycaster();
|
|
|
+ const pointer = new THREE.Vector2();
|
|
|
+ document.addEventListener( 'pointermove', ( event ) => {
|
|
|
+
|
|
|
+ // Skip raycasting when dragging
|
|
|
+ if ( isDragging ) return;
|
|
|
+
|
|
|
+ pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
|
|
|
+ pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
|
|
|
+
|
|
|
+ raycaster.setFromCamera( pointer, camera );
|
|
|
+
|
|
|
+ const intersects = raycaster.intersectObjects( scene.children, true );
|
|
|
+
|
|
|
+ rendererWebGL.domElement.style.pointerEvents = '';
|
|
|
+
|
|
|
+ if ( intersects.length > 0 ) {
|
|
|
+
|
|
|
+ const object = intersects[ 0 ].object;
|
|
|
+
|
|
|
+ if ( object.name === 'cutout' ) {
|
|
|
+
|
|
|
+ rendererWebGL.domElement.style.pointerEvents = 'none';
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ window.addEventListener( 'resize', onWindowResize );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function buildFrame( width, height, thickness ) {
|
|
|
+
|
|
|
+ const group = new THREE.Group();
|
|
|
+ const material = new THREE.MeshStandardMaterial( { color: 0x2200ff } );
|
|
|
+
|
|
|
+ // Create the frame border
|
|
|
+ const outerShape = new THREE.Shape();
|
|
|
+ outerShape.moveTo( - ( width / 2 + thickness ), - ( height / 2 + thickness ) );
|
|
|
+ outerShape.lineTo( width / 2 + thickness, - ( height / 2 + thickness ) );
|
|
|
+ outerShape.lineTo( width / 2 + thickness, height / 2 + thickness );
|
|
|
+ outerShape.lineTo( - ( width / 2 + thickness ), height / 2 + thickness );
|
|
|
+ outerShape.lineTo( - ( width / 2 + thickness ), - ( height / 2 + thickness ) );
|
|
|
+
|
|
|
+ // Create inner rectangle (hole)
|
|
|
+ const innerHole = new THREE.Path();
|
|
|
+ innerHole.moveTo( - width / 2, - height / 2 );
|
|
|
+ innerHole.lineTo( width / 2, - height / 2 );
|
|
|
+ innerHole.lineTo( width / 2, height / 2 );
|
|
|
+ innerHole.lineTo( - width / 2, height / 2 );
|
|
|
+ innerHole.lineTo( - width / 2, - height / 2 );
|
|
|
+
|
|
|
+ outerShape.holes.push( innerHole );
|
|
|
+
|
|
|
+ const frameGeometry = new THREE.ExtrudeGeometry( outerShape, {
|
|
|
+ depth: thickness,
|
|
|
+ bevelEnabled: false
|
|
|
+ } );
|
|
|
+
|
|
|
+ const frameMesh = new THREE.Mesh( frameGeometry, material );
|
|
|
+ frameMesh.position.z = - thickness / 2;
|
|
|
+ group.add( frameMesh );
|
|
|
+
|
|
|
+ // Add back plane
|
|
|
+ const backGeometry = new THREE.PlaneGeometry( width + ( thickness * 2 ), height + ( thickness * 2 ) );
|
|
|
+ const backMesh = new THREE.Mesh( backGeometry, material );
|
|
|
+ backMesh.position.set( 0, 0, - thickness / 2 );
|
|
|
+ backMesh.rotation.y = Math.PI;
|
|
|
+ group.add( backMesh );
|
|
|
+
|
|
|
+ return group;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function onWindowResize() {
|
|
|
+
|
|
|
+ camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ camera.updateProjectionMatrix();
|
|
|
+
|
|
|
+ rendererWebGL.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ rendererCSS3D.setSize( window.innerWidth, window.innerHeight );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function animate() {
|
|
|
+
|
|
|
+ controls.update();
|
|
|
+
|
|
|
+ rendererWebGL.render( scene, camera );
|
|
|
+ rendererCSS3D.render( scene, camera );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ </script>
|
|
|
+ </body>
|
|
|
+</html>
|