Skip to content

Commit

Permalink
[Issue 92] implement animated graphic to indicate the distance from t…
Browse files Browse the repository at this point in the history
…he target for the latest click
  • Loading branch information
silentDjay committed Nov 2, 2024
1 parent 158184a commit 48df829
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 13 deletions.
8 changes: 4 additions & 4 deletions src/components/ClickMarker.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from "react";
import React, { useEffect, useState } from "react";

export const ClickMarker: React.FC<
google.maps.marker.AdvancedMarkerViewOptions
> = (markerProps) => {
const [marker, setMarker] =
React.useState<google.maps.marker.AdvancedMarkerViewOptions>();
useState<google.maps.marker.AdvancedMarkerViewOptions>();

React.useEffect(() => {
useEffect(() => {
// remove marker from map on unmount
return () => {
if (marker) {
Expand All @@ -15,7 +15,7 @@ export const ClickMarker: React.FC<
};
}, [marker]);

React.useEffect(() => {
useEffect(() => {
if (!marker) {
setMarker(new google.maps.marker.AdvancedMarkerView(markerProps));
}
Expand Down
64 changes: 56 additions & 8 deletions src/components/GameplayMap.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React from "react";
import React, {
Children,
isValidElement,
useEffect,
useRef,
useState,
} from "react";

import { getMapOptions, initialMapProps } from "../utils";
import { GameStatus, Country, GameCategory } from "../types";
Expand All @@ -7,6 +13,8 @@ interface GameplayMapProps extends google.maps.MapOptions {
gameStatus: GameStatus;
gameCategory?: GameCategory;
targetCountryData?: Country;
distanceFromTarget?: number;
latestClickCoordinates?: google.maps.LatLng;
style: { [key: string]: string };
onClick: (e: google.maps.MapMouseEvent) => void;
children?: React.ReactNode;
Expand All @@ -16,15 +24,17 @@ export const GameplayMap: React.FC<GameplayMapProps> = ({
gameStatus,
gameCategory,
targetCountryData,
distanceFromTarget,
latestClickCoordinates,
onClick,
children,
style,
...props
}) => {
const ref = React.useRef<HTMLDivElement>(null);
const [map, setMap] = React.useState<google.maps.Map>();
const ref = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<google.maps.Map>();

React.useEffect(() => {
useEffect(() => {
if (ref.current && !map) {
setMap(
new window.google.maps.Map(ref.current, {
Expand All @@ -35,7 +45,7 @@ export const GameplayMap: React.FC<GameplayMapProps> = ({
}
}, [ref, map, props.zoom, props.center]);

React.useEffect(() => {
useEffect(() => {
if (!!map) {
["click"].forEach((eventName) =>
google.maps.event.clearListeners(map, eventName)
Expand All @@ -47,18 +57,56 @@ export const GameplayMap: React.FC<GameplayMapProps> = ({
}
}, [map, onClick]);

React.useEffect(() => {
useEffect(() => {
if (!!map && !!targetCountryData)
map.setOptions(
getMapOptions(gameStatus, gameCategory, targetCountryData)
);
}, [map, targetCountryData, gameStatus]);

useEffect(() => {
if (!!map && !!distanceFromTarget && !!latestClickCoordinates) {
const initialRadius = 0;

const clickDistanceIndicator = new google.maps.Circle({
center: latestClickCoordinates,
radius: initialRadius, // meters
map: map,
strokeColor: "yellow",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "yellow",
fillOpacity: 0.1,
});

let currentRadius = initialRadius;
const maxRadius = distanceFromTarget * 1000; // meters

const growthRate = 22000 / (map.getZoom() || 1); // Amount to increase the radius per interval (in meters)
const intervalRate = 5; // milliseconds

const timeRequiredToFillCircle = (maxRadius / growthRate) * intervalRate;

const intervalId = setInterval(() => {
if (currentRadius < maxRadius) {
currentRadius += growthRate;
clickDistanceIndicator.setRadius(currentRadius);
} else {
clearInterval(intervalId); // Stop expanding when max radius is reached
}
}, intervalRate); // Adjust interval duration for speed (in milliseconds)

setTimeout(() => {
clickDistanceIndicator.setMap(null);
}, 500 + timeRequiredToFillCircle); // delete the circle shortly after expanding to the full radius
}
}, [map, distanceFromTarget, latestClickCoordinates]);

return (
<>
<div ref={ref} style={style} />
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
{Children.map(children, (child) => {
if (isValidElement(child)) {
// set the map prop on the child component
// @ts-ignore
return React.cloneElement(child, { map });
Expand Down
25 changes: 24 additions & 1 deletion src/components/PlayGame.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { Wrapper, Status } from "@googlemaps/react-wrapper";
import Geonames from "geonames.js";
import posthog from "posthog-js";
Expand Down Expand Up @@ -36,6 +36,7 @@ import {
getFilteredCountryList,
captureEvent,
isCampaignCompleted,
getClickDistanceFromTarget,
} from "../utils";

posthog.init(POSTHOG_API_KEY, { api_host: "https://us.posthog.com" });
Expand Down Expand Up @@ -68,6 +69,26 @@ export const PlayGame: React.FC = () => {
const [gameStatus, setGameStatus] = useState<GameStatus>("PENDING");
const [clicks, setClicks] = useState<Click[]>([]);
const [revealedHintCount, setRevealedHintCount] = useState(1);
const [latestClickDistance, setLatestClickDistance] = useState<number>();
const [latestClickCoords, setLatestClickCoords] =
useState<google.maps.LatLng>();

useEffect(() => {
const totalClicks = clicks?.length;

const lastClickData = clicks[totalClicks - 1];

if (!!lastClickData && !!targetCountryData) {
setLatestClickDistance(
getClickDistanceFromTarget(
targetCountryData.latlng,
lastClickData.coordinates
)
);

setLatestClickCoords(lastClickData.coordinates);
}
}, [clicks]);

const handleSetNewTargetCountry = (category: GameCategory) => {
const randomCountryData = getRandomCountryData(
Expand Down Expand Up @@ -359,6 +380,8 @@ export const PlayGame: React.FC = () => {
gameStatus={gameStatus}
gameCategory={gameCategory}
targetCountryData={targetCountryData}
distanceFromTarget={latestClickDistance}
latestClickCoordinates={latestClickCoords}
>
{clicks.map((click, index) => (
<ClickMarker
Expand Down

0 comments on commit 48df829

Please sign in to comment.