169 lines
4.1 KiB
TypeScript
169 lines
4.1 KiB
TypeScript
|
import 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();
|
||
|
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
|
||
|
hasBounds: false,
|
||
|
enabled: true,
|
||
|
wireframes: false,
|
||
|
showSleeping: true,
|
||
|
showDebug: false,
|
||
|
showBroadphase: false,
|
||
|
showBounds: false,
|
||
|
showVelocity: false,
|
||
|
showCollisions: false,
|
||
|
showAxes: false,
|
||
|
showPositions: false,
|
||
|
showAngleIndicator: false,
|
||
|
showIds: false,
|
||
|
showShadows: false
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Disable to hide debug
|
||
|
Matter.Render.run(render);
|
||
|
|
||
|
// create runner
|
||
|
const runner = Matter.Runner.create();
|
||
|
Matter.Runner.run(runner, engine);
|
||
|
|
||
|
// add walls
|
||
|
const wallopts = {
|
||
|
isStatic: true,
|
||
|
restitution: 0.2,
|
||
|
friction: 1
|
||
|
};
|
||
|
const groundopts = {
|
||
|
isStatic: true,
|
||
|
restitution: 0.1,
|
||
|
friction: 2
|
||
|
};
|
||
|
|
||
|
const groundThickness = 100;
|
||
|
const ground = Matter.Bodies.rectangle(containerCenterX, containerHeight + (groundThickness / 2), containerWidth, groundThickness, groundopts);
|
||
|
//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);
|
||
|
const objs = [];
|
||
|
for (const objEl of objEls) {
|
||
|
let obj;
|
||
|
if (objEl.classList.contains('_physics_circle_')) {
|
||
|
obj = Matter.Bodies.circle(
|
||
|
objEl.offsetLeft + (objEl.offsetWidth / 2),
|
||
|
objEl.offsetTop + (objEl.offsetHeight / 2),
|
||
|
Math.max(objEl.offsetWidth, objEl.offsetHeight) / 2,
|
||
|
{
|
||
|
restitution: 0.1,
|
||
|
friction: 4,
|
||
|
frictionAir: 0,
|
||
|
frictionStatic: 50,
|
||
|
density: 100,
|
||
|
}
|
||
|
);
|
||
|
} else {
|
||
|
const style = window.getComputedStyle(objEl);
|
||
|
obj = Matter.Bodies.rectangle(
|
||
|
objEl.offsetLeft + (objEl.offsetWidth / 2),
|
||
|
objEl.offsetTop + (objEl.offsetHeight / 2),
|
||
|
objEl.offsetWidth,
|
||
|
objEl.offsetHeight,
|
||
|
{
|
||
|
restitution: 0.1,
|
||
|
friction: 4,
|
||
|
frictionAir: 0,
|
||
|
frictionStatic: 50,
|
||
|
density: 100,
|
||
|
chamfer: { radius: parseInt(style.borderRadius, 10) },
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
objEl.id = obj.id;
|
||
|
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: 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;
|
||
|
objEl.style.userSelect = 'none';
|
||
|
objEl.style.willChange = 'transform';
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
stop: () => {
|
||
|
stop = true;
|
||
|
Matter.Runner.stop(runner);
|
||
|
}
|
||
|
};
|
||
|
}
|