Implement revealable animation on hover
This commit is contained in:
parent
8ad126c8d8
commit
f98c3e58dc
5 changed files with 107 additions and 28 deletions
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -1235,6 +1235,12 @@
|
||||||
"integrity": "sha512-WUjeFT2SXd6intfE6cg6eL1jk/JL88JqM2gC4WqO4iHLmbCvHUq6aoLK13lGpDWs4FtS2PHoYraJZ0dEx99Dyg==",
|
"integrity": "sha512-WUjeFT2SXd6intfE6cg6eL1jk/JL88JqM2gC4WqO4iHLmbCvHUq6aoLK13lGpDWs4FtS2PHoYraJZ0dEx99Dyg==",
|
||||||
"dev": true
|
"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": {
|
"@types/p5": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.3.0.tgz",
|
||||||
|
|
@ -4215,8 +4221,7 @@
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lodash.clone": {
|
"lodash.clone": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/animejs": "^3.1.4",
|
"@types/animejs": "^3.1.4",
|
||||||
|
"@types/lodash": "^4.14.171",
|
||||||
"@types/p5": "^1.3.0",
|
"@types/p5": "^1.3.0",
|
||||||
"parcel-bundler": "^1.12.5",
|
"parcel-bundler": "^1.12.5",
|
||||||
"prettier": "^2.3.2",
|
"prettier": "^2.3.2",
|
||||||
|
|
@ -20,6 +21,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"animejs": "^3.2.1",
|
"animejs": "^3.2.1",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"p5": "^1.4.0",
|
"p5": "^1.4.0",
|
||||||
"rxjs": "^7.2.0",
|
"rxjs": "^7.2.0",
|
||||||
"zustand": "^3.5.7"
|
"zustand": "^3.5.7"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import _ from 'lodash';
|
||||||
import { mp5 } from '../../main';
|
import { mp5 } from '../../main';
|
||||||
import { colors } from '../constants/colors';
|
import { colors } from '../constants/colors';
|
||||||
import { generateRevealableCoords } from '../helpers';
|
import { generateRevealableCoords } from '../helpers';
|
||||||
|
|
@ -15,7 +16,8 @@ export class DetailScene {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.player = new Player();
|
this.player = new Player();
|
||||||
|
|
||||||
store.subscribe((state) => {
|
store.subscribe((state, prevState) => {
|
||||||
|
if (!_.isEqual(state.revealables, prevState.revealables)) {
|
||||||
this.revealables = state.revealables;
|
this.revealables = state.revealables;
|
||||||
this.revealableCoords = generateRevealableCoords();
|
this.revealableCoords = generateRevealableCoords();
|
||||||
this.revealableObjects = this.revealables.map(
|
this.revealableObjects = this.revealables.map(
|
||||||
|
|
@ -26,6 +28,9 @@ export class DetailScene {
|
||||||
w: this.revealables[i].size,
|
w: this.revealables[i].size,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if everything was found, if so, get back to overview screen
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { combineLatest } from 'rxjs';
|
||||||
import { mp5 } from '../../main';
|
import { mp5 } from '../../main';
|
||||||
import { areasColliding, playerHead$, revealedArea$ } from '../area';
|
import { areasColliding, playerHead$, revealedArea$ } from '../area';
|
||||||
import { colors } from '../constants/colors';
|
import { colors } from '../constants/colors';
|
||||||
|
import store from '../store';
|
||||||
import { Area } from '../types';
|
import { Area } from '../types';
|
||||||
|
|
||||||
export enum RevealableTypes {
|
export enum RevealableTypes {
|
||||||
|
|
@ -24,22 +25,42 @@ enum RevealableStates {
|
||||||
HIDDEN = 'HIDDEN',
|
HIDDEN = 'HIDDEN',
|
||||||
REVEALED = 'REVEALED',
|
REVEALED = 'REVEALED',
|
||||||
FOUND = 'FOUND',
|
FOUND = 'FOUND',
|
||||||
|
INACTIVE = 'INACTIVE',
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Revealable {
|
export class Revealable {
|
||||||
state: RevealableStates = RevealableStates.HIDDEN;
|
state: RevealableStates = RevealableStates.HIDDEN;
|
||||||
area: Area;
|
area: Area;
|
||||||
|
|
||||||
|
type: RevealableTypes;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
contents: string;
|
||||||
|
url: string;
|
||||||
|
imageUrl: string;
|
||||||
|
|
||||||
isHovered: boolean;
|
isHovered: boolean;
|
||||||
isRevealed: boolean;
|
isRevealed: boolean;
|
||||||
|
wasInteractedWith: boolean;
|
||||||
|
|
||||||
minSize: number = 5;
|
minSize: number = 5;
|
||||||
currentSize: number;
|
currentSize: number;
|
||||||
maxSize: number;
|
maxSize: number;
|
||||||
|
|
||||||
|
pulseCurrentSize: number;
|
||||||
|
pulseOpacity: number = 255;
|
||||||
|
pulseCountUp: boolean;
|
||||||
|
|
||||||
constructor({ type, name, path, contents, url, imageUrl }: RevealableInterface, area: Area) {
|
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.area = area;
|
||||||
this.currentSize = this.minSize;
|
this.currentSize = this.minSize;
|
||||||
|
this.pulseCurrentSize = area.w;
|
||||||
this.maxSize = area.w;
|
this.maxSize = area.w;
|
||||||
|
|
||||||
combineLatest([revealedArea$, playerHead$]).subscribe(([revealedArea, playerHead]) => {
|
combineLatest([revealedArea$, playerHead$]).subscribe(([revealedArea, playerHead]) => {
|
||||||
|
|
@ -54,6 +75,9 @@ export class Revealable {
|
||||||
w: this.currentSize,
|
w: this.currentSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.wasInteractedWith) {
|
||||||
|
this.state = RevealableStates.INACTIVE;
|
||||||
|
} else {
|
||||||
if (
|
if (
|
||||||
((isRevealed && isHovered) || (!isRevealed && isHovered)) &&
|
((isRevealed && isHovered) || (!isRevealed && isHovered)) &&
|
||||||
(this.state === RevealableStates.REVEALED || this.state === RevealableStates.FOUND)
|
(this.state === RevealableStates.REVEALED || this.state === RevealableStates.FOUND)
|
||||||
|
|
@ -64,6 +88,7 @@ export class Revealable {
|
||||||
} else {
|
} else {
|
||||||
this.state = RevealableStates.HIDDEN;
|
this.state = RevealableStates.HIDDEN;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.isHovered = this.state === RevealableStates.FOUND ? isHovered : false;
|
this.isHovered = this.state === RevealableStates.FOUND ? isHovered : false;
|
||||||
this.isRevealed = isRevealed;
|
this.isRevealed = isRevealed;
|
||||||
|
|
@ -79,14 +104,27 @@ export class Revealable {
|
||||||
mp5.fill(mp5.color(colors.greyLight));
|
mp5.fill(mp5.color(colors.greyLight));
|
||||||
mp5.ellipse(this.area.x, this.area.y, this.currentSize);
|
mp5.ellipse(this.area.x, this.area.y, this.currentSize);
|
||||||
} else if (this.state === RevealableStates.FOUND) {
|
} 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);
|
mp5.ellipse(this.area.x, this.area.y, this.currentSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onClick() {
|
public onClick() {
|
||||||
if (this.isHovered) {
|
if (this.isHovered && !this.wasInteractedWith) {
|
||||||
console.log('Clicked on Revealable');
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,16 @@ const store = create<State>(
|
||||||
infoMessageShown: false,
|
infoMessageShown: false,
|
||||||
infoMessages: [],
|
infoMessages: [],
|
||||||
addInfoMessage: (newMessage) =>
|
addInfoMessage: (newMessage) =>
|
||||||
set((state) => ({ infoMessages: [...state.infoMessages, newMessage] })),
|
set((state) => ({ ...state, infoMessages: [...state.infoMessages, newMessage] })),
|
||||||
userMessages: [],
|
userMessages: [],
|
||||||
addUserMessage: (newMessage) =>
|
addUserMessage: (newMessage) =>
|
||||||
set((state) => ({ userMessages: [...state.userMessages, newMessage] })),
|
set((state) => ({ ...state, userMessages: [...state.userMessages, newMessage] })),
|
||||||
revealables: [],
|
revealables: [],
|
||||||
setProjectMetadata: (projectName) =>
|
setProjectMetadata: (projectName) =>
|
||||||
set({
|
set((state) => ({
|
||||||
|
...state,
|
||||||
revealables: getRevealablesforSubproject(projectName, project.subprojects),
|
revealables: getRevealablesforSubproject(projectName, project.subprojects),
|
||||||
}),
|
})),
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Reference in a new issue