2025-04-07 20:09:47 +02:00

178 lines
5.7 KiB
JavaScript

import * as THREE from "three";
// import * as dat from 'dat.gui';
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/addons/shaders/FXAAShader.js";
import { Timer } from "three/addons/Addons.js";
import { setupThreeJS, serialiseCamera } from "./helpers.js";
import { CustomOutlinePass } from "./CustomOutlinePass.js";
import FindSurfaces from "./FindSurfaces.js";
import { load_gltf } from "./LoadGLTF.js";
// let ticking = false;
// let attached = false;
// function render() {
// const there =
// viewer.getBoundingClientRect().bottom <
// sticky.getBoundingClientRect().bottom;
// const delta = (window.scrollY - last_scroll_pos) / 30;
// if (there && !attached) {
// console.log("attaching");
// sticky.appendChild(viewer);
// viewer.hide_ui();
// viewer.style.height = "100px";
// viewer.style.width = "100px";
// viewer.style["min-height"] = "unset";
// viewer.component.canvas.style.height = "100%";
// viewer.style.border = "unset";
// viewer.onWindowResize();
// viewer.component.render_loop = false;
// attached = true;
// }
// if ((window.scrollY < transition_scroll_pos) && attached) {
// console.log("detaching");
// viewer.replaceWith(date);
// byline.style.display = "unset";
// attached = false;
// }
export class ScrollLockedViewer extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: "open" });
}
connectedCallback() {
let component = setupThreeJS(this);
this.component = component;
const { canvas, camera, scene, renderer } = component;
component.hide_ui();
this.style.display = "block";
// this.style.height = "100px";
this.style["aspect-ratio"] = "1 / 1";
this.style["min-height"] = "unset";
component.canvas.style.height = "100%";
component.container.style.height = "100%";
// this.style.border = "unset";
const render_size_multiplier = 4;
renderer.setPixelRatio(render_size_multiplier);
renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);
const model_path = this.getAttribute("model");
const spin = (this.getAttribute("spin") || "true") === "true";
// determine the outline and bg colors
const body = document.getElementsByTagName("body")[0];
const style = window.getComputedStyle(body);
const outline_color = style.getPropertyValue("--theme-model-line-color");
const model_color = style.getPropertyValue("--theme-model-bg-color");
const directionalLight = new THREE.DirectionalLight(
0xffffff,
this.getAttribute("directional-light") || 2
);
scene.add(directionalLight);
directionalLight.position.set(1.7, 1, -1);
const ambientLight = new THREE.AmbientLight(
0xffffff,
this.getAttribute("ambient-light") || 0.5
);
scene.add(ambientLight);
// Set up post processing
// Create a render target that holds a depthTexture so we can use it in the outline pass
// See: https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderTarget.depthBuffer
console.log(`clientHeight ${canvas.clientHeight} height ${canvas.height}`);
console.log(`clientWidth ${canvas.clientWidth} width ${canvas.width}`);
const depthTexture = new THREE.DepthTexture();
const renderTarget = new THREE.WebGLRenderTarget(
canvas.width,
canvas.height,
{
depthTexture: depthTexture,
depthBuffer: true,
}
);
// Initial render pass.
const composer = new EffectComposer(renderer, renderTarget);
component.composer = composer;
const pass = new RenderPass(scene, camera);
composer.addPass(pass);
// Outline pass.
const customOutline = new CustomOutlinePass(
new THREE.Vector2(canvas.width, canvas.height),
scene,
camera,
outline_color,
render_size_multiplier
);
composer.addPass(customOutline);
// Antialias pass.
// const effectFXAA = new ShaderPass(FXAAShader);
// effectFXAA.uniforms["resolution"].value.set(
// 1.0 / canvas.width,
// 1.0 / canvas.height
// );
// composer.addPass(effectFXAA);
// Load model
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderConfig({ type: "js" });
dracoLoader.setDecoderPath("https://www.gstatic.com/draco/v1/decoders/");
loader.setDRACOLoader(dracoLoader);
const surfaceFinder = new FindSurfaces();
loader.load(model_path, (gltf) =>
load_gltf(this, scene, surfaceFinder, model_color, customOutline, gltf)
);
// Set up orbital camera controls.
let controls = new OrbitControls(camera, renderer.domElement);
component.controls = controls;
controls.autoRotate = spin;
controls.update();
let last_scroll_pos = 0;
const onscroll = () => {
const delta = (window.scrollY - last_scroll_pos) / 30;
if (Math.abs(delta) > 0.1) {
controls.update(delta);
composer.render();
last_scroll_pos = window.scrollY;
}
};
let ticking = false;
document.addEventListener("scroll", (event) => {
if (!ticking) {
window.requestAnimationFrame(() => {
onscroll();
ticking = false;
});
ticking = true;
}
});
this.render = composer.render;
}
}
export default ScrollLockedViewer;