diff --git a/src/music/MusicPlayer.tsx b/src/music/MusicPlayer.tsx index 3960bf7..59fc4f6 100644 --- a/src/music/MusicPlayer.tsx +++ b/src/music/MusicPlayer.tsx @@ -6,81 +6,85 @@ import Visualizer from "./components/visualizer"; import { State } from "./store"; type AudioState = { - audioContext: AudioContext; - audioSource: HTMLAudioElement; - audioSourceNode: MediaElementAudioSourceNode; - audioVolume: GainNode; + audioContext: AudioContext; + audioSource: HTMLAudioElement; + audioSourceNode: MediaElementAudioSourceNode; + audioVolume: GainNode; }; type MusicPlayerProps = { - playing: boolean; - muted: boolean; - source?: string; + playing: boolean; + muted: boolean; + source?: string; }; class MusicPlayer extends React.Component { - private audioState: AudioState; + private audioState: AudioState; - constructor(props: MusicPlayerProps) { - super(props); + constructor(props: MusicPlayerProps) { + super(props); - const context = new AudioContext(); - const source = new Audio(); - const sourceNode = context.createMediaElementSource(source); - const volume = context.createGain(); + const context = new AudioContext(); + const source = new Audio(); + const sourceNode = context.createMediaElementSource(source); + const volume = context.createGain(); - sourceNode.connect(volume); - volume.connect(context.destination); + sourceNode.connect(volume); + volume.connect(context.destination); - this.audioState = { - audioContext: context, - audioSourceNode: sourceNode, - audioSource: source, - audioVolume: volume, - }; - } + this.audioState = { + audioContext: context, + audioSourceNode: sourceNode, + audioSource: source, + audioVolume: volume, + }; + } - render() { - return ( -
- - -
- ); - } + render() { + return ( +
+
+ +
+
+ +
+
+ ); + } - componentDidUpdate() { - const context = this.audioState.audioContext; - const source = this.audioState.audioSource; - const volume = this.audioState.audioVolume; + componentDidUpdate() { + const context = this.audioState.audioContext; + const source = this.audioState.audioSource; + const volume = this.audioState.audioVolume; - // First, set the audio source (if it changed) - if (this.props.source && source.src != this.props.source) { - source.src = this.props.source; - } + // First, set the audio source (if it changed) + if (this.props.source && source.src != this.props.source) { + source.src = this.props.source; + } - if (this.props.playing) { - source - .play() - .then(() => { - console.info("Started playing audio"); - }) - .catch((error) => { - console.error(`Could not play audio: ${error}`); - }); - } else { - source.pause(); - } + if (this.props.playing) { + source + .play() + .then(() => { + console.info("Started playing audio"); + }) + .catch((error) => { + console.error(`Could not play audio: ${error}`); + }); + } else { + source.pause(); + } - if (!this.props.muted) { - volume.gain.setValueAtTime(1, context.currentTime); - } else { - volume.gain.setValueAtTime(0, context.currentTime); - } - } + if (!this.props.muted) { + volume.gain.setValueAtTime(1, context.currentTime); + } else { + volume.gain.setValueAtTime(0, context.currentTime); + } + } } function mapStateToProps(state: State): MusicPlayerProps { diff --git a/src/music/components/controls.tsx b/src/music/components/controls.tsx index c812d0e..380e666 100644 --- a/src/music/components/controls.tsx +++ b/src/music/components/controls.tsx @@ -11,29 +11,45 @@ type ControlProps = { class Controls extends React.Component { render() { - return ( -
-
- -
- {this.props.title.name} - {this.props.title.album} -
+ let title = ( +
+
{this.props.title.name}
+
+ ); - {this.props.title.name === "Journey" && - this.props.title.artist === "Mseq" ? ( -
- Journey -  by Mseq (c) copyright 2016 Licensed under a Creative - Commons  - - Attribution Noncommercial (3.0) - -   license. Ft: Admiral Bob,Texas Radio Fish -
- ) : null} + if ( + this.props.title.name == "Journey" && + this.props.title.artist == "Mseq" + ) { + title = ( +
+
+ + Journey + +  by Mseq (c) copyright 2016 Licensed under a + Creative Commons  + + Attribution Noncommercial (3.0) + +   license. Ft: Admiral Bob,Texas Radio Fish +
+
+ ); + } + + return ( +
+
+
+ + {title} +
+
+
+ {this.props.title.artist} +
+
); diff --git a/src/music/components/indicator.tsx b/src/music/components/indicator.tsx index d916391..2189e0a 100644 --- a/src/music/components/indicator.tsx +++ b/src/music/components/indicator.tsx @@ -22,22 +22,30 @@ class Indicator extends React.Component { } render() { - const classes = classNames({ - btn: true, - "col-auto": true, + const button_classes = classNames({ + button: true, + "is-primary": true, + "level-item": true, + // TODO(tlater): Add loading logic here + }); + + const icon_classes = classNames({ fas: true, + "fa-2x": true, "fa-muted": this.props.muted, - "fa-play": this.props.playing, - "fa-pause": !this.props.playing, + "fa-play": !this.props.playing, + "fa-pause": this.props.playing, }); return ( + className={button_classes}> + + + + ); } } diff --git a/src/music/components/visualizer.tsx b/src/music/components/visualizer.tsx index 7fcda45..ce981e6 100644 --- a/src/music/components/visualizer.tsx +++ b/src/music/components/visualizer.tsx @@ -80,7 +80,7 @@ class CanvasDrawer { }); this.renderer.setClearColor(new three.Color(0x0f0f0f)); - this.renderer.setSize(canvas.width, canvas.height); + this.renderer.setSize(canvas.width, canvas.height, false); // Set up canvas resizing window.addEventListener("resize", this.resize.bind(this)); @@ -147,28 +147,31 @@ class CanvasDrawer { resize() { const canvas = this.canvas; + if (canvas.parentElement === null) { throw Error("Could not access canvas parent for size calculation"); } - // Compute the height of all our siblings - let combinedHeight = 0; - for (let i = 0; i < canvas.parentElement.children.length; i++) { - const child = canvas.parentElement.children[i]; + // This is stupid, but by setting the canvas proportions to 0 + // for a split second the browser can actually figure out the + // height of the parentElement. + // + // If the canvas is allowed to keep its height, it will + // prevent the parentElement from being able to shrink because + // its contents are filling it completely. + // + // I've seen others use `overflow: hidden` to achieve this + // (and in fact seen it render in my browser on jsfiddle), but + // that doesn't work here somehow. + canvas.height = 0; + canvas.width = 0; - if (child != canvas) { - combinedHeight += child.clientHeight; - } - } + let height = canvas.parentElement.clientHeight; + let width = canvas.parentElement.clientWidth; - // The remaining space we want to fill - const remainingHeight = canvas.parentElement.clientHeight - combinedHeight; - canvas.height = remainingHeight; - canvas.width = canvas.parentElement.clientWidth; - - this.camera.aspect = canvas.width / remainingHeight; + this.camera.aspect = width / height; this.camera.updateProjectionMatrix(); - this.renderer.setSize(canvas.width, remainingHeight); + this.renderer.setSize(width, height, false); } stop() { @@ -179,9 +182,9 @@ class CanvasDrawer { } class Visualizer extends React.Component { - private analyser: AnalyserNode; + private analyser?: AnalyserNode; private canvas: React.RefObject; - private drawer: CanvasDrawer; + private drawer?: CanvasDrawer; constructor(props: VisualizerProps) { super(props); @@ -191,10 +194,10 @@ class Visualizer extends React.Component { render(): React.ReactNode { return ( + style={{ + display: "block", + }}> ); } @@ -207,11 +210,14 @@ class Visualizer extends React.Component { this.analyser.fftSize = 2048; this.analyser.smoothingTimeConstant = 0.8; this.props.audioSource.connect(this.analyser); - this.drawer = new CanvasDrawer(this.analyser, this.canvas.current); } componentWillUnmount(): void { + if (!this.drawer || !this.analyser) { + return; + } + this.drawer.stop(); this.props.audioSource.disconnect(this.analyser); } diff --git a/src/music/index.tsx b/src/music/index.tsx index f11df8f..a3ad17f 100644 --- a/src/music/index.tsx +++ b/src/music/index.tsx @@ -1,5 +1,4 @@ -import React from "react"; -import ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; import { Provider } from "react-redux"; import { store } from "./store"; @@ -10,11 +9,11 @@ import mseq from "./Mseq_-_Journey.mp3"; const rootElement = document.getElementById("playerUI"); -ReactDOM.render( +const root = createRoot(rootElement!); +root.render( - , - rootElement + ); store.dispatch(setSource(mseq)); diff --git a/src/music/music.scss b/src/music/music.scss index e0d0dc0..364124e 100644 --- a/src/music/music.scss +++ b/src/music/music.scss @@ -3,6 +3,6 @@ $fa-font-path: "npm:@fortawesome/fontawesome-free/webfonts"; @import "~/node_modules/@fortawesome/fontawesome-free/scss/fontawesome"; @import "~/node_modules/@fortawesome/fontawesome-free/scss/solid"; -#playerControls { - background-color: #11151c; +.is-overflow-hidden { + overflow: hidden !important; } diff --git a/src/music_sample.html b/src/music_sample.html index 172457a..d5a2841 100644 --- a/src/music_sample.html +++ b/src/music_sample.html @@ -3,8 +3,8 @@ - -
+ +