throw
This commit is contained in:
11
site.css
11
site.css
@@ -43,6 +43,8 @@ main {
|
|||||||
right: 10vw;
|
right: 10vw;
|
||||||
|
|
||||||
transform: perspective(50cm) rotateX(45deg);
|
transform: perspective(50cm) rotateX(45deg);
|
||||||
|
|
||||||
|
transition: transform 2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Cup {
|
.Cup {
|
||||||
@@ -96,8 +98,11 @@ main {
|
|||||||
.Die {
|
.Die {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
height: 20px;
|
border-radius: 2px;
|
||||||
|
height: 10px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
transform: translate(-10px, -10px);
|
transform: translate(-5px, -5px);
|
||||||
width: 20px;
|
width: 10px;
|
||||||
|
|
||||||
|
transition: transform 0.4s ease-out;
|
||||||
}
|
}
|
||||||
|
|||||||
22
src/die.ts
22
src/die.ts
@@ -1,4 +1,6 @@
|
|||||||
import { Power } from "./power.js";
|
import { Power } from "./power.js";
|
||||||
|
import { View } from "./model.js";
|
||||||
|
import { MessageHandler } from "./updates.js";
|
||||||
|
|
||||||
interface Point {
|
interface Point {
|
||||||
x: number;
|
x: number;
|
||||||
@@ -19,6 +21,7 @@ const touchLocation = (event: TouchEvent): Point => {
|
|||||||
export class Die {
|
export class Die {
|
||||||
node: HTMLDivElement;
|
node: HTMLDivElement;
|
||||||
|
|
||||||
|
messageHandler: MessageHandler;
|
||||||
power: Power;
|
power: Power;
|
||||||
|
|
||||||
x: number;
|
x: number;
|
||||||
@@ -32,7 +35,7 @@ export class Die {
|
|||||||
|
|
||||||
positionRAF: number | null = null;
|
positionRAF: number | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor(messageHandler: MessageHandler) {
|
||||||
const node = document.getElementById("Die");
|
const node = document.getElementById("Die");
|
||||||
if (!node) {
|
if (!node) {
|
||||||
throw new Error("No dice");
|
throw new Error("No dice");
|
||||||
@@ -40,6 +43,7 @@ export class Die {
|
|||||||
|
|
||||||
this.node = node as HTMLDivElement;
|
this.node = node as HTMLDivElement;
|
||||||
|
|
||||||
|
this.messageHandler = messageHandler;
|
||||||
this.power = new Power();
|
this.power = new Power();
|
||||||
|
|
||||||
this.x = this.initialX();
|
this.x = this.initialX();
|
||||||
@@ -96,7 +100,10 @@ export class Die {
|
|||||||
window.removeEventListener("mouseup", this.dragEnd);
|
window.removeEventListener("mouseup", this.dragEnd);
|
||||||
window.removeEventListener("touchend", this.dragEnd);
|
window.removeEventListener("touchend", this.dragEnd);
|
||||||
|
|
||||||
console.log("TOSS!", this.startX, this.startY, this.x, this.y);
|
this.messageHandler.update("toss", {
|
||||||
|
power: this.getTossPower(),
|
||||||
|
direction: this.getTossDirection()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updatePosition = () => {
|
private updatePosition = () => {
|
||||||
@@ -119,4 +126,15 @@ export class Die {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getTossDirection = () => {
|
||||||
|
if (this.startX && this.startY) {
|
||||||
|
const dx = this.x - this.startX;
|
||||||
|
const dy = this.y - this.startY;
|
||||||
|
|
||||||
|
return Math.atan2(dy, dx);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Die } from "./die.js";
|
import { Die } from "./die.js";
|
||||||
|
import { MessageHandler } from "./updates.js";
|
||||||
|
|
||||||
export const startApp = () => {
|
export const startApp = () => {
|
||||||
const die = new Die();
|
const messageHandler = new MessageHandler();
|
||||||
|
const die = new Die(messageHandler);
|
||||||
};
|
};
|
||||||
|
|||||||
9
src/model.ts
Normal file
9
src/model.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface State {
|
||||||
|
view: View;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum View {
|
||||||
|
Throw,
|
||||||
|
Toss,
|
||||||
|
Wait
|
||||||
|
}
|
||||||
102
src/updates.ts
Normal file
102
src/updates.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import * as Model from "./model.js";
|
||||||
|
|
||||||
|
|
||||||
|
// Updates
|
||||||
|
const toss = ({ power, direction }: { power: number, direction: number }) => {
|
||||||
|
console.log("Power", power);
|
||||||
|
console.log("Direction", direction);
|
||||||
|
|
||||||
|
document.getElementById("Table")!.style.transform = "initial";
|
||||||
|
document.getElementById("Toss")!.style.display = "none";
|
||||||
|
document.getElementById("Die")!.style.display = "none";
|
||||||
|
|
||||||
|
const die = document.createElement("div");
|
||||||
|
|
||||||
|
const startX = window.outerWidth / 2;
|
||||||
|
const startY = window.outerHeight * 0.9;
|
||||||
|
|
||||||
|
die.id = "Die-toss";
|
||||||
|
die.className = "Die";
|
||||||
|
die.style.left = startX + "px";
|
||||||
|
die.style.top = startY + "px";
|
||||||
|
|
||||||
|
document.getElementById("game")!.appendChild(die);
|
||||||
|
|
||||||
|
// Add +/- pi/20 variance
|
||||||
|
const throwDirection = direction + Math.random()*0.15 - 0.075;
|
||||||
|
|
||||||
|
const table = document.getElementById("Table");
|
||||||
|
if (!table) {
|
||||||
|
throw new Error("no table");
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableX = table.clientLeft;
|
||||||
|
const tableY = table.clientTop;
|
||||||
|
const tableWidth = table.clientWidth;
|
||||||
|
const tableHeight = table.clientHeight;
|
||||||
|
|
||||||
|
// multiplying both by tableHeight as we want at full-power throw to go off
|
||||||
|
// the far side of the table
|
||||||
|
const dx = tableHeight * (0.3 + power) * Math.cos(throwDirection);
|
||||||
|
const dy = tableHeight * (0.3 + power) * Math.sin(throwDirection);
|
||||||
|
|
||||||
|
console.log("Moving to", dx, dy);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
die.style.transform = `translate(${dx}px, ${dy}px)`
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setView = (view: Model.View) => {
|
||||||
|
console.log("Load view", view);
|
||||||
|
switch (view) {
|
||||||
|
case Model.View.Throw:
|
||||||
|
const toss = document.getElementById("Toss")
|
||||||
|
toss!.style.visibility = "none";
|
||||||
|
return;
|
||||||
|
case Model.View.Toss:
|
||||||
|
case Model.View.Wait:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Updates = typeof updates;
|
||||||
|
type UpdateFn = keyof Updates;
|
||||||
|
|
||||||
|
const updates = {
|
||||||
|
setView,
|
||||||
|
toss
|
||||||
|
};
|
||||||
|
|
||||||
|
type Func<F> = F extends (a: infer A) => any ? A : never;
|
||||||
|
type Message<F extends UpdateFn> = {
|
||||||
|
fn: F,
|
||||||
|
args: Func<Updates[F]>
|
||||||
|
};
|
||||||
|
|
||||||
|
export class MessageHandler {
|
||||||
|
constructor() {
|
||||||
|
window.addEventListener("message", this.recieve);
|
||||||
|
}
|
||||||
|
|
||||||
|
update = <F extends UpdateFn>(fn: F, args: Func<Updates[F]>) => {
|
||||||
|
const message = JSON.stringify({ fn, args });
|
||||||
|
window.postMessage(message, window.location.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
recieve = (event: MessageEvent) => {
|
||||||
|
if (event.origin !== window.location.origin) return;
|
||||||
|
const message: Message<UpdateFn> = extractData(event.data);
|
||||||
|
|
||||||
|
(<any>updates[message.fn])(message.args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractData = <F extends UpdateFn>(data: string): Message<F> => {
|
||||||
|
const message = JSON.parse(data);
|
||||||
|
if (!message.fn) {
|
||||||
|
throw new Error("invalid message");
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user