diff --git a/src/JSReactApp/package.json b/src/JSReactApp/package.json
index 917b069..51c5309 100644
--- a/src/JSReactApp/package.json
+++ b/src/JSReactApp/package.json
@@ -12,7 +12,10 @@
"dependencies": {
"axios": "^0.19.0",
"react": "16.9.0",
- "react-native": "0.61.5"
+ "react-native": "0.61.5",
+ "react-native-gesture-handler": "^1.5.2",
+ "react-navigation": "^4.0.10",
+ "react-navigation-stack": "^1.10.3"
},
"devDependencies": {
"@babel/core": "^7.7.4",
diff --git a/src/JSReactApp/src/App.js b/src/JSReactApp/src/App.js
index 6233e1d..d1555a4 100644
--- a/src/JSReactApp/src/App.js
+++ b/src/JSReactApp/src/App.js
@@ -8,124 +8,148 @@
import React, {Component} from 'react';
import {
- View,
- Text,
- FlatList,
- StyleSheet,
+ View,
+ Text,
+ FlatList,
+ StyleSheet,
+ TouchableOpacity,
} from 'react-native';
import Button from './components/Button';
import axios from 'axios';
+import defaults from "@babel/runtime/helpers/esm/defaults";
+import {get} from "react-native/Libraries/TurboModule/TurboModuleRegistry";
const API_BASE_PATH = 'https://jsonplaceholder.typicode.com/';
const POSTS_PATH = `${API_BASE_PATH}posts/`;
const USER_PATH = `${API_BASE_PATH}users/`;
+const COMMENTS_PATH = `${API_BASE_PATH}comments?postId=`;
const postKeyExtractor = (item, index) => {
- return `key-${index}`;
+ return `key-${index}`;
};
export default class App extends Component {
- constructor(props) {
- super(props);
- this.state = {
- posts: [],
- users: {},
- };
- }
+ constructor(props) {
+ super(props);
+ this.state = {
+ posts: [],
+ comments: [],
+ users: {},
+ };
+ }
+
+ componentDidMount() {
+ axios.get(POSTS_PATH).then(({data}) => {
+
+ this.setState({posts: data.sort(() => Math.random() - 0.5)});
+
+ Array.from(data.reduce((acc, item) => {
+ acc.add(item.userId);
+ return acc;
+ }, new Set())).forEach(id => {
+ axios.get(`${USER_PATH}${id}`).then(({data}) => {
+ this.setState({users: {...this.state.users, [data.id]: data}});
+ });
+ });
+ });
+ }
+
+ render() {
+ const {posts} = this.state;
+ const {comments} = this.state;
+ const {rootContainer, headerButton, headerContainer} = styles;
+ return
+
+
+
+
+
+ ;
+ }
+
+ handleBackPress = () => {
- componentDidMount() {
- axios.get(POSTS_PATH).then(({data}) => {
+ };
- this.setState({posts: data.sort(() => Math.random() - 0.5)});
+ handleRefreshPres = () => {
+ this.componentDidMount();
+ };
- Array.from(data.reduce((acc, item) => {
- acc.add(item.userId);
- return acc;
- }, new Set())).forEach(id => {
- axios.get(`${USER_PATH}${id}`).then(({data}) => {
- this.setState({users: {...this.state.users, [data.id]: data}});
+ renderComments = () => {
+ axios.get(`${COMMENTS_PATH}1`).then(() => {
+ this.setState(this.state.comments);
});
- });
- });
- }
-
- render() {
- const { posts } = this.state;
- const { rootContainer, headerButton, headerContainer } = styles;
- return
-
-
-
-
-
- ;
- }
-
- handleBackPress = () => {
-
- }
-
- handleRefreshPres = () => {
-
- }
-
- renderPostItem = ({item, index}) => {
- const { users } = this.state;
- const { userId, body } = item;
-
- const { postHeader, postContainerOdd, postContainerEven } = styles;
- const headerStyle = index % 2 ? postContainerOdd : postContainerEven;
- const user = users[userId];
- const userName = user ? user.name : '';
- const userEmail = user ? user.email : '';
-
- return
-
- {userName}
- {userEmail}
-
- {body}
-
- }
-};
+ return
+ {this.state.comments}
+
+ };
+
+ pressPostItem = () => {
+ return this.renderComments;
+ };
+
+ renderPostItem = ({item, index}) => {
+ const {users} = this.state;
+ const {userId, body} = item;
+
+ const {postHeader, postContainerOdd, postContainerEven, headerText} = styles;
+ const headerStyle = index % 2 ? postContainerOdd : postContainerEven;
+ const user = users[userId];
+ const userName = user ? user.name : '';
+ const userEmail = user ? user.email : '';
+ return
+
+ {userName}
+ {userEmail}
+
+ {body}
+
+ }
+}
const styles = StyleSheet.create({
- rootContainer: {
- flex: 1,
- },
- headerContainer: {
- flexDirection: 'row',
- backgroundColor: '#567',
- },
- headerButton: {
- flex: 1,
- margin: 8,
- },
- postContainerOdd: {
- padding: 8,
- backgroundColor: '#fff',
- },
- postContainerEven: {
- padding: 8,
- backgroundColor: '#ddd',
- },
- postHeader: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- marginBottom: 16,
- },
+ rootContainer: {
+ flex: 1,
+ },
+ headerContainer: {
+ flexDirection: 'row',
+ backgroundColor: '#4a1477',
+ },
+ headerButton: {
+ flex: 1,
+ margin: 8,
+ },
+ postContainerOdd: {
+ padding: 8,
+ backgroundColor: '#fff',
+ },
+ postContainerEven: {
+ padding: 8,
+ backgroundColor: '#ddd',
+ },
+ postHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ marginBottom: 16,
+ },
+ headerText: {
+ fontWeight: 'bold',
+ fontSize: 16,
+ }
});
diff --git a/src/JSReactApp/src/components/Button.js b/src/JSReactApp/src/components/Button.js
index af93033..d2e9fdd 100644
--- a/src/JSReactApp/src/components/Button.js
+++ b/src/JSReactApp/src/components/Button.js
@@ -24,11 +24,11 @@ const styles = StyleSheet.create({
flex: 1,
height: 32,
paddingHorizontal: 8,
- backgroundColor: '#433',
+ backgroundColor: '#ec3aff',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 4,
- borderColor: '#877',
+ borderColor: '#aa44ff',
},
content: {
color: 'white',
diff --git a/src/JSReactApp/yarn.lock b/src/JSReactApp/yarn.lock
index cb54e52..0bb4175 100644
--- a/src/JSReactApp/yarn.lock
+++ b/src/JSReactApp/yarn.lock
@@ -948,6 +948,25 @@
eslint-plugin-react-native "3.6.0"
prettier "1.16.4"
+"@react-navigation/core@^3.5.1":
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-3.5.1.tgz#7a2339fca3496979305fb3a8ab88c2ca8d8c214d"
+ integrity sha512-q7NyhWVYOhVIWqL2GZKa6G78YarXaVTTtOlSDkvy4ZIggo40wZzamlnrJRvsaQX46gsgw45FAWb5SriHh8o7eA==
+ dependencies:
+ hoist-non-react-statics "^3.3.0"
+ path-to-regexp "^1.7.0"
+ query-string "^6.4.2"
+ react-is "^16.8.6"
+
+"@react-navigation/native@^3.6.2":
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-3.6.2.tgz#3634697b6350cc5189657ae4551f2d52b57fbbf0"
+ integrity sha512-Cybeou6N82ZeRmgnGlu+wzlV3z5BZQR2dmYaNFV1TNLUGHqtvv8E7oNw9uYcz9Ox5LFbiX+FdNTn2d6ZPlK0kg==
+ dependencies:
+ hoist-non-react-statics "^3.0.1"
+ react-native-safe-area-view "^0.14.1"
+ react-native-screens "^1.0.0 || ^1.0.0-alpha"
+
"@types/babel__core@^7.1.0":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30"
@@ -2010,6 +2029,11 @@ dayjs@^1.8.15:
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.17.tgz#53ec413f2a7b02afbea1846d61bb260fa8567cea"
integrity sha512-47VY/htqYqr9GHd7HW/h56PpQzRBSJcxIQFwqL3P20bMF/3az5c3PWdVY3LmPXFl6cQCYHL7c79b9ov+2bOBbw==
+debounce@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131"
+ integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg==
+
debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -3071,6 +3095,11 @@ growly@^1.3.0:
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
+hammerjs@^2.0.8:
+ version "2.0.8"
+ resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
+ integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=
+
handlebars@^4.1.2:
version "4.5.3"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482"
@@ -3160,6 +3189,18 @@ hermes-engine@^0.2.1:
resolved "https://registry.yarnpkg.com/hermes-engine/-/hermes-engine-0.2.1.tgz#25c0f1ff852512a92cb5c5cc47cf967e1e722ea2"
integrity sha512-eNHUQHuadDMJARpaqvlCZoK/Nitpj6oywq3vQ3wCwEsww5morX34mW5PmKWQTO7aU0ck0hgulxR+EVDlXygGxQ==
+hoist-non-react-statics@^2.3.1:
+ version "2.5.5"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
+ integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
+
+hoist-non-react-statics@^3.0.1, hoist-non-react-statics@^3.3.0:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#101685d3aff3b23ea213163f6e8e12f4f111e19f"
+ integrity sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==
+ dependencies:
+ react-is "^16.7.0"
+
hosted-git-info@^2.1.4:
version "2.8.5"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
@@ -3530,6 +3571,11 @@ is-wsl@^1.1.0:
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+ integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
+
isarray@1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -5296,6 +5342,13 @@ path-parse@^1.0.6:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+path-to-regexp@^1.7.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
+ integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
+ dependencies:
+ isarray "0.0.1"
+
path-type@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
@@ -5505,6 +5558,15 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+query-string@^6.4.2:
+ version "6.9.0"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.9.0.tgz#1c3b727c370cf00f177c99f328fda2108f8fa3dd"
+ integrity sha512-KG4bhCFYapExLsUHrFt+kQVEegF2agm4cpF/VNc6pZVthIfCc/GK8t8VyNIE3nyXG9DK3Tf2EGkxjR6/uRdYsA==
+ dependencies:
+ decode-uri-component "^0.2.0"
+ split-on-first "^1.0.0"
+ strict-uri-encode "^2.0.0"
+
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@@ -5528,11 +5590,35 @@ react-devtools-core@^3.6.3:
shell-quote "^1.6.1"
ws "^3.3.1"
-react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
+react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
+react-native-gesture-handler@^1.5.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.5.2.tgz#281111550bf1eee10b7feba5278d142169892731"
+ integrity sha512-Xp03dq4XYVTD0xmWx4DW4eX+ox1NQLjHmbykspTdS5FCNIVIOekVXRLFCw1698/v8dYUHApNo6K3s3BCD8fqPA==
+ dependencies:
+ hammerjs "^2.0.8"
+ hoist-non-react-statics "^2.3.1"
+ invariant "^2.2.4"
+ prop-types "^15.7.2"
+
+react-native-safe-area-view@^0.14.1:
+ version "0.14.8"
+ resolved "https://registry.yarnpkg.com/react-native-safe-area-view/-/react-native-safe-area-view-0.14.8.tgz#ef33c46ff8164ae77acad48c3039ec9c34873e5b"
+ integrity sha512-MtRSIcZNstxv87Jet+UsPhEd1tpGe8cVskDXlP657x6rHpSrbrc+y13ZNXrwAgGNNhqQNX7UJT68ZIq//ZRmvw==
+ dependencies:
+ hoist-non-react-statics "^2.3.1"
+
+"react-native-screens@^1.0.0 || ^1.0.0-alpha":
+ version "1.0.0-alpha.23"
+ resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-1.0.0-alpha.23.tgz#25d7ea4d11bda4fcde2d1da7ae50271c6aa636e0"
+ integrity sha512-tOxHGQUN83MTmQB4ghoQkibqOdGiX4JQEmeyEv96MKWO/x8T2PJv84ECUos9hD3blPRQwVwSpAid1PPPhrVEaw==
+ dependencies:
+ debounce "^1.2.0"
+
react-native@0.61.5:
version "0.61.5"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.61.5.tgz#6e21acb56cbd75a3baeb1f70201a66f42600bba8"
@@ -5568,6 +5654,21 @@ react-native@0.61.5:
stacktrace-parser "^0.1.3"
whatwg-fetch "^3.0.0"
+react-navigation-stack@^1.10.3:
+ version "1.10.3"
+ resolved "https://registry.yarnpkg.com/react-navigation-stack/-/react-navigation-stack-1.10.3.tgz#e714e442b20427f0d2d3c18fce1f9e8cfe69be0b"
+ integrity sha512-1gksFi/g/Lg9sBhgLlD0OiEB5xnatHb4C0eNMA5tli9cTVlhq375XNPIqOiTyftibBmjdApAsZFj5srUCoOu/w==
+ dependencies:
+ prop-types "^15.7.2"
+
+react-navigation@^4.0.10:
+ version "4.0.10"
+ resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-4.0.10.tgz#ddf41134600689d6ba99e35dd22ba1f664f91e5c"
+ integrity sha512-7PqvmsdQ7HIyxPUMYbd9Uq//VoMdniEOLAOSvIhb/ExtbAt/1INSjUF+RiMWOMCWLTCNvNPRvTz7xy7qwWureg==
+ dependencies:
+ "@react-navigation/core" "^3.5.1"
+ "@react-navigation/native" "^3.6.2"
+
react-refresh@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.4.2.tgz#54a277a6caaac2803d88f1d6f13c1dcfbd81e334"
@@ -6185,6 +6286,11 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
+split-on-first@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
+ integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
+
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@@ -6247,6 +6353,11 @@ stream-buffers@~2.2.0:
resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4"
integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ=
+strict-uri-encode@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+ integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
+
string-length@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
diff --git a/src/assignment3.js b/src/assignment3.js
index 45923db..e2ec818 100644
--- a/src/assignment3.js
+++ b/src/assignment3.js
@@ -1,16 +1,167 @@
-window.onload = function() {
- const canvas = document.getElementById('mainDrawingCanvas');
- const resetButton = document.getElementById('resetButton');
- const scoreSpan = document.getElementById('scoreSpan');
+const BALL_COUNT = 100;
+const INITIAL_SCORE = 10;
+const SCORE_MULTIPLIED = 1.6;
+const EXPLOSION_RADIUS = 20;
+const EXPLOSION_FADE_TIME = 3000;
+const EXPLODING_TIME = 500;
+const BALL_SPEED = 10;
+const FPS = 60; //frames per second
- const context = canvas.getContext('2d');
+window.onload = function () {
+ const canvas = document.getElementById('mainDrawingCanvas');
+ const resetButton = document.getElementById('resetButton');
+ resetButton.onclick = () => {
+ context.clearRect(0, 0, canvas.width, canvas.height);
+ clearInterval(animateBallsInterval);
+ gameStarted = false;
- canvas.addEventListener('click', event => {
- const x = event.offsetX;
- const y = event.offsetY;
- }, false);
+ };
+ const scoreSpan = document.getElementById('scoreSpan');
+ const context = canvas.getContext('2d');
+ let gameStarted = false;
+ let explodedBallsArray = [];
+ let ballsArray = [];
+ let animateBallsInterval;
- resetButton.onclick = () => {
+ // draw BALL_COUNT of balls
+ if (!gameStarted) {
+ createBalls();
+ animateBallsInterval = setInterval(animateBalls, 1000 / FPS);
+ }
- }
+ function createBalls() {
+ for (let i = 0; i < BALL_COUNT; i += 1) {
+ ballsArray[i] = new Ball();
+ }
+ }
+
+ //animate balls
+ function animateBalls() {
+ context.clearRect(0, 0, canvas.width, canvas.height);
+ ballsArray.forEach(item => {
+ item.x += item.vx * BALL_SPEED / FPS;
+ item.y += item.vy * BALL_SPEED / FPS;
+
+ if (item.x > canvas.width - item.radius || item.x - item.radius < 0) {
+ item.vx = -item.vx;
+ }
+ if (item.y > canvas.height - item.radius || item.y - item.radius < 0) {
+ item.vy = -item.vy;
+ }
+ item.draw(context);
+ });
+
+ explodedBallsArray.forEach(explodedBalls => {
+ explodedBalls.draw();
+ });
+ if (gameStarted && explodedBallsArray.length === 0) {
+ clearInterval(animateBallsInterval);
+ }
+ }
+
+ canvas.addEventListener('click', event => {
+ if (!gameStarted) {
+ gameStarted = true;
+ } else {
+ return;
+ }
+ const x = event.offsetX;
+ const y = event.offsetY;
+ startChainReaction(x, y, explodedBallsArray);
+ }, false);
+
+ function startChainReaction(x, y, array) {
+ let firstExplosion = new Ball();
+ firstExplosion.x = x;
+ firstExplosion.y = y;
+ array.push(firstExplosion);
+ explosion(firstExplosion, EXPLODING_TIME);
+ fadeColor(firstExplosion, EXPLOSION_FADE_TIME);
+ }
+
+ explodedBallsArray.forEach(explodedBall => {
+ });
+
+
+ //explosions
+ setInterval(() => {
+ explodedBallsArray.forEach(explodedBall => {
+ const ex = explodedBall.x;
+ const ey = explodedBall.y;
+ const eRadius = explodedBall.radius;
+ ballsArray.forEach(ball => {
+ const {x, y, radius} = ball;
+ const distanceSQ = (x - ex) * (x - ex) + (y - ey) * (y - ey);
+ const radiusSum = radius + eRadius;
+ if (distanceSQ < radiusSum * radiusSum) {
+ explosion(ball, EXPLODING_TIME);
+ fadeColor(ball, EXPLOSION_FADE_TIME);
+ explodedBallsArray.push(ball);
+ ballsArray.splice(ballsArray.indexOf(ball), 1);
+ }
+ })
+ });
+ explodedBallsArray = explodedBallsArray.filter(item => item.color.substr(7, 2) !== '00');
+ }, 1000 / FPS);
+
+
+ //draw a ball
+ function Ball() {
+ this.radius = 10;
+ this.x = Math.random() * (canvas.width - this.radius);
+ if (this.x < this.radius) {
+ this.x = this.radius;
+ }
+ this.y = Math.random() * (canvas.height - this.radius);
+ if (this.y < this.radius) {
+ this.y = this.radius;
+ }
+ this.vx = Math.random() * 2 * BALL_SPEED - BALL_SPEED;
+ // random number between +/- BALL_SPEED
+ this.vy = Math.sqrt(BALL_SPEED * BALL_SPEED - this.vx * this.vx) * (Math.round(Math.random()) * 2 - 1);
+ // (square root of ball speed - vx) * (+/- 1)
+ this.color = '#' + Math.random().toString(16).substr(2, 6) + 'ff';
+ this.draw = () => {
+ context.beginPath();
+ context.fillStyle = this.color;
+ context.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ context.fill();
+ };
+ }
};
+
+//state of balls at the explosion moment
+function explosion(circle, duration) {
+ let startTime = new Date().getTime();
+
+ let explosionInterval = setInterval(() => {
+ let now = new Date().getTime();
+ circle.radius = circle.radius + (EXPLOSION_RADIUS - circle.radius) * (now - startTime) / duration;
+ if (circle.radius === EXPLOSION_RADIUS) {
+ clearInterval(explosionInterval);
+ }
+ }, 1000 / FPS)
+}
+
+//fade color
+function fadeColor(circle, duration) {
+ let startTime = new Date().getTime();
+ let startOpacityVal = parseInt(circle.color.substring(7), 16);
+
+ let fadeInterval = setInterval(() => {
+ let now = new Date().getTime();
+ let value = startOpacityVal - startOpacityVal * (now - startTime) / duration;
+ let opacity = Math.round(value);
+ if (opacity < 0) {
+ opacity = 0;
+ }
+ opacity = opacity.toString(16);
+ if (opacity.length === 1) {
+ opacity = '0' + opacity;
+ }
+ if (opacity.toString() === '00') {
+ clearInterval(fadeInterval);
+ }
+ circle.color = circle.color.substring(0, 7) + opacity;
+ }, 1000 / FPS);
+}
\ No newline at end of file