Commit ee1238f2 authored by Hauk Olaussen's avatar Hauk Olaussen
Browse files

Added a button for changing theme. The loading and unloading of the theme will...

Added a button for changing theme. The loading and unloading of the theme will come in the next commit. Also added functionality for the user to input what color to choose for the animations. This is done inside the Animation.ts file mostly. I have also added some comments and optimalizations all over :)

Builds heavily on issue #12
Closes issue #4 and #7
parent 91774f43
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);
};
// 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);
`;
......@@ -198,6 +198,14 @@ const App: React.FC = () => {
>
{isFavourite(currentIndex) ? "Remove from fav" : "Add to fav"}
</button>
<button
id="themebutton"
onClick={() => {
changeTheme();
}}
>
{theme === 0 ? "Theme 2" : "Theme 1"}
</button>
{
<Exhibition
title={installation.title}
......
/**
* 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";
/**
......@@ -13,13 +13,33 @@ const Exhibition: React.FC<Installation> = ({
currentIndex,
audiosource,
}) => {
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>
......@@ -31,6 +51,31 @@ const Exhibition: React.FC<Installation> = ({
By - <em>{author}</em>
</h3>
<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>
);
}
}
......
......@@ -30,4 +30,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