Musical keyboard component written in React.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 

142 Zeilen
3.1 KiB

  1. import * as React from 'react'
  2. import * as PropTypes from 'prop-types'
  3. import styled from 'styled-components'
  4. import isNaturalKey from '../services/isNaturalKey'
  5. import getKeyWidth from '../services/getKeyWidth'
  6. import getKeyLeft from '../services/getKeyLeft'
  7. import generateKeys from '../services/generateKeys'
  8. const Base = styled('div')({
  9. position: 'relative',
  10. })
  11. const Key = styled('div')({
  12. position: 'absolute',
  13. border: '1px solid',
  14. boxSizing: 'border-box',
  15. top: 0,
  16. })
  17. const NaturalKey = styled(Key)({
  18. zIndex: 0,
  19. backgroundColor: 'transparent',
  20. })
  21. const AccidentalKey = styled(Key)({
  22. zIndex: 2,
  23. backgroundColor: 'currentColor',
  24. })
  25. const Highlight = styled('div')({
  26. width: '100%',
  27. height: '100%',
  28. })
  29. export const propTypes = {
  30. startKey: PropTypes.number.isRequired,
  31. endKey: PropTypes.number.isRequired,
  32. //octaveDivision: PropTypes.number,
  33. accidentalKeyLengthRatio: PropTypes.number,
  34. keyChannels: PropTypes.arrayOf(PropTypes.shape({
  35. channel: PropTypes.number.isRequired,
  36. key: PropTypes.number.isRequired,
  37. velocity: PropTypes.number.isRequired,
  38. })),
  39. channelColors: PropTypes.arrayOf(PropTypes.string),
  40. width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  41. height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  42. }
  43. const DEFAULT_CHANNEL_COLORS = [
  44. '#ff5555',
  45. '#ffff00',
  46. '#00aa00',
  47. '#0055aa',
  48. '#aa00ff',
  49. '#aa0000',
  50. '#aa5500',
  51. '#ffaa00',
  52. '#00ff00',
  53. '#00aaaa',
  54. '#00ffff',
  55. '#ff00aa',
  56. '#aaaa00',
  57. '#555500',
  58. '#5500aa',
  59. '#ff55ff',
  60. ]
  61. type Props = PropTypes.InferProps<typeof propTypes>
  62. const Keyboard: React.FC<Props> = ({
  63. startKey,
  64. endKey,
  65. //octaveDivision = 12,
  66. accidentalKeyLengthRatio = 0.65,
  67. keyChannels = [],
  68. channelColors = DEFAULT_CHANNEL_COLORS,
  69. width = '100%',
  70. height = 64,
  71. }) => {
  72. const [keys, setKeys, ] = React.useState<number[]>([])
  73. React.useEffect(() => {
  74. setKeys(generateKeys(startKey!, endKey!))
  75. }, [startKey, endKey, ])
  76. return (
  77. <Base
  78. style={{
  79. width: width!,
  80. height: height!,
  81. }}
  82. >
  83. {keys.map(k => {
  84. const isNatural = isNaturalKey(k)
  85. const Component = isNatural ? NaturalKey : AccidentalKey
  86. const width = getKeyWidth(startKey!, endKey!)(k)
  87. const height = isNatural ? 100 : 100 * accidentalKeyLengthRatio!
  88. const left = getKeyLeft(startKey!, endKey!)(k)
  89. const currentKeyChannels = (
  90. Array.isArray(keyChannels!)
  91. ? keyChannels.filter(kc => kc!.key === k)
  92. : null
  93. )
  94. return (
  95. <Component
  96. style={{
  97. width: width + '%',
  98. height: height + '%',
  99. left: left + '%',
  100. }}
  101. >
  102. {
  103. Array.isArray(currentKeyChannels)
  104. && currentKeyChannels.map(c => (
  105. <Highlight
  106. key={c!.channel}
  107. style={{
  108. backgroundColor: channelColors![c!.channel] as string,
  109. }}
  110. />
  111. ))
  112. }
  113. </Component>
  114. )
  115. })}
  116. </Base>
  117. )
  118. }
  119. Keyboard.propTypes = propTypes
  120. export default Keyboard