feat: pseudo 3d layer
Some checks failed
Build Demo / build (push) Failing after 6m39s

This commit is contained in:
Cdm2883 2024-08-09 22:24:14 +08:00
parent c966b1c617
commit c40aeb1dfb
5 changed files with 95 additions and 6 deletions

View File

@ -9,3 +9,10 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
#pseudo-3d-layer {
position: fixed;
width: 100%;
height: 100%;
pointer-events: none;
}

View File

@ -14,6 +14,8 @@
<canvas id="three-viewport"></canvas> <canvas id="three-viewport"></canvas>
<div id="pseudo-3d-layer"></div>
<script src="assets/javascripts/main.js"></script> <script src="assets/javascripts/main.js"></script>
</body> </body>
</html> </html>

View File

@ -1,8 +1,31 @@
import ThreeViewport from "./three.js"; import WorldViewport from "./world-viewport.js";
import WebGL from "three/examples/jsm/capabilities/WebGL.js"; import WebGL from "three/examples/jsm/capabilities/WebGL.js";
import Pseudo3DLayer from "./pseudo-3d-layer.js";
if (WebGL.isWebGL2Available()) { if (WebGL.isWebGL2Available()) {
new ThreeViewport(document.getElementById('three-viewport')).setup(); const canvas = document.getElementById('three-viewport');
const world = new WorldViewport(canvas);
world.setup();
console.log(world);
const container = document.getElementById('pseudo-3d-layer');
const layer = new Pseudo3DLayer(container);
layer.bind(world);
console.log(layer);
const test1 = document.createElement('div');
test1.style.width = '10%';
test1.style.height = '10%';
test1.style.background = 'red';
test1.style.pointerEvents = 'fill';
layer.add(test1, { x: 0, y: 0, z: 0 });
const test2 = document.createElement('div');
test2.style.width = '5%';
test2.style.height = '5%';
test2.style.background = 'green';
test2.style.pointerEvents = 'fill';
layer.add(test2, { x: 0, y: 0, z: 1 });
} else { } else {
document.body.appendChild(WebGL.getWebGL2ErrorMessage()); document.body.appendChild(WebGL.getWebGL2ErrorMessage());
} }

56
src/pseudo-3d-layer.js Normal file
View File

@ -0,0 +1,56 @@
export default class Pseudo3DLayer {
/** @param {HTMLDivElement} container */
constructor(container) {
this.container = container;
}
/** @param {WorldViewport} world */
bind(world) {
this.world = world;
this.originX = world.camera.position.z;
world.renderPseudo = this.render.bind(this);
}
/** @type {[ { wrapper: HTMLDivElement, element: HTMLElement, x: (layer: Pseudo3DLayer) => number, y: (layer: Pseudo3DLayer) => number, z: (layer: Pseudo3DLayer) => number } ]} */
items = [];
/**
* @param {HTMLElement} element
* @param {{ x: number | (() => number), y?: number | (() => number), z?: number | (() => number) }} options
*/
add(element, options) {
const wrapper = document.createElement('div');
wrapper.style.position = 'absolute';
wrapper.style.width = '100%';
wrapper.style.height = '100%';
wrapper.appendChild(element);
this.container.appendChild(wrapper);
// const
const value = x => typeof x === 'function' ? x : () => (x || 0);
this.items.push({
wrapper,
element,
x: value(options.x),
y: value(options.y),
z: value(options.z),
});
}
static PixelToBlockMultiple = 40;
render(item) {
if (!item) return this.items.forEach(item => this.render(item));
const { innerWidth: windowWidth, innerHeight: windowHeight } = window;
const { clientWidth: width, clientHeight: height } = item.element;
const [ x, y, z ] = [ item.x(this), item.y(this), item.z(this) ];
const cameraZ = this.world.camera.position.z;
const top = windowHeight / 2 - height / 2 + y;
const left = windowWidth / 2 - width / 2
+ x
+ (this.originX - cameraZ) * Pseudo3DLayer.PixelToBlockMultiple
* (1 + z);
item.wrapper.style.top = `${top}px`;
item.wrapper.style.left = `${left}px`;
}
}

View File

@ -34,7 +34,7 @@ const motionBlurShader = {
`, `,
}; };
export default class ThreeViewport { export default class WorldViewport {
/** @param {HTMLCanvasElement} canvas */ /** @param {HTMLCanvasElement} canvas */
constructor(canvas) { constructor(canvas) {
this.canvas = canvas; this.canvas = canvas;
@ -229,8 +229,6 @@ export default class ThreeViewport {
this.camera.position.set(-34, -12, -0.1); this.camera.position.set(-34, -12, -0.1);
this.camera.rotation.set(0, THREE.MathUtils.degToRad(270), 0); this.camera.rotation.set(0, THREE.MathUtils.degToRad(270), 0);
// this.camera.position.z += -3;
this.controller(); this.controller();
this.animate(); this.animate();
} }
@ -249,12 +247,13 @@ export default class ThreeViewport {
this.canvas.addEventListener('touchstart', event => this.previousX = event.touches[0].clientX); this.canvas.addEventListener('touchstart', event => this.previousX = event.touches[0].clientX);
this.canvas.addEventListener('touchmove', event => { this.canvas.addEventListener('touchmove', event => {
const currentX = event.touches[0].clientX; const currentX = event.touches[0].clientX;
if (this.previousX) this.targetZ += (this.previousX - currentX) * 0.02; if (this.previousX) this.targetZ += (this.previousX - currentX) * 0.025;
this.previousX = currentX; this.previousX = currentX;
}); });
window.addEventListener('touchend', () => this.previousX = null); window.addEventListener('touchend', () => this.previousX = null);
} }
renderPseudo;
animate() { animate() {
requestAnimationFrame(this.animate.bind(this)); requestAnimationFrame(this.animate.bind(this));
@ -275,6 +274,8 @@ export default class ThreeViewport {
this.animatePetals(); this.animatePetals();
if (this.renderPseudo) this.renderPseudo();
this.composer.render(); this.composer.render();
} }
} }