Flocking birds are animated
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
4c33dfcdf1
commit
abbbbafad4
BIN
client/public/assets/gfx/birds.png
Executable file
BIN
client/public/assets/gfx/birds.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
@ -27,6 +27,7 @@ import { Trade } from "./Trade.js";
|
|||||||
import { Winner } from "./Winner.js";
|
import { Winner } from "./Winner.js";
|
||||||
import { HouseRules } from "./HouseRules.js";
|
import { HouseRules } from "./HouseRules.js";
|
||||||
import { Dice } from "./Dice.js";
|
import { Dice } from "./Dice.js";
|
||||||
|
import { Flock } from "./Bird.js";
|
||||||
|
|
||||||
import history from "./history.js";
|
import history from "./history.js";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
@ -332,6 +333,7 @@ const Table = () => {
|
|||||||
return <GlobalContext.Provider value={global}>
|
return <GlobalContext.Provider value={global}>
|
||||||
{ /* <PingPong/> */ }
|
{ /* <PingPong/> */ }
|
||||||
<div className="Table">
|
<div className="Table">
|
||||||
|
<Flock count={15}/>
|
||||||
<div className="ActivitiesBox">
|
<div className="ActivitiesBox">
|
||||||
<Activities/>
|
<Activities/>
|
||||||
{ dice && dice.length && <div className="DiceRoll">
|
{ dice && dice.length && <div className="DiceRoll">
|
||||||
|
16
client/src/Bird.css
Normal file
16
client/src/Bird.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.Flock {
|
||||||
|
z-index: 50;
|
||||||
|
position: relative;
|
||||||
|
top: 5rem;
|
||||||
|
left: 10rem;
|
||||||
|
width: 20rem;
|
||||||
|
height: 20rem;
|
||||||
|
border: 1px solid purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Bird {
|
||||||
|
z-index: 50; /* Above Tile (5,10), Road (12), Corner (20) */
|
||||||
|
position: absolute;
|
||||||
|
background-size: 1200% auto;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
105
client/src/Bird.js
Normal file
105
client/src/Bird.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import React, { useEffect, useState, useCallback } from "react";
|
||||||
|
import { assetsPath } from "./Common.js";
|
||||||
|
import "./Bird.css";
|
||||||
|
|
||||||
|
const
|
||||||
|
birdAngles = 12,
|
||||||
|
birdAnimations = 4;
|
||||||
|
const frames = [0, 1, 2, 3, 3, 3, 2, 1, 0, 0, 0];
|
||||||
|
|
||||||
|
const useAnimationFrame = callback => {
|
||||||
|
// Use useRef for mutable variables that we want to persist
|
||||||
|
// without triggering a re-render on their change
|
||||||
|
const requestRef = React.useRef();
|
||||||
|
|
||||||
|
const animate = time => {
|
||||||
|
callback(time)
|
||||||
|
requestRef.current = requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
requestRef.current = requestAnimationFrame(animate);
|
||||||
|
return () => cancelAnimationFrame(requestRef.current);
|
||||||
|
}, []); // Make sure the effect runs only once
|
||||||
|
}
|
||||||
|
|
||||||
|
const Bird = ({ origin, radius, speed, angle, size, style }) => {
|
||||||
|
const [time, setTime] = useState(0);
|
||||||
|
const [rotation] = useState(Math.PI * 2 * radius / speed);
|
||||||
|
const [direction] = useState(Math.floor(birdAngles * (angle ? angle : 0) / 360.));
|
||||||
|
const [cell, setCell] = useState(0);
|
||||||
|
|
||||||
|
const previousTimeRef = React.useRef();
|
||||||
|
|
||||||
|
useAnimationFrame(time => {
|
||||||
|
if (previousTimeRef.current !== undefined) {
|
||||||
|
const deltaTime = time - previousTimeRef.current;
|
||||||
|
setTime(deltaTime);
|
||||||
|
} else {
|
||||||
|
previousTimeRef.current = time;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const alpha = (time % speed) / speed;
|
||||||
|
const frame = Math.floor(frames.length * alpha);
|
||||||
|
console.log({ alpha, frame });
|
||||||
|
setCell(frames[Math.floor(frame)]);
|
||||||
|
}, [time, setCell, speed]);
|
||||||
|
|
||||||
|
return <div className={`Bird`}
|
||||||
|
style={{
|
||||||
|
//top: ${origin.x + radius * }
|
||||||
|
width: `${size * 64}px`,
|
||||||
|
height: `${size * 64}px`,
|
||||||
|
backgroundImage: `url(${assetsPath}/gfx/birds.png)`,
|
||||||
|
backgroundPositionX: `${100 * direction / 11}%`,
|
||||||
|
backgroundPositionY: `${100 * cell / 3}%`,
|
||||||
|
transform: `translate(-50%, -50%)`,
|
||||||
|
...style
|
||||||
|
}}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Flock = ({count}) => {
|
||||||
|
const [birds, setBirds] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
const tmp = [];
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
tmp.push(<Bird
|
||||||
|
speed={500 + Math.random() * 1000}
|
||||||
|
size={0.2 + Math.random() * 0.15}
|
||||||
|
angle={Math.random() * 360}
|
||||||
|
key={i}
|
||||||
|
style={{
|
||||||
|
left: `${Math.random() * 100}%`,
|
||||||
|
top: `${ Math.random() * 100}%`,
|
||||||
|
}}/>)
|
||||||
|
}
|
||||||
|
tmp.push(<Bird
|
||||||
|
speed={500 + Math.random() * 1000}
|
||||||
|
size={0.2 + Math.random() * 0.15}
|
||||||
|
angle={Math.random() * 360}
|
||||||
|
key={count+1}
|
||||||
|
style={{
|
||||||
|
left: `0%`,
|
||||||
|
top: `0%`,
|
||||||
|
border: `1px solid orange`
|
||||||
|
}}/>);
|
||||||
|
tmp.push(<Bird
|
||||||
|
speed={500 + Math.random() * 1000}
|
||||||
|
size={0.2 + Math.random() * 0.15}
|
||||||
|
angle={Math.random() * 360}
|
||||||
|
key={count + 2}
|
||||||
|
style={{
|
||||||
|
left: `100%`,
|
||||||
|
top: `100%`,
|
||||||
|
border: `1px solid green`
|
||||||
|
}} />);
|
||||||
|
setBirds(tmp);
|
||||||
|
}, [count, setBirds]);
|
||||||
|
|
||||||
|
return <div className="Flock">{ birds }</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Bird, Flock };
|
BIN
original/birds.png
Executable file
BIN
original/birds.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
BIN
original/birds.xcf
Executable file
BIN
original/birds.xcf
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user