pucchinglgl/src/main.ts
Kohei Watanabe 290d3dda6c Revert "feat: vibrate"
This reverts commit 33bc465182.
あまり気持ちよくないのでやめる
2020-12-29 19:53:26 +09:00

150 lines
4.5 KiB
TypeScript

import {
Color,
Mesh,
MeshBasicMaterial,
PerspectiveCamera,
PlaneGeometry,
Raycaster,
Scene,
Vector2,
WebGLRenderer,
} from "three";
import { Oscillator } from "tone";
import randomInt from "./randomInt";
import keyPosition from "./keyPosition";
import keyLayouts from "./config/keyLayouts";
import pallet from "./config/pallet";
const root = document.body;
const scene = new Scene();
const camera = new PerspectiveCamera();
const grid = { colum: 14, row: 4 } as const;
const mouse = new Vector2(NaN, NaN);
const panels = Array.from({ length: grid.colum * grid.row }, (_, i) => {
const geometry = new PlaneGeometry();
const material = new MeshBasicMaterial({ opacity: 0 });
const mesh = new Mesh(geometry, material);
const note = `${"CDEFGAB"[i % 7]}${Math.floor(i / 7)}`;
mesh.userData.osc = new Oscillator(note, "square").toDestination();
return mesh;
});
const renderer = new WebGLRenderer();
const canvas = renderer.domElement;
const raycaster = new Raycaster();
function render() {
renderer.render(scene, camera);
}
function tone(osc: Oscillator) {
osc.start();
osc.stop("+0.05");
}
function drawRect() {
raycaster.setFromCamera(mouse, camera);
const [intersect] = raycaster.intersectObjects(panels);
if (!intersect) return;
try {
// FIXME: Error: Start time must be strictly greater than previous start time.
tone(intersect.object.userData.osc);
} catch {}
const color = pallet[randomInt(pallet.length - 1)];
const geometry = new PlaneGeometry(1, 1);
const material = new MeshBasicMaterial({ color });
const mesh = new Mesh(geometry, material);
const offset = () => 0.05 * (Math.random() - 0.5);
mesh.scale.set(0.075 + offset(), 0.075 + offset(), 0);
Object.assign(mesh.position, intersect.point);
scene.add(mesh);
render();
// NOTE: 1度だけだと正しく表示されないことがあるのでもう一度renderする。原因よくわからない。
window.requestAnimationFrame(render);
}
function handleKeydown({ key }: KeyboardEvent) {
if (key === " ") return setup();
const [x = randomInt(grid.colum - 1), y = randomInt(grid.row - 1)] = [
...keyPosition(keyLayouts.default, key),
...keyPosition(keyLayouts.shift, key),
];
const offset = () => Math.random() - 0.5;
mouse.x = ((x + offset()) / (grid.colum - 1)) * 2 - 1;
mouse.y = -((y + offset()) / (grid.row - 1)) * 2 + 1;
drawRect();
}
function handleMouseMove(event: { clientX: number; clientY: number }) {
const offset = () => 0.2 * (Math.random() - 0.5);
mouse.x = (event.clientX / canvas.clientWidth) * 2 - 1 + offset();
mouse.y = -(event.clientY / canvas.clientHeight) * 2 + 1 + offset();
drawRect();
}
function handleTouchmove(event: TouchEvent) {
event.preventDefault();
[...event.touches].map(handleMouseMove);
}
function handleMouseMoveEnd() {
mouse.set(NaN, NaN);
}
function adjustPanelSize() {
const width = camera.aspect / grid.colum;
const height = 1 / grid.row;
for (const [i, panel] of panels.entries()) {
panel.scale.set(width, height, 1);
Object.assign(panel.position, {
x: width * ((i % grid.colum) - (grid.colum - 1) / 2),
y: height * (Math.floor(i / grid.colum) - (grid.row - 1) / 2),
});
}
}
function adjustRendererSize() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
adjustPanelSize();
render();
}
function setup() {
scene.background = new Color();
scene.clear();
scene.add(...panels);
camera.position.z = 1;
adjustRendererSize();
}
function main() {
Object.assign(canvas.style, { width: "100%", height: "100%" });
root.addEventListener("keydown", handleKeydown);
root.addEventListener("touchstart", handleTouchmove, { passive: false });
root.addEventListener("touchmove", handleTouchmove, { passive: false });
root.addEventListener("mousedown", handleMouseMove);
root.addEventListener("mousedown", () => {
root.addEventListener("mousemove", handleMouseMove);
});
for (const event of ["mouseup", "mouseleave"] as const) {
root.addEventListener(event, () => {
root.removeEventListener("mousemove", handleMouseMove);
});
}
for (const event of ["keyup", "mouseup", "mouseleave", "touchend"] as const) {
root.addEventListener(event, handleMouseMoveEnd);
}
root.appendChild(canvas);
Object.assign(root.style, { overflow: "hidden", overscrollBehavior: "none" });
window.addEventListener("resize", adjustRendererSize);
setup();
}
main();