diff --git a/src/App.js b/src/App.js index 30e1576..7b9c289 100644 --- a/src/App.js +++ b/src/App.js @@ -5,21 +5,16 @@ import Wall from "./wall/Wall"; import Sphere from "./wall/Sphere"; import createTouches from "touches"; import "./App.scss"; -import i1 from "./images/1.jpg"; -import i2 from "./images/2.jpg"; -import i3 from "./images/3.jpg"; - +import images from "./images/image-helper"; const videoWidth = 600; const videoHeight = 500; -const width = 1080; -const height = 1920; +const width = window.innerWidth; +const height = window.innerHeight; const radius = 412; -const displacement = 412; +const displacement = 600; const POSENET_URL = "https://lg-cjdqwkbo-1256266248.cos.ap-shanghai.myqcloud.com/mobile-net/50/model-stride16.json"; -const Block = ({ style }) =>
; - const Mist = ({ style }) => (
@@ -98,21 +93,27 @@ const App = () => { } }, [video]); + const initWall = () => { + const wall = new Wall({ + items: Object.values(images).map((url) => ({ url })), + imgWidth: 90, + imgHeight: 100, + containerWidth: width, + containerHeight: height, + speed: 1, + onListChange: setList, + }); + wall.init(); + setWall(wall); + return wall; + }; useEffect(() => { - if (!wall) { - const wall = new Wall({ - items: [{ url: i1 }, { url: i2 }, { url: i3 }], - imgWidth: 90, - imgHeight: 100, - containerWidth: 1080, - containerHeight: 1920, - speed: 1, - onListChange: setList, - }); - wall.init(); - setWall(wall); - wall.getList(); - } else { + if (wall && sphere) { + wall.attachSphere(sphere); + } + }, [wall]); + useEffect(() => { + if (!sphere) { let sphere = new Sphere({ x: width / 2, y: height / 2, @@ -120,34 +121,36 @@ const App = () => { displacement, width, }); - if (wall) { - wall.attachSphere(sphere); - } setSphere(sphere); - } - return () => { - if (wall) wall.dispose(); - }; - }, [wall]); - - useEffect(() => { - if (sphere) { - const container = document.querySelector(".App"); - const touchHandler = createTouches(container, { - target: container, - filtered: true, - }); - touchHandler.on("move", (_, [x, y]) => { - sphere.x = x; - sphere.y = y; - }); + initWall(); } }, [sphere]); + // useEffect(() => { + // if (sphere) { + // const container = document.querySelector(".App"); + // const touchHandler = createTouches(container, { + // target: container, + // filtered: true, + // }); + // touchHandler.on("move", (_, [x, y]) => { + // sphere.x = x; + // sphere.y = y; + // }); + // } + // }, [sphere]); + return ( -
+
{ + await wall.dispose(); + setWall(null); + initWall(); + }} + > {list.map(([key, el]) => ( - +
))} {sphere && }
diff --git a/src/App.scss b/src/App.scss index 1ca0979..9fdb41c 100644 --- a/src/App.scss +++ b/src/App.scss @@ -15,6 +15,12 @@ height: 100vh; z-index: 1; } + .block { + background-size: cover; + background-position: center; + opacity: 1; + position: absolute; + } .mist { position: absolute; z-index: 100; diff --git a/src/images/10.jpg b/src/images/10.jpg new file mode 100644 index 0000000..0e001cf Binary files /dev/null and b/src/images/10.jpg differ diff --git a/src/images/11.jpg b/src/images/11.jpg new file mode 100644 index 0000000..ca95782 Binary files /dev/null and b/src/images/11.jpg differ diff --git a/src/images/12.jpg b/src/images/12.jpg new file mode 100644 index 0000000..90a083b Binary files /dev/null and b/src/images/12.jpg differ diff --git a/src/images/13.jpg b/src/images/13.jpg new file mode 100644 index 0000000..0df2f9a Binary files /dev/null and b/src/images/13.jpg differ diff --git a/src/images/14.jpg b/src/images/14.jpg new file mode 100644 index 0000000..39ee8ab Binary files /dev/null and b/src/images/14.jpg differ diff --git a/src/images/4.jpg b/src/images/4.jpg new file mode 100644 index 0000000..fe5a8df Binary files /dev/null and b/src/images/4.jpg differ diff --git a/src/images/5.jpg b/src/images/5.jpg new file mode 100644 index 0000000..7f81025 Binary files /dev/null and b/src/images/5.jpg differ diff --git a/src/images/6.jpg b/src/images/6.jpg new file mode 100644 index 0000000..6692433 Binary files /dev/null and b/src/images/6.jpg differ diff --git a/src/images/7.jpg b/src/images/7.jpg new file mode 100644 index 0000000..a9d8993 Binary files /dev/null and b/src/images/7.jpg differ diff --git a/src/images/8.jpg b/src/images/8.jpg new file mode 100644 index 0000000..27e2198 Binary files /dev/null and b/src/images/8.jpg differ diff --git a/src/images/9.jpg b/src/images/9.jpg new file mode 100644 index 0000000..b24fe8d Binary files /dev/null and b/src/images/9.jpg differ diff --git a/src/images/image-helper.js b/src/images/image-helper.js new file mode 100644 index 0000000..729f2a4 --- /dev/null +++ b/src/images/image-helper.js @@ -0,0 +1,11 @@ +function importAll(r) { + let obj = {}; + r.keys().forEach((key) => { + obj[key.replace("./", "").replace(".jpg", "")] = r(key); + }); + return obj; +} + +const images = importAll(require.context("./", false, /\.(png|jpe?g|svg)$/)); + +export default images; diff --git a/src/wall/Wall.js b/src/wall/Wall.js index 6951521..fde22ff 100644 --- a/src/wall/Wall.js +++ b/src/wall/Wall.js @@ -1,7 +1,5 @@ import { Vector3 } from "three"; -export const fps = 60; -export const mspf = 1000 / fps; - +const g = 0.55 / 1000; class El { constructor(seed) { const { data, row, wall, key, index } = seed; @@ -11,32 +9,28 @@ class El { const point = new Vector3(x, y, 0); const targetPoint = new Vector3(x, y, 0); const spherePoint = sphere ? sphere.point : null; + let scale = 1; if (sphere && point.distanceTo(spherePoint) < sphere.displacement) { const direction = point.clone().sub(spherePoint); const displacementAmount = sphere.displacement - direction.length(); + scale = ((1 - displacementAmount / sphere.displacement) / 1) * 0.3 + 0.7; direction.setLength(displacementAmount); direction.add(point); - - point.lerp(direction, 1); // ✨ magic number + point.lerp(direction, 0.725); // ✨ magic number } - - // and move them back to their original position if (point.distanceTo(targetPoint) > 0.01) { point.lerp(targetPoint, 0.27); // ✨ magic number } const top = point.y - imgHeight / 2; const left = point.x - imgWidth / 2; const { url } = data; - const style = { - position: "absolute", width: `${imgWidth}px`, height: `${imgHeight}px`, top: `${top}px`, left: `${left}px`, - backgroundSize: "cover", backgroundImage: `url(${url})`, - opacity: 1, + transform: `scale(${scale})`, zIndex: row.size - Math.floor(Math.abs(row.size / 2 - row.index)), }; Object.assign(this, { seed, data, style, key, point }); @@ -45,10 +39,14 @@ class El { } class Row { + falled = false; + q = []; + fst; + t1; + t2; constructor({ wall, index }) { Object.assign(this, { wall, - q: [], minSize: wall.colNum + 1, index, ratio: Math.random(), @@ -67,7 +65,13 @@ class Row { const { wall } = this; const data = wall.getNext(); const key = wall.getKey(); - const seed = { data, row: this, wall, key, index: this.q.length }; + const seed = { + data, + row: this, + wall, + key, + index: this.q.length, + }; const el = new El(seed); this.wall.elMap.set(key, el); this.q.push(el); @@ -76,6 +80,22 @@ class Row { const el = this.q.shift(); this.wall.elMap.delete(el.key); } + update() { + this.q.forEach((el) => { + this.wall.elMap.set(el.key, new El(el.seed)); + }); + } + fall(now) { + if (!this.fst) { + this.fst = now; + this.t1 = 0; + } else { + this.t2 = now - this.fst; + this.y += (g * (this.t2 * this.t2 - this.t1 * this.t1)) / 2; + this.t1 = this.t2; + } + this.update(); + } nextFrame(ratioSpeed) { this.ratio += ratioSpeed; if (this.ratio > 1) { @@ -86,15 +106,19 @@ class Row { }); this.ratio -= 1; } - this.q.forEach((el) => { - this.wall.elMap.set(el.key, new El(el.seed)); - }); + this.update(); } } export default class Wall { - #rafID; + #rafID = null; #lastTime; + falling = false; + rows = []; + key = 0; + time = 0; + index = 0; + maxDeltaTime = 1 / 30; constructor({ items, imgWidth, @@ -112,37 +136,27 @@ export default class Wall { const blockHeight = containerHeight / (rowNum - 1); const elMap = new Map(); Object.assign(this, { - time: 0, - index: 0, items, colNum, rowNum, elNum, imgWidth, imgHeight, + containerWidth, + containerHeight, ratioSpeed, - rows: [], - key: 0, blockWidth, blockHeight, elMap, onListChange, - maxDeltaTime: 1 / 30, }); } getNext() { - const { index, elNum, items } = this; - if (index === elNum) { - this.index = 1; - return items[0]; - } else { - this.index++; - return items[index]; - } + const { items } = this; + return items[Math.floor(Math.random() * items.length)]; } init() { const { rowNum } = this; - for (let i = 0; i < rowNum; i++) { const row = new Row({ wall: this, index: i }); row.init(); @@ -154,20 +168,29 @@ export default class Wall { } animate = () => { if (!this.isRunning) return; - window.requestAnimationFrame(this.animate); - const now = performance.now(); const dt = Math.min(this.maxDeltaTime, (now - this.#lastTime) / 1000); - this.rows.forEach((row) => row.nextFrame(this.ratioSpeed)); + this.rows.forEach((row) => + this.falling ? row.fall(now) : row.nextFrame(this.ratioSpeed) + ); this.getList(); this.time += dt; this.#lastTime = now; + this.#rafID = window.requestAnimationFrame(this.animate); }; - dispose() { + async dispose() { if (this.#rafID === null) return; - window.cancelAnimationFrame(this.#rafID); - this.#rafID = null; - this.isRunning = false; + this.falling = true; + await new Promise((resolve) => { + setTimeout(() => { + this.falling = false; + window.cancelAnimationFrame(this.#rafID); + this.#rafID = null; + this.isRunning = false; + resolve(); + }, Math.sqrt((this.containerHeight * 2) / g) + 500); + }); + return this; } getKey() {