add preset selection overlay

This commit is contained in:
jberg 2018-06-06 21:29:05 -07:00 committed by Jordan Eldredge
parent 4e7369a38d
commit a4c3670b11
2 changed files with 198 additions and 19 deletions

View file

@ -0,0 +1,126 @@
import React from "react";
class PresetOverlay extends React.Component {
constructor(props) {
super(props);
this.state = { presetIdx: Math.max(props.currentPreset, 0) };
this._handleFocusedKeyboardInput = this._handleFocusedKeyboardInput.bind(
this
);
}
componentDidMount() {
this._unsubscribeFocusedKeyDown = this.props.onFocusedKeyDown(
this._handleFocusedKeyboardInput
);
}
componentWillUnmount() {
if (this._unsubscribeFocusedKeyDown) {
this._unsubscribeFocusedKeyDown();
}
}
_handleFocusedKeyboardInput(e) {
switch (e.keyCode) {
case 38: // up arrow
this.setState({ presetIdx: Math.max(this.state.presetIdx - 1, 0) });
e.stopPropagation();
break;
case 40: // down arrow
this.setState({
presetIdx: Math.min(
this.state.presetIdx + 1,
Object.keys(this.props.presets).length - 1
)
});
e.stopPropagation();
break;
case 13: // enter
this.props.selectPreset(this.state.presetIdx);
e.stopPropagation();
break;
case 27: // escape
this.props.closeOverlay();
e.stopPropagation();
break;
}
}
render() {
if (!this.props.presets) {
return (
<div
style={{
position: "absolute",
top: 0,
left: 0,
color: "white",
background: "rgba(0.33, 0.33, 0.33, 0.33)"
}}
>
<span>Loading presets</span>
</div>
);
}
// display highlighted preset in the middle if possible
const presetKeys = Object.keys(this.props.presets);
const numPresets = presetKeys.length;
let presetListLen = Math.floor(this.props.height / 20);
presetListLen = Math.min(Math.max(presetListLen, 3), numPresets);
presetListLen = presetListLen % 2 ? presetListLen : presetListLen - 1;
const halfPresetListLen = Math.floor(presetListLen / 2);
let startIdx = Math.max(this.state.presetIdx - halfPresetListLen, 0);
let endIdx = Math.min(startIdx + presetListLen, numPresets);
if (endIdx >= numPresets) {
startIdx = Math.max(endIdx - presetListLen, 0);
endIdx = Math.min(startIdx + presetListLen, numPresets);
}
const presets = presetKeys.slice(startIdx, endIdx);
const presetElms = presets.map((presetName, i) => {
let color;
if (i + startIdx === this.props.currentPreset) {
if (i + startIdx === this.state.presetIdx) {
color = "#FFCC22";
} else {
color = "#CCFF03";
}
} else if (i + startIdx === this.state.presetIdx) {
color = "#FF5050";
} else {
color = "#CCCCCC";
}
return (
<li key={i} style={{ color }}>
{presetName}
</li>
);
});
return (
<div
style={{
position: "absolute",
top: 0,
left: 0,
padding: "15px 10px 0 10px"
}}
>
<div
style={{
display: "inline-block",
width: `${this.props.width - 20}px`,
maxHeight: `${this.props.height - 15}px`,
whiteSpace: "nowrap",
overflow: "hidden",
background: "rgba(0, 0, 0, 0.815)",
fontSize: "12px"
}}
>
<ul style={{ listStyleType: "none", padding: 0, margin: 0 }}>
{presetElms}
</ul>
</div>
</div>
);
}
}
export default PresetOverlay;

View file

@ -1,6 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import screenfull from "screenfull";
import PresetOverlay from "./PresetOverlay";
const USER_PRESET_TRANSITION_SECONDS = 5.7;
const PRESET_TRANSITION_SECONDS = 2.7;
@ -9,10 +10,15 @@ const MILLISECONDS_BETWEEN_PRESET_TRANSITIONS = 15000;
class MilkdropWindow extends React.Component {
constructor(props) {
super(props);
this._handleKeyboardInput = this._handleKeyboardInput.bind(this);
this.state = {
isFullscreen: false,
presetOverlay: false,
currentPreset: -1
};
this._handleFocusedKeyboardInput = this._handleFocusedKeyboardInput.bind(
this
);
this._handleFullscreenChange = this._handleFullscreenChange.bind(this);
}
componentDidMount() {
require.ensure(
@ -29,11 +35,13 @@ class MilkdropWindow extends React.Component {
analyserNode.context,
this._canvasNode,
{
width: this._canvasNode.width,
height: this._canvasNode.height,
width: this.props.width,
height: this.props.height,
pixelRatio: window.devicePixelRatio || 1
}
);
this._canvasNode.width = this.props.width;
this._canvasNode.height = this.props.height;
this.visualizer.connectAudio(analyserNode);
this.visualizer.loadPreset(reactionDiffusion2, 0);
// Kick off the animation loop
@ -44,6 +52,8 @@ class MilkdropWindow extends React.Component {
window.requestAnimationFrame(loop);
};
loop();
screenfull.onchange(this._handleFullscreenChange);
},
e => {
console.error("Error loading Butterchurn", e);
@ -61,7 +71,6 @@ class MilkdropWindow extends React.Component {
this.presetRandomize = true;
this.presetCycle = true;
this._restartCycling();
document.addEventListener("keydown", this._handleKeyboardInput);
this._unsubscribeFocusedKeyDown = this.props.onFocusedKeyDown(
this._handleFocusedKeyboardInput
);
@ -75,10 +84,10 @@ class MilkdropWindow extends React.Component {
componentWillUnmount() {
this._pauseViz();
this._stopCycling();
document.removeEventListener("keydown", this._handleKeyboardInput);
if (this._unsubscribeFocusedKeyDown) {
this._unsubscribeFocusedKeyDown();
}
screenfull.off("change", this._handleFullscreenChange);
}
componentDidUpdate(prevProps) {
if (
@ -117,18 +126,24 @@ class MilkdropWindow extends React.Component {
this.visualizer.setRendererSize(width, height);
}
}
_handleFullscreenChange() {
if (screenfull.isFullscreen) {
this._setRendererSize(window.innerWidth, window.innerHeight);
} else {
this._setRendererSize(this.props.width, this.props.height);
}
this.setState({ isFullscreen: screenfull.isFullscreen });
}
_handleRequestFullsceen() {
if (screenfull.enabled) {
if (!screenfull.isFullscreen) {
screenfull.request(this._canvasNode);
this._setRendererSize(window.innerWidth, window.innerHeight);
screenfull.request(this._wrapperNode);
} else {
screenfull.exit();
this._setRendererSize(this.props.width, this.props.height);
}
}
}
_handleKeyboardInput(e) {
_handleFocusedKeyboardInput(e) {
switch (e.keyCode) {
case 32: // spacebar
this._nextPreset(USER_PRESET_TRANSITION_SECONDS);
@ -142,6 +157,10 @@ class MilkdropWindow extends React.Component {
case 82: // R
this.presetRandomize = !this.presetRandomize;
break;
case 76: // L
this.setState({ presetOverlay: !this.state.presetOverlay });
e.stopPropagation();
break;
case 145: // scroll lock
case 125: // F14 (scroll lock for OS X)
this.presetCycle = !this.presetCycle;
@ -149,9 +168,6 @@ class MilkdropWindow extends React.Component {
break;
}
}
_handleFocusedKeyboardInput(e) {
console.log("Milkdrop keyDown", e.keyCode);
}
_nextPreset(blendTime) {
// The visualizer may not have initialized yet.
if (this.visualizer != null) {
@ -166,26 +182,41 @@ class MilkdropWindow extends React.Component {
this.presetHistory.push(presetIdx);
this.visualizer.loadPreset(preset, blendTime);
this._restartCycling();
this.setState({ currentPreset: presetIdx });
}
}
_prevPreset(blendTime) {
if (this.presetHistory.length > 1 && this.visualizer != null) {
this.presetHistory.pop();
const prevPreset = this.presetHistory[this.presetHistory.length - 1];
this.visualizer.loadPreset(
this.presets[
this.presetKeys[this.presetHistory[this.presetHistory.length - 1]]
],
this.presets[this.presetKeys[prevPreset]],
blendTime
);
this._restartCycling();
this.setState({ currentPreset: prevPreset });
}
}
selectPreset(presetIdx) {
const preset = this.presets[this.presetKeys[presetIdx]];
this.presetHistory.push(presetIdx);
this.visualizer.loadPreset(preset, 0);
this._restartCycling();
this.setState({ currentPreset: presetIdx });
}
closePresetOverlay() {
this.setState({ presetOverlay: false });
}
render() {
const width = this.state.isFullscreen
? window.innerWidth
: this.props.width;
const height = this.state.isFullscreen
? window.innerHeight
: this.props.height;
return (
<canvas
<div
className="draggable"
ref={node => (this._canvasNode = node)}
onDoubleClick={() => this._handleRequestFullsceen()}
style={{
// This color will be used until Butterchurn is loaded
backgroundColor: "#000",
@ -197,7 +228,29 @@ class MilkdropWindow extends React.Component {
height: "100%",
width: "100%"
}}
/>
tabIndex="0"
ref={node => (this._wrapperNode = node)}
onDoubleClick={() => this._handleRequestFullsceen()}
>
{this.state.presetOverlay && (
<PresetOverlay
width={width}
height={height}
presets={this.presets}
currentPreset={this.state.currentPreset}
onFocusedKeyDown={listener => this.props.onFocusedKeyDown(listener)}
selectPreset={idx => this.selectPreset(idx)}
closeOverlay={() => this.closePresetOverlay()}
/>
)}
<canvas
style={{
height: "100%",
width: "100%"
}}
ref={node => (this._canvasNode = node)}
/>
</div>
);
}
}