diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-03-09 14:28:01 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-03-09 14:28:01 +0900 |
| commit | be7e3b9a0cb81b78a744993fef2fa2fd2833fa9c (patch) | |
| tree | c82e18ce93ec0a24c57d7e36eb54a09266b3a25b /packages/frontend/src/utility/physics.ts | |
| parent | enhnace(frontend): 文字列比較のためのローマナイズを強化(... (diff) | |
| download | sharkey-be7e3b9a0cb81b78a744993fef2fa2fd2833fa9c.tar.gz sharkey-be7e3b9a0cb81b78a744993fef2fa2fd2833fa9c.tar.bz2 sharkey-be7e3b9a0cb81b78a744993fef2fa2fd2833fa9c.zip | |
refactor(frontend): scripts -> utility
Diffstat (limited to 'packages/frontend/src/utility/physics.ts')
| -rw-r--r-- | packages/frontend/src/utility/physics.ts | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/packages/frontend/src/utility/physics.ts b/packages/frontend/src/utility/physics.ts new file mode 100644 index 0000000000..8a4e9319b3 --- /dev/null +++ b/packages/frontend/src/utility/physics.ts @@ -0,0 +1,157 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as Matter from 'matter-js'; + +export function physics(container: HTMLElement) { + const containerWidth = container.offsetWidth; + const containerHeight = container.offsetHeight; + const containerCenterX = containerWidth / 2; + + // サイズ固定化(要らないかも?) + container.style.position = 'relative'; + container.style.boxSizing = 'border-box'; + container.style.width = `${containerWidth}px`; + container.style.height = `${containerHeight}px`; + + // create engine + const engine = Matter.Engine.create({ + constraintIterations: 4, + positionIterations: 8, + velocityIterations: 8, + }); + + const world = engine.world; + + // create renderer + const render = Matter.Render.create({ + engine: engine, + //element: document.getElementById('debug'), + options: { + width: containerWidth, + height: containerHeight, + background: 'transparent', // transparent to hide + wireframeBackground: 'transparent', // transparent to hide + }, + }); + + // Disable to hide debug + Matter.Render.run(render); + + // create runner + const runner = Matter.Runner.create(); + Matter.Runner.run(runner, engine); + + const groundThickness = 1024; + const ground = Matter.Bodies.rectangle(containerCenterX, containerHeight + (groundThickness / 2), containerWidth, groundThickness, { + isStatic: true, + restitution: 0.1, + friction: 2, + }); + + //const wallRight = Matter.Bodies.rectangle(window.innerWidth+50, window.innerHeight/2, 100, window.innerHeight, wallopts); + //const wallLeft = Matter.Bodies.rectangle(-50, window.innerHeight/2, 100, window.innerHeight, wallopts); + + Matter.World.add(world, [ + ground, + //wallRight, + //wallLeft, + ]); + + const objEls = Array.from(container.children) as HTMLElement[]; + const objs: Matter.Body[] = []; + for (const objEl of objEls) { + const left = objEl.dataset.physicsX ? parseInt(objEl.dataset.physicsX) : objEl.offsetLeft; + const top = objEl.dataset.physicsY ? parseInt(objEl.dataset.physicsY) : objEl.offsetTop; + + let obj: Matter.Body; + if (objEl.classList.contains('_physics_circle_')) { + obj = Matter.Bodies.circle( + left + (objEl.offsetWidth / 2), + top + (objEl.offsetHeight / 2), + Math.max(objEl.offsetWidth, objEl.offsetHeight) / 2, + { + restitution: 0.5, + }, + ); + } else { + const style = window.getComputedStyle(objEl); + obj = Matter.Bodies.rectangle( + left + (objEl.offsetWidth / 2), + top + (objEl.offsetHeight / 2), + objEl.offsetWidth, + objEl.offsetHeight, + { + chamfer: { radius: parseInt(style.borderRadius || '0', 10) }, + restitution: 0.5, + }, + ); + } + objEl.id = obj.id.toString(); + objs.push(obj); + } + + Matter.World.add(engine.world, objs); + + // Add mouse control + + const mouse = Matter.Mouse.create(container); + const mouseConstraint = Matter.MouseConstraint.create(engine, { + mouse: mouse, + constraint: { + stiffness: 0.1, + render: { + visible: false, + }, + }, + }); + + Matter.World.add(engine.world, mouseConstraint); + + // keep the mouse in sync with rendering + render.mouse = mouse; + + for (const objEl of objEls) { + objEl.style.position = 'absolute'; + objEl.style.top = '0'; + objEl.style.left = '0'; + objEl.style.margin = '0'; + } + + window.requestAnimationFrame(update); + + let stop = false; + + function update() { + for (const objEl of objEls) { + const obj = objs.find(obj => obj.id.toString() === objEl.id.toString()); + if (obj == null) continue; + + const x = (obj.position.x - objEl.offsetWidth / 2); + const y = (obj.position.y - objEl.offsetHeight / 2); + const angle = obj.angle; + objEl.style.transform = `translate(${x}px, ${y}px) rotate(${angle}rad)`; + } + + if (!stop) { + window.requestAnimationFrame(update); + } + } + + // 奈落に落ちたオブジェクトは消す + const intervalId = window.setInterval(() => { + for (const obj of objs) { + if (obj.position.y > (containerHeight + 1024)) Matter.World.remove(world, obj); + } + }, 1000 * 10); + + return { + stop: () => { + stop = true; + Matter.Runner.stop(runner); + window.clearInterval(intervalId); + }, + }; +} |