From 2dcdef4c38194196e67869da2576feeb74c9cae3 Mon Sep 17 00:00:00 2001 From: Voellmy Raphael Date: Sun, 7 Jun 2020 15:11:05 +0200 Subject: [PATCH] feat(chord chart): set colors for individual fingers and barre chords Allow overriding the global color for each individual finger and barre chords. Both the color of the finger or barre chord, as well as the text on the finger or barre chord can be overridden. --- README.md | 10 ++- src/renderer/roughjs/roughjs-renderer.ts | 4 +- src/renderer/svgjs/svg-js-renderer.ts | 2 +- src/svguitar.ts | 54 ++++++++++---- test/svguitar.test.ts | 89 +++++++++++++++++++++++- test/testutils.ts | 2 +- 6 files changed, 140 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 0b69edd..4b6b6f4 100644 --- a/README.md +++ b/README.md @@ -80,17 +80,21 @@ Here's an example of a customized chart: ```javascript new SVGuitarChord('#some-selector') .chord({ - // array of [string, fret, text] + // array of [string, fret, text | options] fingers: [ + // finger at string 1, fret 2, with text '2' [1, 2, '2'], - [2, 3, '3'], + + // finger at string 2, fret 3, with text '3', colored red + [2, 3, { text: '3', color: '#F00' }], + [3, 3], [6, 'x'] ], // optional: barres for barre chords barres: [ - { fromString: 5, toString: 1, fret: 1, text: '1' }, + { fromString: 5, toString: 1, fret: 1, text: '1', color: '#0F0', textColor: '#F00' }, ], }) .configure({ diff --git a/src/renderer/roughjs/roughjs-renderer.ts b/src/renderer/roughjs/roughjs-renderer.ts index e84fc4e..d5748f6 100644 --- a/src/renderer/roughjs/roughjs-renderer.ts +++ b/src/renderer/roughjs/roughjs-renderer.ts @@ -269,7 +269,7 @@ export class RoughJsRenderer extends Renderer { return RoughJsRenderer.boxToElement(txtElem.getBBox(), txtElem.remove.bind(txtElem)) } - static boxToElement(box: DOMRect, remove: () => void): GraphcisElement { + private static boxToElement(box: DOMRect, remove: () => void): GraphcisElement { return { width: box.width, height: box.height, @@ -279,7 +279,7 @@ export class RoughJsRenderer extends Renderer { } } - static roundedRectData( + private static roundedRectData( w: number, h: number, tlr: number, diff --git a/src/renderer/svgjs/svg-js-renderer.ts b/src/renderer/svgjs/svg-js-renderer.ts index 1ef42e9..2ebeb48 100644 --- a/src/renderer/svgjs/svg-js-renderer.ts +++ b/src/renderer/svgjs/svg-js-renderer.ts @@ -142,7 +142,7 @@ export class SvgJsRenderer extends Renderer { return SvgJsRenderer.boxToElement(element.bbox(), element.remove.bind(element)) } - static boxToElement(box: Box, remove: () => void): GraphcisElement { + private static boxToElement(box: Box, remove: () => void): GraphcisElement { return { width: box.width, height: box.height, diff --git a/src/svguitar.ts b/src/svguitar.ts index db0d62b..c10de8d 100644 --- a/src/svguitar.ts +++ b/src/svguitar.ts @@ -7,12 +7,20 @@ import { Alignment, GraphcisElement, Renderer, RoughJsRenderer, SvgJsRenderer } export type SilentString = 'x' export type OpenString = 0 export type Finger = [number, number | OpenString | SilentString, (string | FingerOptions)?] -export type Barre = { fromString: number; toString: number; fret: number; text?: string } +export type Barre = { + fromString: number + toString: number + fret: number + text?: string + color?: string + textColor?: string +} export type Chord = { fingers: Finger[]; barres: Barre[] } export interface FingerOptions { text?: string color?: string + textColor?: string } /** @@ -614,26 +622,25 @@ export class SVGuitarChord { .forEach(([stringIndex, fretIndex, textOrOptions]) => { const nutCenterX = startX + stringIndex * stringSpacing const nutCenterY = y + fretIndex * fretSpacing - fretSpacing / 2 + const fingerOptions = SVGuitarChord.getFingerOptions(textOrOptions) this.renderer.circle( nutCenterX - nutSize / 2, nutCenterY - nutSize / 2, nutSize, 0, - nutColor, - nutColor, + fingerOptions.color || nutColor, + fingerOptions.color || nutColor, ) - const text = typeof textOrOptions === 'string' ? textOrOptions : textOrOptions?.text - // draw text on the nut - if (text) { + if (fingerOptions.text) { this.renderer.text( - text, + fingerOptions.text, nutCenterX, nutCenterY, nutTextSize, - nutTextColor, + fingerOptions.textColor || nutTextColor, fontFamily, Alignment.MIDDLE, true, @@ -642,7 +649,7 @@ export class SVGuitarChord { }) // draw barre chords - this.chordInternal.barres.forEach(({ fret, fromString, toString, text }) => { + this.chordInternal.barres.forEach(({ fret, fromString, toString, text, color, textColor }) => { const barreCenterY = fretYPositions[fret - 1] - fretSpacing / 2 const fromStringX = stringXPositions[this.toArrayIndex(fromString)] const distance = Math.abs(toString - fromString) * stringSpacing @@ -653,8 +660,8 @@ export class SVGuitarChord { distance + stringSpacing / 2, nutSize, 0, - nutColor, - nutColor, + color || nutColor, + color || nutColor, nutSize * barreChordRadius, ) @@ -665,7 +672,7 @@ export class SVGuitarChord { fromStringX + distance / 2, barreCenterY, nutTextSize, - nutTextColor, + textColor || nutTextColor, fontFamily, Alignment.MIDDLE, true, @@ -722,4 +729,27 @@ export class SVGuitarChord { remove(): void { this.renderer.remove() } + + /** + * Helper method to get an options object from the 3rd array value for a finger, that can either + * be undefined, a string or and options object. This method will return an options object in + * any case, so it's easier to work with this third value. + * + * @param textOrOptions + */ + private static getFingerOptions( + textOrOptions: string | FingerOptions | undefined, + ): FingerOptions { + if (!textOrOptions) { + return {} + } + + if (typeof textOrOptions === 'string') { + return { + text: textOrOptions, + } + } + + return textOrOptions + } } diff --git a/test/svguitar.test.ts b/test/svguitar.test.ts index 7ef29a4..72b2846 100644 --- a/test/svguitar.test.ts +++ b/test/svguitar.test.ts @@ -83,14 +83,99 @@ describe('SVGuitarChord', () => { .configure({ strings: 5, frets: 6, - title: 'Text on Nuts', - nutTextColor: 'tomato', + title: 'Colored Nuts', }) .draw() saveSvg('colored nuts', container.outerHTML) }) + it('Should render text on nuts with a different color', () => { + svguitar + .chord({ + fingers: [ + [1, 2, { text: 'G', textColor: 'green' }], + [2, 1, { text: 'B', textColor: 'blue' }], + [3, 1, { textColor: 'green' }], // no effect + ], + barres: [], + }) + .configure({ + strings: 5, + frets: 6, + title: 'Colored Text on Nuts', + }) + .draw() + + saveSvg('colored text on nuts', container.outerHTML) + }) + + it('Should render barre chords with a different color', () => { + svguitar + .chord({ + fingers: [], + barres: [ + { + fret: 1, + fromString: 4, + toString: 1, + color: 'blue', + }, + { + fret: 3, + fromString: 5, + toString: 2, + color: 'red', + }, + ], + }) + .configure({ + strings: 5, + frets: 6, + title: 'Colored Barre Chords', + }) + .draw() + + saveSvg('colored barre chords', container.outerHTML) + }) + + it('Should render text on barre chords with a different color', () => { + svguitar + .chord({ + fingers: [], + barres: [ + { + fret: 1, + fromString: 4, + toString: 1, + text: 'Blue Text', + textColor: 'blue', + }, + { + fret: 3, + fromString: 5, + toString: 2, + text: 'Red Text', + textColor: 'red', + }, + { + fret: 2, + fromString: 3, + toString: 2, + textColor: 'red', + }, + ], + }) + .configure({ + strings: 5, + frets: 6, + title: 'Colored Text on Barre Chords', + }) + .draw() + + saveSvg('colored text on barre chords', container.outerHTML) + }) + it('Should render text on the barre chords', () => { svguitar .chord({ diff --git a/test/testutils.ts b/test/testutils.ts index ab4c09e..f8e6441 100644 --- a/test/testutils.ts +++ b/test/testutils.ts @@ -28,5 +28,5 @@ export function saveSvg(name: string, svg: string) { mkdirSync(svgOutputDir) } - writeFileSync(join(svgOutputDir, `${name}.svg`.replace(' ', '-')), svg) + writeFileSync(join(svgOutputDir, `${name}.svg`.replace(/\s+/g, '-')), svg) }