钢琴和弦小工具(四)显示和弦

2024-10-16 08:39:34
119次阅读
0个评论

前言

人,不但要有科学技术,而且还要,文化,艺术,跟音乐。 ————钱学森

遗留的subKeys()方法

我们继续来看下之前遗留的subKeys()方法。这个方法在前文中,用于给黑键和白键分别布局。

private subKeys(isWhite: boolean): Key[] { // 根据入参的布尔值,返回黑键或者白键的对象数组subKeys。Key类声明见下文。
  const subKeys: Array<Key> = new Array<Key>()
  let id = 0 // 这个id很关键,用于UI布局里面,所有的按键按照半音间隔依次排列,这个id就是可见按键的序号。
  for (let i = 0; i < 27; i++) { // 两个八度的按键循环,算上fake black,一共28个按键。
    if (isWhite) { // 如果需要返回白键
      if (i % 2 === 0 ) { // 第一个按键是白键,所以判断循环计数为偶数。
        subKeys.push(new Key(id, KeyType.white, this.isSelected(id), this.isChord(id)))
      }
    } else { // 如果需要返回黑键
      if (i % 2 === 1 ) { // 根据半音间隔排列,第二个按键是黑键,所以判断循环计数为奇数。
        let keyType = KeyType.black
        if (this.isFakeBlack(i)) { // mi fa si do 中间的空挡(fake black)。此处keyType需要做特殊处理。
          keyType = KeyType.fakeBlack
        }
        subKeys.push(new Key(id, keyType, this.isSelected(id), this.isChord(id)))
      }
    }
    if (!this.isFakeBlack(i)) { // 每次循环最后要判断一次当前是否遍历到的是fake black,如果不是fake black,即为实际显示的按钮,那么id计数加一。如此,返回的可见黑键id即为正常累加。
      id++
    }
  }
  return subKeys
}
export class Key {
  public id: number 
  public keyType: KeyType // 白键,黑键还是fake black
  public isSelected: boolean // 是否被选中
  public isChord: boolean // 是否为和弦按键

  constructor(
    id: number,
    keyType: KeyType,
    isSelected: boolean,
    isChord: boolean
  ) {
    this.id = id
    this.keyType = keyType
    this.isSelected = isSelected
    this.isChord = isChord
  }
}

明白了subKeys()实现之后,可以回过头再理解一下前文中的isFakeBlack()方法

private isFakeBlack(index: number): boolean {
  return (index + 1) % 14 === 6 || (index + 1) % 14 === 0
}

subKeys()的循环是遍历所有包括白色和黑色的按键,所以我们在图里按顺序给所有按键编号,便于理解。我们把index加一再判断,符合日常的编号习惯。而且这样正好把一个八度里面所有显示和不显示的按键都编上了号,一共14个,下一个八度正好重新循环。可以看到把index+1模除6和14的时候,这种情况是需要隐藏的按键。image-20240813073125276

显示和弦

万事俱备,只欠东风。至此,其实代码在之前都已经展示过了。但还需要梳理一下,显示和弦的流程。

首先在成员属性里面向currentChord赋值。

@Prop currentChord: number[]

这个数字的数组含义为,和弦中所有按键之间的半音间隔数量。在之前布局的时候,subKeys()方法的核心思想是把所有按键按照间隔顺序编号。所以我们以大三和弦举例,根音为do时,需要的按键序号id(这里的序号和上文中包含fake black按键的序号需要注意区分)就是0,4,7。但这个数组不能表达按键的序号,表达的应该是按键之间的半音间隔数量。因为根音还会有re或者mi和其他任意按键。

image-20240813071003065

这是之前提到的isChord()方法,用于判断某个按键是否为和弦。每次点击按键以后,会给this.currentKey赋值当前按键的id,因为按下的按钮需要给粉色,所以遍历到当前按钮时也会返回false。只有根音以外的其他按键才会返回true。

private isChord(index: number): boolean { // index为布局时遍历到的每一个按键
  if (this.currentKey === -1) {
    return false
  }
  const currentKey_ = this.currentKey % 12 // 模除12,只在第一个八度中显示和弦按键
  return this.currentChord.includes(index - currentKey_) // 把遍历到的按键序号index和当前按下的按键序号currentKey_相减,得到按键之间的半音间隔,如果这个间隔包含在数组中,即为和弦按键。
}

理解了和弦数据表达以后,我们得出大三和弦的数组即为0,4,7。以此类推,小三和弦为0,3,7。七和弦为0,4,7,10。这样,任意和弦的数据在之后添加的时候就非常简单了。

结尾

这个应用看似功能简单,实则在实现的时候,有很多细节需要考虑。主要难点在于布局的序号和实际可见按键序号的分离。布局的序号有利于布局循环,可见按键的序号用于和弦数据的表达。

另外,我认为人类的知识应该是融会贯通的,而不是各自成为孤岛。科学和艺术应该是相互融合的,比如黄金分割数会很自然的给人带来美感。

没有艺术的科学是没有灵魂的,没有科学的艺术是杂乱的。

感谢阅读。

收藏00

登录 后评论。没有帐号? 注册 一个。