From f98c3e58dc28f9d2f374eff146745911ec3b0168 Mon Sep 17 00:00:00 2001 From: Dennis Schoepf Date: Sun, 1 Aug 2021 17:38:38 +0200 Subject: [PATCH] Implement revealable animation on hover --- package-lock.json | 9 +++- package.json | 2 + src/scenes/DetailScene.ts | 27 +++++----- src/sketchObjects/Revealable.ts | 88 ++++++++++++++++++++++++++++----- src/store.ts | 9 ++-- 5 files changed, 107 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index 60fb884..f151f38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index d9a9c4c..1ab2615 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/scenes/DetailScene.ts b/src/scenes/DetailScene.ts index adff5f0..f7e3c2c 100644 --- a/src/scenes/DetailScene.ts +++ b/src/scenes/DetailScene.ts @@ -1,3 +1,4 @@ +import _ from 'lodash'; import { mp5 } from '../../main'; import { colors } from '../constants/colors'; import { generateRevealableCoords } from '../helpers'; @@ -15,17 +16,21 @@ export class DetailScene { constructor() { this.player = new Player(); - store.subscribe((state) => { - this.revealables = state.revealables; - this.revealableCoords = generateRevealableCoords(); - this.revealableObjects = this.revealables.map( - (revealable, i) => - new Revealable(revealable, { - x: this.revealableCoords[i].x, - y: this.revealableCoords[i].y, - w: this.revealables[i].size, - }) - ); + store.subscribe((state, prevState) => { + if (!_.isEqual(state.revealables, prevState.revealables)) { + this.revealables = state.revealables; + this.revealableCoords = generateRevealableCoords(); + this.revealableObjects = this.revealables.map( + (revealable, i) => + new Revealable(revealable, { + x: this.revealableCoords[i].x, + y: this.revealableCoords[i].y, + w: this.revealables[i].size, + }) + ); + } + + // TODO: Check if everything was found, if so, get back to overview screen }); } diff --git a/src/sketchObjects/Revealable.ts b/src/sketchObjects/Revealable.ts index f10cc8e..c48f050 100644 --- a/src/sketchObjects/Revealable.ts +++ b/src/sketchObjects/Revealable.ts @@ -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,15 +75,19 @@ export class Revealable { w: this.currentSize, }); - if ( - ((isRevealed && isHovered) || (!isRevealed && isHovered)) && - (this.state === RevealableStates.REVEALED || this.state === RevealableStates.FOUND) - ) { - this.state = RevealableStates.FOUND; - } else if (isRevealed && !isHovered) { - this.state = RevealableStates.REVEALED; + if (this.wasInteractedWith) { + this.state = RevealableStates.INACTIVE; } else { - this.state = RevealableStates.HIDDEN; + if ( + ((isRevealed && isHovered) || (!isRevealed && isHovered)) && + (this.state === RevealableStates.REVEALED || this.state === RevealableStates.FOUND) + ) { + this.state = RevealableStates.FOUND; + } else if (isRevealed && !isHovered) { + this.state = RevealableStates.REVEALED; + } else { + this.state = RevealableStates.HIDDEN; + } } this.isHovered = this.state === RevealableStates.FOUND ? isHovered : false; @@ -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); + } } diff --git a/src/store.ts b/src/store.ts index d10ae4a..f8d61dd 100644 --- a/src/store.ts +++ b/src/store.ts @@ -26,15 +26,16 @@ const store = create( 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), - }), + })), })) );