Implement revealable animation on hover

This commit is contained in:
Dennis Schoepf 2021-08-01 17:38:38 +02:00
parent 8ad126c8d8
commit f98c3e58dc
5 changed files with 107 additions and 28 deletions

9
package-lock.json generated
View file

@ -1235,6 +1235,12 @@
"integrity": "sha512-WUjeFT2SXd6intfE6cg6eL1jk/JL88JqM2gC4WqO4iHLmbCvHUq6aoLK13lGpDWs4FtS2PHoYraJZ0dEx99Dyg==",
"dev": true
},
"@types/lodash": {
"version": "4.14.171",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz",
"integrity": "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==",
"dev": true
},
"@types/p5": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.3.0.tgz",
@ -4215,8 +4221,7 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.clone": {
"version": "4.5.0",

View file

@ -12,6 +12,7 @@
},
"devDependencies": {
"@types/animejs": "^3.1.4",
"@types/lodash": "^4.14.171",
"@types/p5": "^1.3.0",
"parcel-bundler": "^1.12.5",
"prettier": "^2.3.2",
@ -20,6 +21,7 @@
},
"dependencies": {
"animejs": "^3.2.1",
"lodash": "^4.17.21",
"p5": "^1.4.0",
"rxjs": "^7.2.0",
"zustand": "^3.5.7"

View file

@ -1,3 +1,4 @@
import _ from 'lodash';
import { mp5 } from '../../main';
import { colors } from '../constants/colors';
import { generateRevealableCoords } from '../helpers';
@ -15,7 +16,8 @@ export class DetailScene {
constructor() {
this.player = new Player();
store.subscribe((state) => {
store.subscribe((state, prevState) => {
if (!_.isEqual(state.revealables, prevState.revealables)) {
this.revealables = state.revealables;
this.revealableCoords = generateRevealableCoords();
this.revealableObjects = this.revealables.map(
@ -26,6 +28,9 @@ export class DetailScene {
w: this.revealables[i].size,
})
);
}
// TODO: Check if everything was found, if so, get back to overview screen
});
}

View file

@ -2,6 +2,7 @@ import { combineLatest } from 'rxjs';
import { mp5 } from '../../main';
import { areasColliding, playerHead$, revealedArea$ } from '../area';
import { colors } from '../constants/colors';
import store from '../store';
import { Area } from '../types';
export enum RevealableTypes {
@ -24,22 +25,42 @@ enum RevealableStates {
HIDDEN = 'HIDDEN',
REVEALED = 'REVEALED',
FOUND = 'FOUND',
INACTIVE = 'INACTIVE',
}
export class Revealable {
state: RevealableStates = RevealableStates.HIDDEN;
area: Area;
type: RevealableTypes;
name: string;
path: string;
contents: string;
url: string;
imageUrl: string;
isHovered: boolean;
isRevealed: boolean;
wasInteractedWith: boolean;
minSize: number = 5;
currentSize: number;
maxSize: number;
pulseCurrentSize: number;
pulseOpacity: number = 255;
pulseCountUp: boolean;
constructor({ type, name, path, contents, url, imageUrl }: RevealableInterface, area: Area) {
this.type = type;
this.name = name;
this.path = path;
this.contents = contents;
this.url = url;
this.imageUrl = imageUrl;
this.area = area;
this.currentSize = this.minSize;
this.pulseCurrentSize = area.w;
this.maxSize = area.w;
combineLatest([revealedArea$, playerHead$]).subscribe(([revealedArea, playerHead]) => {
@ -54,6 +75,9 @@ export class Revealable {
w: this.currentSize,
});
if (this.wasInteractedWith) {
this.state = RevealableStates.INACTIVE;
} else {
if (
((isRevealed && isHovered) || (!isRevealed && isHovered)) &&
(this.state === RevealableStates.REVEALED || this.state === RevealableStates.FOUND)
@ -64,6 +88,7 @@ export class Revealable {
} else {
this.state = RevealableStates.HIDDEN;
}
}
this.isHovered = this.state === RevealableStates.FOUND ? isHovered : false;
this.isRevealed = isRevealed;
@ -79,14 +104,27 @@ export class Revealable {
mp5.fill(mp5.color(colors.greyLight));
mp5.ellipse(this.area.x, this.area.y, this.currentSize);
} else if (this.state === RevealableStates.FOUND) {
mp5.fill(mp5.color(colors.redDark));
this.pulsate();
mp5.fill(mp5.color(colors.red));
mp5.ellipse(this.area.x, this.area.y, this.currentSize);
} else if (this.state === RevealableStates.INACTIVE) {
this.reduceSize();
mp5.fill(mp5.color(colors.greyDark));
mp5.ellipse(this.area.x, this.area.y, this.currentSize);
}
}
public onClick() {
if (this.isHovered) {
if (this.isHovered && !this.wasInteractedWith) {
console.log('Clicked on Revealable');
this.wasInteractedWith = true;
store.getState().addInfoMessage({
headline: this.name,
innerHTML: this.contents,
});
}
}
@ -106,5 +144,33 @@ export class Revealable {
}
}
private pulsate() {}
private pulsate() {
const minPulse = this.currentSize;
const maxPulse = this.currentSize + 40;
let color: any = mp5.color(colors.red);
console.log(this.pulseCurrentSize, this.pulseCountUp);
if (this.pulseCountUp) {
this.pulseCurrentSize += 1;
this.pulseOpacity = this.pulseOpacity > 255 ? 255 : this.pulseOpacity - 6;
if (this.pulseCurrentSize > maxPulse) {
this.pulseCountUp = false;
}
} else {
this.pulseCurrentSize -= 6;
if (this.pulseCurrentSize < minPulse) {
this.pulseCountUp = true;
this.pulseOpacity = 255;
}
}
color.setAlpha(this.pulseOpacity);
mp5.fill(mp5.color(color));
mp5.ellipse(this.area.x, this.area.y, this.pulseCurrentSize);
}
}

View file

@ -26,15 +26,16 @@ const store = create<State>(
infoMessageShown: false,
infoMessages: [],
addInfoMessage: (newMessage) =>
set((state) => ({ infoMessages: [...state.infoMessages, newMessage] })),
set((state) => ({ ...state, infoMessages: [...state.infoMessages, newMessage] })),
userMessages: [],
addUserMessage: (newMessage) =>
set((state) => ({ userMessages: [...state.userMessages, newMessage] })),
set((state) => ({ ...state, userMessages: [...state.userMessages, newMessage] })),
revealables: [],
setProjectMetadata: (projectName) =>
set({
set((state) => ({
...state,
revealables: getRevealablesforSubproject(projectName, project.subprojects),
}),
})),
}))
);