Implement edge highlighting and finished state
This commit is contained in:
parent
3633b32b3f
commit
748e1e0739
8 changed files with 141 additions and 11 deletions
|
|
@ -23,8 +23,12 @@
|
||||||
<button type="button" id="message-confirm">Weiter</button>
|
<button type="button" id="message-confirm">Weiter</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="info-message">
|
<div id="info-message">
|
||||||
|
<div id="info-message-header">
|
||||||
<h2 id="info-message-headline">Test</h2>
|
<h2 id="info-message-headline">Test</h2>
|
||||||
|
<img id="info-message-img" src="" />
|
||||||
|
</div>
|
||||||
<div id="info-message-contents">Test</div>
|
<div id="info-message-contents">Test</div>
|
||||||
|
<a id="info-message-link" href="" target="_blank">Check this out on Github</a>
|
||||||
<div id="info-message-close">
|
<div id="info-message-close">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||||
<path
|
<path
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,10 @@ export class DetailScene {
|
||||||
this.revealableObjects.every((revObj) => revObj.wasInteractedWith) &&
|
this.revealableObjects.every((revObj) => revObj.wasInteractedWith) &&
|
||||||
!(store.getState().companionState === CompanionState.ACTIVE)
|
!(store.getState().companionState === CompanionState.ACTIVE)
|
||||||
) {
|
) {
|
||||||
|
store.setState((state) => ({
|
||||||
|
finishedSubProjects: [...state.finishedSubProjects, state.currentSubproject],
|
||||||
|
}));
|
||||||
|
|
||||||
store.getState().addUserMessage({
|
store.getState().addUserMessage({
|
||||||
text: "Yaay! You've found all of the important parts of this part of the repository. You will be returned to the subproject overview now. Pick the next subproject you want to take a look at there.",
|
text: "Yaay! You've found all of the important parts of this part of the repository. You will be returned to the subproject overview now. Pick the next subproject you want to take a look at there.",
|
||||||
inputWanted: false,
|
inputWanted: false,
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,12 @@ import store from '../store';
|
||||||
import { generateEdges } from '../helpers';
|
import { generateEdges } from '../helpers';
|
||||||
import { Scenes } from './scenes';
|
import { Scenes } from './scenes';
|
||||||
import projectMetadata from '../../metadata/project.json';
|
import projectMetadata from '../../metadata/project.json';
|
||||||
|
import { playerHead$ } from '../area';
|
||||||
|
import { Area } from '../types';
|
||||||
|
|
||||||
export class OverviewScene {
|
export class OverviewScene {
|
||||||
player: Player;
|
player: Player;
|
||||||
|
playerHead: Area;
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -29,12 +32,18 @@ export class OverviewScene {
|
||||||
const dist = mp5.dist(mp5.mouseX, mp5.mouseY, edge.x, edge.y);
|
const dist = mp5.dist(mp5.mouseX, mp5.mouseY, edge.x, edge.y);
|
||||||
if (dist < edge.r) {
|
if (dist < edge.r) {
|
||||||
store.getState().setProjectMetadata(edge.name);
|
store.getState().setProjectMetadata(edge.name);
|
||||||
store.setState({ currentScene: Scenes.DETAIL });
|
store.setState({ currentSubproject: edge.name, currentScene: Scenes.DETAIL });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawLocations() {
|
private drawLocations() {
|
||||||
this.edges.forEach((edgeShape) => edgeShape.draw());
|
this.edges.forEach((edgeShape) => {
|
||||||
|
if (store.getState().finishedSubProjects.some((fsp) => fsp === edgeShape.name)) {
|
||||||
|
edgeShape.finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeShape.draw();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { mp5 } from '../../main';
|
import { mp5 } from '../../main';
|
||||||
|
import { areasColliding, playerHead$ } from '../area';
|
||||||
import { colors } from '../constants/colors';
|
import { colors } from '../constants/colors';
|
||||||
|
|
||||||
export class Edge {
|
export class Edge {
|
||||||
|
|
@ -7,18 +8,58 @@ export class Edge {
|
||||||
r: number;
|
r: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
currentSize: number;
|
||||||
|
maxSize: number;
|
||||||
|
isHovered: boolean;
|
||||||
|
hoverColor: any;
|
||||||
|
|
||||||
|
finished: boolean;
|
||||||
|
|
||||||
constructor({ x, y, r, name }: { x: number; y: number; r: number; name: string }) {
|
constructor({ x, y, r, name }: { x: number; y: number; r: number; name: string }) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.maxSize = r * 2 + 20;
|
||||||
|
this.currentSize = r * 2;
|
||||||
|
this.hoverColor = mp5.color(colors.red);
|
||||||
|
this.hoverColor.setAlpha(200);
|
||||||
|
|
||||||
|
playerHead$.subscribe((playerHead) => {
|
||||||
|
this.isHovered = areasColliding(playerHead, { x: this.x, y: this.y, w: this.r * 2 });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
mp5.fill(mp5.color(colors.grey));
|
if (this.finished) {
|
||||||
|
mp5.fill(mp5.color(colors.greyLight));
|
||||||
mp5.ellipse(this.x, this.y, this.r * 2);
|
mp5.ellipse(this.x, this.y, this.r * 2);
|
||||||
mp5.textSize(20);
|
mp5.textSize(20);
|
||||||
mp5.fill(0);
|
mp5.fill(mp5.color(colors.grey));
|
||||||
mp5.text(this.name, this.x, this.y);
|
mp5.text(`packages/${this.name}`, this.x - this.r / 2, this.y);
|
||||||
|
} else {
|
||||||
|
mp5.fill(mp5.color(colors.grey));
|
||||||
|
|
||||||
|
if (this.isHovered) {
|
||||||
|
mp5.fill(mp5.color(this.hoverColor));
|
||||||
|
|
||||||
|
if (this.currentSize < this.maxSize) {
|
||||||
|
this.currentSize++;
|
||||||
|
} else {
|
||||||
|
this.currentSize = this.maxSize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.currentSize > this.r * 2) {
|
||||||
|
this.currentSize--;
|
||||||
|
} else {
|
||||||
|
this.currentSize = this.r * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp5.ellipse(this.x, this.y, this.currentSize);
|
||||||
|
mp5.textSize(20);
|
||||||
|
mp5.fill(mp5.color(colors.black));
|
||||||
|
mp5.text(`packages/${this.name}`, this.x - this.r / 2, this.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,8 @@ export class Revealable {
|
||||||
store.getState().addInfoMessage({
|
store.getState().addInfoMessage({
|
||||||
headline: this.name,
|
headline: this.name,
|
||||||
innerHTML: this.contents,
|
innerHTML: this.contents,
|
||||||
|
imgUrl: this.imageUrl,
|
||||||
|
url: this.url,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ import project from '../metadata/project.json';
|
||||||
import { InfoMessageType } from './ui/info';
|
import { InfoMessageType } from './ui/info';
|
||||||
import { RevealableInterface, RevealableTypes } from './sketchObjects/Revealable';
|
import { RevealableInterface, RevealableTypes } from './sketchObjects/Revealable';
|
||||||
import { getRevealablesforSubproject } from './helpers';
|
import { getRevealablesforSubproject } from './helpers';
|
||||||
|
import { SubProject } from './types';
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
currentScene: Scenes;
|
currentScene: Scenes;
|
||||||
|
currentSubproject?: string;
|
||||||
companionState: CompanionState;
|
companionState: CompanionState;
|
||||||
infoMessageShown: boolean;
|
infoMessageShown: boolean;
|
||||||
infoMessages: InfoMessageType[];
|
infoMessages: InfoMessageType[];
|
||||||
|
|
@ -16,12 +18,14 @@ export interface State {
|
||||||
userMessages: CompanionMessage[];
|
userMessages: CompanionMessage[];
|
||||||
addUserMessage: (newMessage: CompanionMessage) => void;
|
addUserMessage: (newMessage: CompanionMessage) => void;
|
||||||
revealables: RevealableInterface[];
|
revealables: RevealableInterface[];
|
||||||
|
finishedSubProjects: string[];
|
||||||
setProjectMetadata: (projectName: string) => void;
|
setProjectMetadata: (projectName: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = create<State>(
|
const store = create<State>(
|
||||||
devtools((set) => ({
|
devtools((set) => ({
|
||||||
currentScene: Scenes.OVERVIEW,
|
currentScene: Scenes.OVERVIEW,
|
||||||
|
currentSubproject: null,
|
||||||
companionState: CompanionState.IDLE,
|
companionState: CompanionState.IDLE,
|
||||||
infoMessageShown: false,
|
infoMessageShown: false,
|
||||||
infoMessages: [],
|
infoMessages: [],
|
||||||
|
|
@ -33,6 +37,7 @@ const store = create<State>(
|
||||||
userMessages: [...state.userMessages, newMessage],
|
userMessages: [...state.userMessages, newMessage],
|
||||||
})),
|
})),
|
||||||
revealables: [],
|
revealables: [],
|
||||||
|
finishedSubProjects: [],
|
||||||
setProjectMetadata: (projectName) =>
|
setProjectMetadata: (projectName) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
...state,
|
...state,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ import store from '../store';
|
||||||
export interface InfoMessageType {
|
export interface InfoMessageType {
|
||||||
headline: string;
|
headline: string;
|
||||||
innerHTML: string;
|
innerHTML: string;
|
||||||
|
imgUrl?: string;
|
||||||
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InfoMessage {
|
export class InfoMessage {
|
||||||
|
|
@ -10,6 +12,8 @@ export class InfoMessage {
|
||||||
infoMessageHeadline: HTMLElement;
|
infoMessageHeadline: HTMLElement;
|
||||||
infoMessageContents: HTMLElement;
|
infoMessageContents: HTMLElement;
|
||||||
infoMessageClose: HTMLElement;
|
infoMessageClose: HTMLElement;
|
||||||
|
infoMessageImgRef: HTMLImageElement;
|
||||||
|
infoMessageLinkRef: HTMLAnchorElement;
|
||||||
backdrop: HTMLElement;
|
backdrop: HTMLElement;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -17,6 +21,8 @@ export class InfoMessage {
|
||||||
this.infoMessageHeadline = document.getElementById('info-message-headline');
|
this.infoMessageHeadline = document.getElementById('info-message-headline');
|
||||||
this.infoMessageContents = document.getElementById('info-message-contents');
|
this.infoMessageContents = document.getElementById('info-message-contents');
|
||||||
this.infoMessageClose = document.getElementById('info-message-close');
|
this.infoMessageClose = document.getElementById('info-message-close');
|
||||||
|
this.infoMessageImgRef = document.getElementById('info-message-img') as HTMLImageElement;
|
||||||
|
this.infoMessageLinkRef = document.getElementById('info-message-link') as HTMLAnchorElement;
|
||||||
this.backdrop = document.getElementById('backdrop');
|
this.backdrop = document.getElementById('backdrop');
|
||||||
|
|
||||||
this.backdrop.addEventListener('click', this.onBackdropClick);
|
this.backdrop.addEventListener('click', this.onBackdropClick);
|
||||||
|
|
@ -32,6 +38,19 @@ export class InfoMessage {
|
||||||
if (state.infoMessages.length > prevState.infoMessages.length) {
|
if (state.infoMessages.length > prevState.infoMessages.length) {
|
||||||
const newMessage = state.infoMessages[state.infoMessages.length - 1];
|
const newMessage = state.infoMessages[state.infoMessages.length - 1];
|
||||||
this.setContents(newMessage.headline, newMessage.innerHTML);
|
this.setContents(newMessage.headline, newMessage.innerHTML);
|
||||||
|
|
||||||
|
if (newMessage.imgUrl) {
|
||||||
|
this.setImg(newMessage.imgUrl);
|
||||||
|
} else {
|
||||||
|
this.infoMessageImgRef.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newMessage.url) {
|
||||||
|
this.setLink(newMessage.url);
|
||||||
|
} else {
|
||||||
|
this.infoMessageLinkRef.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
store.setState({ infoMessageShown: true });
|
store.setState({ infoMessageShown: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -42,6 +61,16 @@ export class InfoMessage {
|
||||||
this.infoMessageContents.innerHTML = innerHTML;
|
this.infoMessageContents.innerHTML = innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setImg(imgUrl: string) {
|
||||||
|
this.infoMessageImgRef.src = imgUrl;
|
||||||
|
this.infoMessageImgRef.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
private setLink(url: string) {
|
||||||
|
this.infoMessageLinkRef.href = url;
|
||||||
|
this.infoMessageLinkRef.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
private show() {
|
private show() {
|
||||||
this.infoMessage.style.display = 'block';
|
this.infoMessage.style.display = 'block';
|
||||||
this.backdrop.style.display = 'block';
|
this.backdrop.style.display = 'block';
|
||||||
|
|
|
||||||
38
styles.scss
38
styles.scss
|
|
@ -149,6 +149,8 @@ button {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
min-width: 60%;
|
||||||
|
max-width: 90%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 2.5rem 4rem;
|
padding: 2.5rem 4rem;
|
||||||
|
|
@ -157,11 +159,45 @@ button {
|
||||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
|
||||||
var(--tw-shadow);
|
var(--tw-shadow);
|
||||||
|
|
||||||
|
#info-message-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-end;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
|
||||||
|
#info-message-img {
|
||||||
|
display: none;
|
||||||
|
height: 100px;
|
||||||
|
width: 100px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
#info-message-headline {
|
#info-message-headline {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-bottom: 1rem;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#info-message-link {
|
||||||
|
display: none;
|
||||||
|
min-width: 20%;
|
||||||
|
padding: 6px 15px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 2px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 50px;
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#info-message-close {
|
#info-message-close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
Reference in a new issue