diff --git a/Gyroscope/src/components/Images/basketball.svg b/Gyroscope/src/components/Images/basketball.svg new file mode 100644 index 00000000..0f7d2cdd --- /dev/null +++ b/Gyroscope/src/components/Images/basketball.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Gyroscope/src/components/Images/bb-hoop.svg b/Gyroscope/src/components/Images/bb-hoop.svg new file mode 100644 index 00000000..20e509ec --- /dev/null +++ b/Gyroscope/src/components/Images/bb-hoop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Gyroscope/src/components/InstructionModal.tsx b/Gyroscope/src/components/InstructionModal.tsx new file mode 100644 index 00000000..5ecc0933 --- /dev/null +++ b/Gyroscope/src/components/InstructionModal.tsx @@ -0,0 +1,45 @@ +import React, { useEffect } from "react" +import Modal from "react-bootstrap/Modal" +import { Row, Col, Button } from "react-bootstrap" +import "../containers/layout.css" +import i18n from "../i18n" + +const InstructionModal = ({...props}) => { + + useEffect(() => { + const listener = (event: any) => { + if (event.code === "Enter" || event.code === "NumpadEnter") { + event.preventDefault(); + props.onHide() + } + }; + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, [props.message]); + + return ( + + + mindLamp + + + + +

{props.message}

+ +
+
+ +
+ +
+
+
+ ) +} + +export default InstructionModal diff --git a/Gyroscope/src/containers/GameComponent.tsx b/Gyroscope/src/containers/GameComponent.tsx index fe9ddbcb..3646a380 100644 --- a/Gyroscope/src/containers/GameComponent.tsx +++ b/Gyroscope/src/containers/GameComponent.tsx @@ -11,6 +11,8 @@ import { } from "@material-ui/core"; import { isMobile } from "react-device-detect"; import GameEnd from "./GameEnd"; +import hoop from "../components/Images/bb-hoop.svg"; +import basketBall from "../components/Images/basketball.svg"; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -55,9 +57,9 @@ const useStyles = makeStyles((theme: Theme) => width: "22px", height: "22px", background: "red", - position: "absolute", - top: "25%", - left: "25%", + position: "relative", + // top: "25%", + // left: "25%", borderRadius: "50%", }, btnblue: { @@ -86,7 +88,6 @@ const useStyles = makeStyles((theme: Theme) => }) ); - //Up to 30 - same direction export function GameComponent({ ...props }) { const canvasRef = useRef(null); @@ -111,6 +112,12 @@ export function GameComponent({ ...props }) { const [offsetArray, setOffsetArray] = useState([]); const [routes, setRoutes] = useState([]); const [trialStarted, setTrialStarted] = useState(false); + const [warningMessage, setWarningMessage] = useState(""); + const [result, setResult] = useState(false); + const [speed, setSpeed] = useState("1"); // Control the base speed + const [angle, setAngle] = useState(0); + const [lastUpdate, setLastUpdate] = useState(Date.now()); + const getRandomQuadrant = (min, max) => { return Math.floor(Math.random() * (max - min + 1) + min); }; @@ -120,6 +127,8 @@ export function GameComponent({ ...props }) { const easyOffsetArray = [15, -15, 0]; const mediumOffsetArray = [15, -15, 0, 30, -30]; const hardOffsetArray = [15, -15, 0, 30, -30, 45, -45]; + const min = 1; + const max = 5; let offArray = []; if (props.adventure === "Hard") { offArray = hardOffsetArray; @@ -156,18 +165,20 @@ export function GameComponent({ ...props }) { setStarted(true); divRef.current.style.background = "transparent"; canvasRef.current.onmousemove = (event) => { - handleMovement(ctx, event.clientX, event.clientY, centerX, centerY); + const rect = canvasRef.current.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + handleMovement(ctx, x, y, centerX, centerY); //track mouse position and change for custom cursor }; } })(); - }, [offset, random]); + }, [offset, random, targetShow, currentCount]); useEffect(() => { (async () => { if (canvasRef?.current && isMobile) { await setup(canvasRef); - window.addEventListener("devicemotion", handleAcceleration); const ctx = canvasRef.current.getContext("2d"); let centerX = ctx.canvas.width / 2; let centerY = ctx.canvas.height / 2 - 75; @@ -178,24 +189,32 @@ export function GameComponent({ ...props }) { divRef.current.style.background = "transparent"; canvasRef.current.ontouchend = (event) => { helpTextRef.current.style.display = "none"; - divRef.current.style.background = - "linear-gradient(to right, rgb(182, 244, 146), rgb(51, 139, 147))"; - const targetPosition = getTargetPostion(centerX, centerY); + divRef.current.style.background = "url(" + basketBall + ") no-repeat"; + divRef.current.style.left = centerX + "px"; + divRef.current.style.top = centerY + "px"; drawTarget(ctx, centerX, centerY); clearCenter(ctx, centerX, centerY); - setTrialStarted(true); - }; - return () => { - window.removeEventListener("devicemotion", handleAcceleration); + setTimeout(() => { + setTrialStarted(true); + }, 300); }; } })(); }, [currentCount]); + useEffect(() => { + if (isMobile) { + window.addEventListener("devicemotion", handleAcceleration); + return () => { + window.removeEventListener("devicemotion", handleAcceleration); + }; + } + }, [speed]); + const getTargetPostion = (centerX, centerY) => { const ctx = canvasRef.current.getContext("2d"); if (currentCount < 50) { - let startX = centerX + ctx.canvas.width / (isMobile ? 4 : 8); + let startX = centerX + ctx.canvas.width / (isMobile ? 3 : 8); let startY = centerY; return { startX: startX, startY: startY, done: false }; } else { @@ -224,22 +243,29 @@ export function GameComponent({ ...props }) { }; const handleAcceleration = (event) => { - let landscape = stateValues?.landscape; - let rotation = event.rotationRate || 0; - var x = event.accelerationIncludingGravity.x; - var y = event.accelerationIncludingGravity.y; - var z = event.accelerationIncludingGravity.z; - x = oneDecimal(x); - y = oneDecimal(y); - z = oneDecimal(z); - setStateValues({ - ...stateValues, - rotation: rotation, - x: x, // landscape ? y : x, - y: y, //landscape ? x : y, - z: z, - landscape: stateValues?.landscape, - }); + const currentTime = Date.now(); + if (currentTime - lastUpdate > 100) { + let landscape = stateValues?.landscape; + let rotation = event.rotationRate || 0; + var x = event.accelerationIncludingGravity.x; + var y = event.accelerationIncludingGravity.y; + var z = event.accelerationIncludingGravity.z; + const newX = x * parseInt(speed); + const newY = y * parseInt(speed); // Invert Y-axis for canvas + const newZ = z * parseInt(speed); + x = oneDecimal(newX); + y = oneDecimal(newY); + z = oneDecimal(newZ); + setStateValues({ + ...stateValues, + rotation: rotation, + x: x, // landscape ? y : x, + y: y, //landscape ? x : y, + z: z, + landscape: stateValues?.landscape, + }); + setLastUpdate(currentTime); + } }; const oneDecimal = (n) => { @@ -248,6 +274,12 @@ export function GameComponent({ ...props }) { return rounded; }; + const twoDecimal = (n) => { + var number = n; + var rounded = Math.round(number * 100) / 100; + return rounded; + }; + const toPercentage = (x, n) => { var p = 0; if (n) { @@ -266,8 +298,10 @@ export function GameComponent({ ...props }) { let centerX = ctx.canvas.width / 2; let centerY = ctx.canvas.height / 2 - 75; if (trialStarted) { - let x1 = ctx.canvas.width / (100 / toPercentage(stateValues.x, 1)); - let y1 = ctx.canvas.height / (100 / toPercentage(stateValues.y, 1)); + // let x1 = ctx.canvas.width / (100 / toPercentage(stateValues.x, 1)); + // let y1 = ctx.canvas.height / (100 / toPercentage(stateValues.y, 1)); + let x1 = ctx.canvas.width / 2 + stateValues.x * 10; // Adjust position with acceleration + let y1 = ctx.canvas.height / 2 + stateValues.y * 10; // Adjust position with acceleration if (Math.abs(offset) > 0) { const xy = rotate(centerX, centerY, x1, y1, -offset); const offsetX = xy.x; @@ -285,17 +319,61 @@ export function GameComponent({ ...props }) { divRef.current.style.top = y + "px"; manageTarget(ctx, x, y, centerX, centerY); }; + const getDistanceBetweenPoints = (x1, y1, x2, y2) => { + const dist = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); + return dist; + }; + + const checkPointOutsideCircle = (x, y, centerX, centerY, radius) => { + const distance = getDistanceBetweenPoints(x, y, centerX, centerY); + if (distance <= radius) { + return false; + } else { + return true; + } + }; + + const getAngle = (x, y, targetPosition, centerX, centerY) => { + const vector1 = { x: x - centerX, y: y - centerY }; + const vector2 = { + x: targetPosition.startX - centerX, + y: targetPosition.startY - centerY, + }; + const angle1 = Math.atan2(vector1.y, vector1.x); + const angle2 = Math.atan2(vector2.y, vector2.x); + let angleDifference = angle2 - angle1; + if (angleDifference > Math.PI) { + angleDifference -= 2 * Math.PI; + } else if (angleDifference < -Math.PI) { + angleDifference += 2 * Math.PI; + } + return twoDecimal(angleDifference); // in radians + }; const manageTarget = (ctx, x, y, centerX, centerY) => { let targetPosition = getTargetPostion(centerX, centerY); - if ( - Math.abs(x - 5 - targetPosition?.startX) < 20 && - Math.abs(y - 5 - targetPosition?.startY) > 50 && - Math.abs(y - 5 - targetPosition?.startY) < 60 - ) { + const radius = getDistanceBetweenPoints( + targetPosition?.startX, + targetPosition?.startY, + centerX, + centerY + ); + const status = checkPointOutsideCircle(x, y, centerX, centerY, radius); + if (status && targetShow) { if (!done) { - divRef.current.style.background = "transparent"; + if ( + Math.abs(x - 5 - targetPosition?.startX) < 20 && + Math.abs(y - 5 - targetPosition?.startY) > 10 && + Math.abs(y - 5 - targetPosition?.startY) < 25 + ) { + setResult(true); + setAngle(0); + } else { + setResult(false); + setAngle(getAngle(x, y, targetPosition, centerX, centerY)); + } clearTarget(ctx, centerX, centerY); + divRef.current.style.background = "transparent"; drawCenterCircle(ctx, centerX, centerY); setTrialStarted(false); if (helpTextRef.current) helpTextRef.current.style.display = "block"; @@ -323,8 +401,7 @@ export function GameComponent({ ...props }) { divRef.current.style.top = y1 + "px"; if (Math.abs(x - centerX) < 20 && Math.abs(y - centerY) < 80) { helpTextRef.current.style.display = "none"; - divRef.current.style.background = - "linear-gradient(to right, rgb(182, 244, 146), rgb(51, 139, 147))"; + divRef.current.style.background = "url(" + basketBall + ") no-repeat"; drawTarget(ctx, centerX, centerY); clearCenter(ctx, centerX, centerY); } @@ -366,7 +443,7 @@ export function GameComponent({ ...props }) { useEffect(() => { if (!!done && currentCount <= 100) { - if (currentCount >= 50 && offsetLevelArray.length > 0) { + if (currentCount > 50 && offsetLevelArray.length > 0) { setDone(false); const tempRandom = getRandomQuadrant(1, 4); setRandom( @@ -377,10 +454,21 @@ export function GameComponent({ ...props }) { setOffsetArray([...offsetArray, randomOffset]); } else { setOffset(0); + if (!isMobile) { + setDone(false); + } } - if (Math.floor(new Date().getTime() - time) > 300) { + if (Math.floor(new Date().getTime() - time) > 600) { setWarning(true); + setWarningMessage("Move faster"); + setTimeout(() => { + setWarning(false); + }, 500); + } + if (Math.floor(new Date().getTime() - time) < 300) { + setWarning(true); + setWarningMessage("Move Slower"); setTimeout(() => { setWarning(false); }, 500); @@ -389,10 +477,11 @@ export function GameComponent({ ...props }) { const route = { duration: Math.floor(new Date().getTime() - time) / 1000, item: currentCount, - level: offset, - type: offset, + level: result, + type: angle, value: null, }; + console.log("route", route); setRoutes([...routes, route]); if (currentCount + 1 >= 100) { sentResult(); @@ -406,7 +495,7 @@ export function GameComponent({ ...props }) { let startY = 0; if (random == 1) { startX = centerX + ctx.canvas.width / (isMobile ? 4 : 8); - startY = centerY - ctx.canvas.height / (isMobile ? 5 : 8); + startY = centerY - ctx.canvas.height / (isMobile ? 7 : 8); } // if (random == 2) { // startX = centerX + ctx.canvas.width / (isMobile ? 3 : 8); @@ -423,20 +512,28 @@ export function GameComponent({ ...props }) { return { x: startX, y: startY }; }; - const drawTarget = async (ctx, centerX, centerY) => { if (!targetShow) { setTargetShow(true); setDone(false); const targetPosition = getTargetPostion(centerX, centerY); - drawCircle(ctx, { - radius: 12, - lineWidth: 0, - strokeStyle: "#FFFFFF", - colorFill: "#ADD8E6", - startX: targetPosition.startX, - startY: targetPosition.startY, - }); + // drawCircle(ctx, { + // radius: getDistanceBetweenPoints(centerX,centerY,targetPosition.startX,targetPosition.startY), + // lineWidth: 0, + // strokeStyle: "#FFFFFF", + // colorFill: "#ADD8E6", + // startX: centerX, + // startY: centerY, + // }); + const image = new Image(); + image.src = hoop; + image.onload = () => { + ctx.drawImage( + image, + targetPosition.startX - 10, + targetPosition.startY - 10 + ); + }; setTime(new Date().getTime()); } }; @@ -444,7 +541,7 @@ export function GameComponent({ ...props }) { const clearTarget = (ctx, centerX, centerY) => { const targetPosition = getTargetPostion(centerX, centerY); drawCircle(ctx, { - radius: 12, + radius: 24, lineWidth: 0, strokeStyle: "#FFFFFF", colorFill: "#FFFFFF", @@ -493,6 +590,15 @@ export function GameComponent({ ...props }) { }); }; + const handleChange = (event) => { + const result = event.target.value; + if (parseInt(result) >= min && parseInt(result) <= max) { + setSpeed(result); + } else { + setSpeed(""); + } + }; + return ( <> - {!!warning && "Move Faster"} + {!!warning && warningMessage} {`${currentCount}/${props?.totalCount ?? 100}`} - + {/* Offset{" "} - + */} + {isMobile && ( + + Speed Factor: + handleChange(e)} + placeholder="Enter value between 1-5" + /> + + )} {iPhone && !permissionGranted && ( diff --git a/Gyroscope/src/index.tsx b/Gyroscope/src/index.tsx index bfbe77d6..44c64a90 100644 --- a/Gyroscope/src/index.tsx +++ b/Gyroscope/src/index.tsx @@ -11,20 +11,20 @@ import * as ReactDOM from 'react-dom'; import { AppContainer } from "react-hot-loader"; import Instructions from "./containers/Instructions"; -const eventMethod = !!window.addEventListener ? "addEventListener" : "attachEvent" -const eventer = window[eventMethod] -const messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message" -eventer( - messageEvent, (e : any) => { +// const eventMethod = !!window.addEventListener ? "addEventListener" : "attachEvent" +// const eventer = window[eventMethod] +// const messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message" +// eventer( +// messageEvent, (e : any) => { ReactDOM.render( , document.getElementById("root") ); - }, - false -) +// }, +// false +// )