基于另一个属性的React组件的访问属性

时间:2018-12-20 16:44:06

标签: javascript reactjs

最终,我正在尝试创建一个类似钢琴的应用程序,您可以通过单击或按下来控制它。我希望每个键盘键都可以控制某个音符。每个“钢琴”键都是具有键码属性和音符属性的组件。当event.keycode与keycode属性匹配时,我要播放关联的音符。最好的策略是什么?

我尝试使用refs并专注于componentDidMount。我似乎无法确定应该如何工作。

class Pad extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            clicked: false
        }

        this.clickedMouse = this.clickedMouse.bind(this)
        this.unClickedMouse = this.unClickedMouse.bind(this)
        this.handleDownKeyPress = this.handleDownKeyPress.bind(this)
        this.handleUpKeyPress = this.handleUpKeyPress.bind(this)
    }

    clickedMouse(e) {
        this.setState({ clicked: true })
        this.props.onDown(this.props.note)
        console.log(e)
    }

    unClickedMouse(e) {
        this.setState({ clicked: false })
        this.props.onUp(this.props.note)
    }

    handleDownKeyPress(e) {

        if (e.keyCode === this.props.keyCode && this.state.clicked === false) {
            this.setState({ clicked: true })
            this.props.onDown(this.props.note)
        }
    }

    handleUpKeyPress(e) {
        this.setState({ clicked: false })
        this.props.onUp(this.props.note)
    }

    render() {
        return (
            <div
                className='pad'
                onMouseUp={this.unClickedMouse}
                onMouseDown={this.clickedMouse}
                onKeyDown={this.handleDownKeyPress}
                onKeyUp={this.handleUpKeyPress}
                tabIndex='0'
            />
        );
    }
}

export default Pad

class Pads extends React.Component {
    constructor(props) {
        super(props);

        // tone.js build
        this.synth = new Tone.Synth().toMaster()
        this.vol = new Tone.Volume(0)
        this.synth.chain(this.vol, Tone.Master)

        // bindings
        this.onDownKey = this.onDownKey.bind(this);
        this.onUpKey = this.onUpKey.bind(this);
    }

    onDownKey(note) {
        console.log(`${note} played`);
        this.synth.triggerAttack(note);
    }

    onUpKey(note) {
        this.synth.triggerRelease();
    }

    render() {
        const { octave } = this.props
        return (
            <div className="pad-grid">
                <Pad
                    keyCode={65}
                    note={`C${octave}`}
                    onDown={this.onDownKey}
                    onUp={this.onUpKey}
                />
                <Pad
                    keyCode={70}
                    note={`Db${octave}`}
                    onDown={this.onDownKey}
                    onUp={this.onUpKey}
                />
                <Pad
                    keyCode={83}
                    note={`D${octave}`}
                    onDown={this.onDownKey}
                    onUp={this.onUpKey}
                />
                <Pad
                    keyCode={68}
                    note={`Eb${octave}`}
                    onDown={this.onDownKey}
                    onUp={this.onUpKey}
                />
            </div>
        );
    }
}

export default Pads

这里有一个Codepen,因此您可以在操作中https://codepen.io/P-FVNK/pen/XopBgW将其检出

1 个答案:

答案 0 :(得分:0)

最终代码:https://codesandbox.io/s/5v89kw6w0n

因此,我进行了大量更改以启用所需的功能。

  

有没有办法遍历重复的组件及其属性以匹配event.keyCode来更正键码属性?

为此,我决定创建一个包含可能的键及其注释的对象数组。我将它们从现已弃用的KeyboardEvent.keyCode更改为KeyboardEvent.key。您也可以使用KeyboardEvent.code

this.padCodes = [
  {
    key: "a",
    note: "C"
  },
  {
    key: "f",
    note: "Db"
  },
  {
    key: "s",
    note: "D"
  },
  {
    key: "d",
    note: "Eb"
  }
];

我将keydown事件侦听器从各个<Pad />移到了父组件,并将侦听器附加到了文档上。

document.addEventListener("keydown", e => {
  let index = this.state.pads.findIndex(item => item.key === e.key);
  let { octave } = this.context;
  if (this.state.pressed === false) {
    this.onDownKey(`${this.state.pads[index].props.note}${octave}`);
    this.setState({
      activeNote: this.state.pads[index].note,
      pressed: true
    });
  }
});
document.addEventListener("keyup", e => {
  this.onUpKey(this.state.activeNote);
  this.setState({
    activeNote: "",
    pressed: false
  });
});

您可能还会注意到我决定使用Context代替道具。那只是个人喜好,但我宁愿主要参考八度音阶,而不是将prop传递给每个子分量。

之后,只需确保这些函数调用相同的函数并传递音符和八度即可。

为了减少混乱,我制作了<Pad />数组,后来我渲染了它们,而不是一一列出。

let newPadCodes = this.padCodes.map(x => (
    <Pad
      key={x.key.toString()}
      note={`${x.note}`}
      onDown={this.onDownKey}
      onUp={this.onUpKey}
    />
));

this.setState({
  pads: newPads
});

//And later

//Render if this.state.pads.length is > 0
{this.state.pads.length > 0 &&
 this.state.pads}

这几乎涵盖了我所做的一切。这是带有修改后的代码的codeandbox:https://codesandbox.io/s/5v89kw6w0n