Commit 657ea895 authored by Noran Baskaran's avatar Noran Baskaran
Browse files

Merge branch 'issue-12-customization' into 'master'

Issue 12 customization

Closes #7 and #4

See merge request !10
parents 91774f43 f24c06f7
const createBoxes = (amountBoxes: number) => {
let boxes: Box[] = [];
/**
* Enum for holding track of the colors
*/
export enum Color {
RANDOM = -1,
RED = 0,
GREEN = 1,
BLUE = 2,
}
/**
* All the animation-functions will have a specification of color (or default = Color.RANDOM) and a boolean clear (or default = false)
*/
interface AnimationFunction {
(specification?: Color, clear?: boolean) : void
}
/**
* Returns a leading zero if the hex is too short (1 character)
* Example: if the hex is just 'a', it will return '0a'
* @param num - number to be converted to hex
* @param len - how long the padding should be
*/
const toPaddedHexString = (num: number, len: number): string => {
const str: string = num.toString(16);
return "0".repeat(len - str.length) + str;
};
/**
* Returns a weighted hexadecimal color based on the color provided.
* @param specification - the color specification
*/
const randomRGB = (specification: Color = Color.RANDOM): string => {
const redMult: number = specification === Color.RED ? 255 : 50;
const greenMult: number = specification === Color.GREEN ? 255 : 50;
const blueMult: number = specification === Color.BLUE ? 255 : 50;
const red: string = toPaddedHexString(Math.floor(Math.random() * redMult), 2);
const green: string = toPaddedHexString(
Math.floor(Math.random() * greenMult),
2
);
const blue: string = toPaddedHexString(
Math.floor(Math.random() * blueMult),
2
);
return red + green + blue;
};
/**
* Returns either a completely random color, or a weighted color depending on the provided color
* @param specification - the color specification
*/
const generateRandomColor = (specification: Color = Color.RANDOM): string => {
const color: string = `#${
specification === Color.RANDOM
? Math.floor(Math.random() * 16777215).toString(16)
: randomRGB(specification)
}`;
return color;
};
// ANIMATION NR 1
/**
* Returns an array with the provided amount of random generated boxes used in animation 1
* @param amountBoxes - the amont of boxes in animation 1
* @param specification - the color specification
*/
const createBoxes = (
amountBoxes: number,
specification: Color = Color.RANDOM
): Box[] => {
let nboxes: Box[] = [];
for (let i = 0; i < amountBoxes; i++) {
var speed: number = Math.floor(Math.random() * 100) / 100;
var color: string = "#" + Math.floor(Math.random() * 16777215).toString(16);
var size: number = Math.floor(Math.random() * 200);
var y: number = Math.floor(Math.random() * 500);
var box: Box = { speed, color, size, y };
boxes.push(box);
const speed: number = Math.floor(Math.random() * 100) / 100;
const color: string = generateRandomColor(specification);
const size: number = Math.floor(Math.random() * 200);
const y: number = Math.floor(Math.random() * 500);
const box: Box = { speed, color, size, y };
nboxes.push(box);
}
return boxes;
return nboxes;
};
const boxes: Box[] = createBoxes(35);
// The array of boxes (this can be altered based on the user input)
let boxes: Box[] = createBoxes(35);
const draw1 = () => {
/**
* Updated the color of the boxes with a weighted color based on the parameter
* @param specification - the color specification
*/
const updateBoxesColor = (specification: Color = Color.RANDOM) => {
for (let box of boxes) {
box.color = generateRandomColor(specification);
}
};
/**
* The function for drawing animation 1. It uses the boxes array for keeping track of each rectangle
* @param specification - the color specification
* @param clear - a boolean signaling the change/clear of color
*/
const draw1:AnimationFunction = (specification: Color = Color.RANDOM, clear: boolean = false) => {
if (clear) {
updateBoxesColor(specification);
}
const canvas: HTMLCanvasElement = document.getElementById(
"canvas"
) as HTMLCanvasElement;
......@@ -40,7 +131,20 @@ const draw1 = () => {
requestAnimationFrame(draw1);
};
const draw2 = () => {
// ANMATION NR 2
// The color for the circles in animation 2. Can be changed by user input
let draw2Color: string = generateRandomColor();
/**
* The function for animation 2. It tracks the color using the draw2Color variable.
* @param specification - the color specification
* @param clear - a boolean signaling the change/clear of color
*/
const draw2: AnimationFunction = (specification: Color = Color.RANDOM, clear: boolean = false) => {
if (clear) {
draw2Color = generateRandomColor(specification);
}
const canvas: HTMLCanvasElement = document.getElementById(
"canvas"
) as HTMLCanvasElement;
......@@ -55,8 +159,7 @@ const draw2 = () => {
ctx.globalAlpha = 0.5;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#0000ff";
ctx.fillStyle = draw2Color;
for (let i = 0; i < amountCircles; i++) {
ctx.beginPath();
......@@ -78,4 +181,15 @@ const draw2 = () => {
requestAnimationFrame(draw2);
};
export const ANIMATIONS = [draw1, draw2];
// EXPORTED ANIMATION FUNCTION USED IN EXHIBITION
/**
* Will call the indexed function with the given parameters.
* Will always set the clear variable in the animation-functions to true as it signals user input
* @param index - the index of the animation in the array
* @param specification - the color specification
*/
export const animate = (index: number, specification: Color = Color.RANDOM) => {
const drawFunction = [draw1, draw2];
drawFunction[index](specification, true);
};
......@@ -13,7 +13,7 @@
}
#wrapperdiv button {
width: 80px;
width: 150px;
margin: 40px;
}
......
// NOT USED BECAUSE ITS IS A THIRD PARTY LIBRARY
import styled, { createGlobalStyle } from "styled-components";
export const GlobalStyle = createGlobalStyle`
html {
height: 100%;
margin: 0;
}
body {
margin: 0;
width: 100%;
height: 100%;
background: #EAE7DC;
display: flex;
justify-content: center;
font-family: 'Lobster', cursive;
color: #3E3D3A
}
`;
export const Wrapper = styled.div`
background: rgb(233, 128, 116);
background: linear-gradient(
90deg,
rgba(233, 128, 116, 1) 0%,
rgba(216, 195, 165, 1) 40%,
rgba(216, 195, 165, 1) 60%,
rgba(233, 128, 116, 1) 100%
);
display: flex;
justify-content: center;
flex-direction: column;
text-align: center;
margin: 0 auto 20px auto;
width: 80vw;
min-height: 80vh;
padding-bottom: 50px;
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.6), 0 12px 30px 0 rgba(0, 0, 0, 0.6);
button {
width: 80px;
margin: 40px;
}
hr {
margin-top: 60px;
width: 600px;
overflow: visible; /* For IE */
padding: 0;
border: none;
border-top: medium double #333;
color: #333;
text-align: center;
}
hr:after {
content: "§";
display: inline-block;
position: relative;
top: -0.7em;
font-size: 1.5em;
padding: 0 0.25em;
background: #d8c3a5;
}
`;
export const Canvas = styled.canvas`
border: 2px solid;
background: white;
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.6), 0 12px 30px 0 rgba(0, 0, 0, 0.6);
`;
......@@ -134,7 +134,6 @@ const App: React.FC = () => {
const addFavourite = (index: number) => {
const newFavs = [...favourites, index];
setFavourites(newFavs);
console.log(newFavs);
lsm.change("favourites", newFavs);
};
......@@ -145,7 +144,6 @@ const App: React.FC = () => {
const removeFavorite = (index: number) => {
const newFavs = favourites.filter((i) => i !== index);
setFavourites(newFavs);
console.log(newFavs);
lsm.change("favourites", newFavs);
};
......@@ -153,7 +151,9 @@ const App: React.FC = () => {
* Will change between the themes available for the web-page
*/
const changeTheme = () => {
setTheme(theme === 0 ? 1 : 0);
const newTheme = theme === 0 ? 1 : 0;
setTheme(newTheme);
lsm.change("theme", newTheme);
};
return (
......@@ -191,12 +191,12 @@ const App: React.FC = () => {
Next poetry
</button>
<button
id="favouritebutton"
id="themebutton"
onClick={() => {
toggleFavourite(currentIndex);
changeTheme();
}}
>
{isFavourite(currentIndex) ? "Remove from fav" : "Add to fav"}
{theme === 0 ? "Theme 2" : "Theme 1"}
</button>
{
<Exhibition
......@@ -205,6 +205,8 @@ const App: React.FC = () => {
lines={installation.lines}
currentIndex={currentIndex + 1}
audiosource={AUDIO_FILES[currentIndex]}
toggleFavourite={toggleFavourite}
isFavourite={isFavourite}
/>
}
</div>
......
/**
* Will fetch an array of poetries from the PoetryDB API.
*/
export const fetchPoetryFromPoetryDB = async () => {
export const fetchPoetryFromPoetryDB = async (): Promise<Installation[] | null> => {
const urls: string[] = [
`https://poetrydb.org/author,title/Robert%20Burns;45.%20My%20Girl%20she%E2%80%99s%20Airy:%20A%20Fragment`,
`https://poetrydb.org/author,title/Percy%20Bysshe%20Shelley;Farewell%20to%20North%20Devon`,
......
import React, { Component } from "react";
/**
* Handles the audio for each of the exhibitions
*/
class AudioPlayer extends Component<AudioPlayerProps, { muted: boolean }> {
audio: HTMLAudioElement;
......@@ -12,26 +15,41 @@ class AudioPlayer extends Component<AudioPlayerProps, { muted: boolean }> {
this.play();
}
/**
* Runs whenever it senses a change
*/
componentDidUpdate() {
this.updateAndPlay();
}
/**
* Pauses the current audio.
* Sets the source to the source prop
* Plays the new audio
*/
updateAndPlay = () => {
this.audio.pause();
this.audio = this.props.source;
this.play();
};
equalSource() {
return this.audio.src.includes(`audio${this.props.currentIndex}`);
}
/**
* As the default volume is way to loud, it will tune it down a bit.
* If the user has muted the music on the page (stored into the state), it will mute and pause the song
*/
play = () => {
this.audio.volume = 0.01;
this.audio.muted = this.state.muted;
this.audio.play();
if (!this.state.muted) {
this.audio.play();
} else {
this.audio.pause();
}
};
/**
* Mutes or unmutes the current audio based on the current state
*/
mute = () => {
this.audio.muted = !this.audio.muted;
this.setState({ muted: this.audio.muted });
......
import React, { useEffect } from "react";
import { ANIMATIONS } from "../Animations";
import { animate, Color } from "../Animations";
import AudioPlayer from "./AudioPlayer";
/**
......@@ -12,14 +12,37 @@ const Exhibition: React.FC<Installation> = ({
lines,
currentIndex,
audiosource,
toggleFavourite,
isFavourite,
}) => {
useEffect(() => {
/**
* Temporary function as not all the animations are done
* @param specification - the color specification
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
const drawAnimation = (specification: Color = Color.RANDOM) => {
let index: number = currentIndex - 1;
if (index > 1) {
index = 1;
}
ANIMATIONS[index]();
}, [currentIndex]);
animate(index, specification);
};
/**
* Runs when the component is loaded on the page
*/
useEffect(() => {
drawAnimation();
}, [drawAnimation]);
/**
* Calls the animate function which will redraw the canvas with colors weighted to the given color
* @param specification - the color specification
*/
const canvasColor = (specification: Color) => {
drawAnimation(specification);
};
return (
<div>
......@@ -30,7 +53,41 @@ const Exhibition: React.FC<Installation> = ({
<h3>
By - <em>{author}</em>
</h3>
<button
id="favouritebutton"
onClick={() => {
toggleFavourite(currentIndex - 1);
}}
>
{isFavourite(currentIndex - 1) ? "Remove from fav" : "Add to fav"}
</button>
<br />
<canvas id="canvas" width="400" height="500" />
<br />
<button
className="canvascolorbutton"
onClick={() => {
canvasColor(Color.RED);
}}
>
Red
</button>
<button
className="canvascolorbutton"
onClick={() => {
canvasColor(Color.GREEN);
}}
>
Green
</button>
<button
className="canvascolorbutton"
onClick={() => {
canvasColor(Color.BLUE);
}}
>
Blue
</button>
<hr />
<div id="lines">
{lines !== undefined
......
import styled from "styled-components";
export const HeaderWrapper = styled.div`
background: rgb(232, 90, 79);
background: linear-gradient(
90deg,
rgba(232, 90, 79, 1) 0%,
rgba(234, 231, 220, 1) 50%,
rgba(232, 90, 79, 1) 100%
);
width: 100%;
text-align: center;
margin-bottom: 20px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.6), 0 6px 20px 0 rgba(0, 0, 0, 0.6);
h1 {
margin: 0;
padding: 30px;
text-decoration: underline;
}
`;
import React, { Component } from "react";
import { HeaderWrapper } from "./Header.style";
export class Header extends Component {
render() {
return (
<HeaderWrapper>
<div>
<h1>The art of Seduction</h1>
</HeaderWrapper>
</div>
);
}
}
......
......@@ -4,6 +4,8 @@ type Installation = {
lines: string[] | undefined;
currentIndex: number;
audiosource: HTMLAudioElement;
toggleFavourite: (index: number) => void;
isFavourite: (index: number) => boolean;
};
type InstallationList = {
......@@ -30,4 +32,4 @@ type Box = {
color: string;
size: number;
y: number;
};
};
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment