// from https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80 import axios from "axios"; import { SpotLight } from "three"; import * as TWEEN from "@tweenjs/tween.js"; import { AppString } from "@/constants/app"; import { createCamera } from "./components/camera.js"; import { createLights } from "./components/lights.js"; import { createScene } from "./components/scene.js"; import { createTerrain } from "./components/objects/terrain.js"; import { Loop } from "./systems/Loop.js"; import { Resizer } from "./systems/Resizer.js"; import { createControls } from "./systems/controls.js"; import { createRenderer } from "./systems/renderer.js"; const ANIMATION_DURATION_SECS = 10; const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; const COLOR1 = "#42b883"; const COLOR2 = "#0055aa"; const PLATFORM_BORDER = 10; const PLATFORM_EDGE_FOR_UNKNOWNS = 10; const PLATFORM_SIZE = 100; function createLight() { const light = new SpotLight(0xffffff, 0, 0, Math.PI / 8, 0.5, 0); // eslint-disable-next-line @typescript-eslint/no-empty-function light.tick = () => {}; return light; } class World { constructor(container) { this.update = this.update.bind(this); // Instances of camera, scene, and renderer this.camera = createCamera(); this.scene = createScene(COLOR2); this.renderer = createRenderer(); this.light = null; this.lights = []; // Initializae Loop this.loop = new Loop(this.camera, this.scene, this.renderer); container.append(this.renderer.domElement); // Orbit Controls const controls = createControls(this.camera, this.renderer.domElement); // Light Instance, with optional light helper const { light } = createLights(COLOR1); // Terrain Instance const terrain = createTerrain({ color: COLOR1, height: PLATFORM_SIZE + PLATFORM_BORDER, width: PLATFORM_SIZE + PLATFORM_BORDER + PLATFORM_EDGE_FOR_UNKNOWNS, }); this.loop.updatables.push(controls); this.loop.updatables.push(light); this.loop.updatables.push(terrain); this.scene.add(light, terrain); this.loadClaims(); requestAnimationFrame(this.update); // Responsive handler const resizer = new Resizer(container, this.camera, this.renderer); resizer.onResize = () => { this.render(); }; } update(time) { TWEEN.update(time); this.lights.forEach((light) => { light.updateMatrixWorld(); light.target.updateMatrixWorld(); }); requestAnimationFrame(this.update); } async loadClaims() { const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; try { const url = endorserApiServer + "/api/v2/report/claims?claimType=GiveAction"; const headers = { "Content-Type": "application/json" }; const resp = await axios.get(url, { headers: headers }); if (resp.status === 200) { const minDate = resp.data.data[resp.data.data.length - 1].issuedAt; const maxDate = resp.data.data[0].issuedAt; const minTimeMillis = new Date(minDate).getTime(); const fullTimeMillis = new Date(maxDate).getTime() - minTimeMillis; // ratio of animation time to real time const fakeRealRatio = (ANIMATION_DURATION_SECS * 1000) / fullTimeMillis; // calculate when lights shine on appearing claim area for (const claim of resp.data.data) { // claim is a GiveServerRecord (see endorserServer.ts) // compute location for this claim const randomness = claim.id.substring(10); const x = (100 * BASE32.indexOf(randomness.substring(0, 1))) / 32 - 50; const z = (100 * BASE32.indexOf(randomness.substring(8, 9))) / 32 - 50; const light = createLight(); light.position.set(x, 20, z); light.target.position.set(x, 0, z); this.loop.updatables.push(light); this.scene.add(light); this.scene.add(light.target); // now figure out the timing and shine a light const timeDelayMillis = fakeRealRatio * (new Date(claim.issuedAt).getTime() - minTimeMillis); new TWEEN.Tween(light) .delay(timeDelayMillis) .to({ intensity: 100 }, 10) .chain( new TWEEN.Tween(light.position) .to({ y: 5 }, 1000) .onComplete(() => { this.scene.remove(light); light.dispose(); }) ) .start(); this.lights = [...this.lights, light]; } console.log("done adding cube"); } else { console.log( "Got bad response status & data of", resp.status, resp.data ); // this.alertTitle = "Error With Server"; // this.alertMessage = // "Got an error retrieving your given time from the server."; // this.isAlertVisible = true; } } catch (error) { console.log("Got error of", error); // this.alertTitle = "Error With Server"; // this.alertMessage = // "Got an error retrieving your given time from the server."; // this.isAlertVisible = true; } } render() { // draw a single frame this.renderer.render(this.scene, this.camera); } // Animation handlers start() { this.loop.start(); } stop() { this.loop.stop(); } } export { World };