From a4c3670b111a5f80b94edffe0a3a6aa37a298b2c Mon Sep 17 00:00:00 2001 From: jberg Date: Wed, 6 Jun 2018 21:29:05 -0700 Subject: [PATCH] add preset selection overlay --- js/components/MilkdropWindow/PresetOverlay.js | 126 ++++++++++++++++++ js/components/MilkdropWindow/index.js | 91 ++++++++++--- 2 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 js/components/MilkdropWindow/PresetOverlay.js diff --git a/js/components/MilkdropWindow/PresetOverlay.js b/js/components/MilkdropWindow/PresetOverlay.js new file mode 100644 index 00000000..87647017 --- /dev/null +++ b/js/components/MilkdropWindow/PresetOverlay.js @@ -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 ( +
+ Loading presets +
+ ); + } + + // 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 ( +
  • + {presetName} +
  • + ); + }); + + return ( +
    +
    +
      + {presetElms} +
    +
    +
    + ); + } +} + +export default PresetOverlay; diff --git a/js/components/MilkdropWindow/index.js b/js/components/MilkdropWindow/index.js index 50d21bfe..f07a5d33 100644 --- a/js/components/MilkdropWindow/index.js +++ b/js/components/MilkdropWindow/index.js @@ -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 ( - (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 && ( + this.props.onFocusedKeyDown(listener)} + selectPreset={idx => this.selectPreset(idx)} + closeOverlay={() => this.closePresetOverlay()} + /> + )} + (this._canvasNode = node)} + /> + ); } }