Added interactive fretboard code

main
Aadhavan Srinivasan 3 days ago
parent 70f70b676a
commit 8a12ea97c1

BIN
js/.DS_Store vendored

Binary file not shown.

BIN
js/src/.DS_Store vendored

Binary file not shown.

@ -0,0 +1,429 @@
/**
* @file Guitar Diagrams JS config class.
* @module js/lib/Guitar-Diagrams-JS/guitar-diagrams-config.mjs
* @link https://github.com/KCarlile/guitar-diagrams-js
*
* @author Kenny Carlile
* @link https://www.kcarlile.com/
* @link https://github.com/KCarlile
*/
/**
* Class representing the Guitar Diagrams JS configuration.
*/
export class GuitarDiagramsJSConfig {
// ========== BEGIN private members
// ----- References
#canvasID = "gdjCanvas";
// ----- Colors
#colorFretboard = "#795548";
#colorNut = "#F2F3F4";
#colorFrets = "#808B96";
#colorStrings = "#CFD8DC";
#colorNutOutline = "#000000";
#colorFretMarkers = "#FFFFFF";
#colorDiagramBackground = null;
#colorLabel = "#000000";
// Markers
#markerStrokeWidth = 2;
#markerFontSize = 16;
// ----- Dimensions/Orientation
#fretCount = 5;
#scaleFactor = 1;
#orientHorizontally = false;
// ----- Features
#fretMarkersEnabled = true;
#fretStartingNumber = 0;
#stringNamesEnabled = true;
#stringNames = ["E", "A", "D", "G", "B", "e"];
#controlsEnabled = false;
#downloadImageEnabled = false;
#changeOrientationEnabled = false;
// ========== END private members
// ========== BEGIN static members
// ========== END static members
// ========== BEGIN constructors
/**
* Create a GuitarDiagramsJSConfig object instance.
*/
constructor(paramCanvasID = null) {
if (paramCanvasID != null) {
this.#canvasID = paramCanvasID;
} // end if test
} // end default constructor
// ========== END constructors
// ========== BEGIN properties
// ----- References
/**
* Gets the ID attribute of the canvas HTML element.
* @return {string} The ID attribute of the canvas HTML element.
*/
get canvasID() {
return this.#canvasID;
} // end get canvasID property
/**
* Sets the ID attribute of the canvas HTML element.
* @param {string} paramCanvasID - The ID attribute of the canvas HTML element.
*/
set canvasID(paramCanvasID) {
this.#canvasID = paramCanvasID;
} // end get canvasID property
// ----- Colors
/**
* Gets the HTML color code of the fretboard.
* @return {string} The HTML color code of the fretboard.
*/
get colorFretboard() {
return this.#colorFretboard;
} // end get colorFretboard property
/**
* Sets the HTML color code of the fretboard.
* @param {string} paramColorFretboard - The HTML color code of the fretboard.
*/
set colorFretboard(paramColorFretboard) {
this.#colorFretboard = paramColorFretboard;
} // end get colorFretboard property
/**
* Gets the HTML color code of the nut.
* @return {string} The HTML color code of the nut.
*/
get colorNut() {
return this.#colorNut;
} // end get colorNut property
/**
* Sets the HTML color code of the nut.
* @param {string} paramColorNut - The HTML color code of the nut.
*/
set colorNut(paramColorNut) {
this.#colorNut = paramColorNut;
} // end get colorNut property
/**
* Gets the HTML color code of the nut outline.
* @return {string} The HTML color code of the nut outline.
*/
get colorNutOutline() {
return this.#colorNutOutline;
} // end get colorNutOutline property
/**
* Sets the HTML color code of the nut outline.
* @param {string} paramColorNutOutline - The HTML color code of the nut outline.
*/
set colorNutOutline(paramColorNutOutline) {
this.#colorNutOutline = paramColorNutOutline;
} // end get colorNutOutline property
/**
* Gets the HTML color code of the frets.
* @return {string} The HTML color code of the frets.
*/
get colorFrets() {
return this.#colorFrets;
} // end get colorFrets property
/**
* Gets the HTML color code of the frets.
* @param {string} paramColorFrets - The HTML color code of the frets.
*/
set colorFrets(paramColorFrets) {
this.#colorFrets = paramColorFrets;
} // end get colorFrets property
/**
* Gets the HTML color code of the strings.
* @return {string} The HTML color code of the strings.
*/
get colorStrings() {
return this.#colorStrings;
} // end get colorStrings property
/**
* Gets the HTML color code of the strings.
* @param {string} paramColorFrets - The HTML color code of the strings.
*/
set colorStrings(paramColorStrings) {
this.#colorStrings = paramColorStrings;
} // end get colorStrings property
/**
* Gets the HTML color code of the fret markers.
* @return {string} The HTML color code of the fret markers.
*/
get colorFretMarkers() {
return this.#colorFretMarkers;
} // end get colorFretMarkers property
/**
* Gets the HTML color code of the fret markers.
* @param {string} paramColorFretMarkers - The HTML color code of the fret markers.
*/
set colorFretMarkers(paramColorFretMarkers) {
this.#colorFretMarkers = paramColorFretMarkers;
} // end get colorFretMarkers property
/**
* Gets the HTML color code of the diagram background.
* @return {string} The HTML color code of the diagram background.
*/
get colorDiagramBackground() {
return this.#colorDiagramBackground;
} // end get colorDiagramBackground property
/**
* Gets the HTML color code of the diagram background.
* @param {string} paramColorDiagramBackground - The HTML color code of the diagram background.
*/
set colorDiagramBackground(paramColorDiagramBackground) {
if (
paramColorDiagramBackground == "" ||
paramColorDiagramBackground == null
) {
paramColorDiagramBackground = "transparent";
} // end if test
this.#colorDiagramBackground = paramColorDiagramBackground;
} // end get colorDiagramBackground property
/**
* Gets HTML color code of the label text for string names and fret number.
* @return {string} The HTML color code of the label text for string names and fret number.
*/
get colorLabel() {
return this.#colorLabel;
} // end get colorLabel property
/**
* Gets the HTML color code of the label text for string names and fret number.
* @param {string} paramColorLabel - The HTML color code of the label text for string names and fret number.
*/
set colorLabel(paramColorLabel) {
this.#colorLabel = paramColorLabel;
} // end get colorLabel property
// ----- Markers
/**
* Gets the marker's stroke width.
* @return {number} The marker's stroke width.
*/
get markerStrokeWidth() {
return this.#markerStrokeWidth;
} // end get markerStrokeWidth property
/**
* Gets the marker's stroke width.
* @param {number} paramMarkerStrokeWidth - The marker's stroke width.
*/
set markerStrokeWidth(paramMarkerStrokeWidth) {
this.#markerStrokeWidth = paramMarkerStrokeWidth;
} // end get markerStrokeWidth property
/**
* Gets the marker's font size.
* @return {number} The marker's font size.
*/
get markerFontSize() {
return this.#markerFontSize;
} // end get markerFontSize property
/**
* Gets the marker's font size.
* @param {number} paramMarkerFontSize - The marker's font size.
*/
set markerFontSize(paramMarkerFontSize) {
this.#markerFontSize = paramMarkerFontSize;
} // end get markerFontSize property
// ----- Dimensions/Orientation
/**
* Gets the number of frets on the diagram.
* @return {number} The number of frets on the diagram.
*/
get fretCount() {
return this.#fretCount;
} // end get fretCount property
/**
* Sets the number of frets on the diagram.
* @param {number} paramFretCount - The number of frets on the diagram.
*/
set fretCount(paramFretCount) {
this.#fretCount = paramFretCount;
} // end get fretCount property
/**
* Gets the scale factor of the diagram.
* @return {number} The scale factor of the diagram.
*/
get scaleFactor() {
return this.#scaleFactor;
} // end get scaleFactor property
/**
* Sets the scale factor of the diagram.
* @param {number} paramScaleFactor - The scale factor of the diagram.
*/
set scaleFactor(paramScaleFactor) {
this.#scaleFactor = paramScaleFactor;
} // end get scaleFactor property
/**
* Gets the horizontal status of the diagram.
* @return {boolean} The horizontal status of diagram.
*/
get orientHorizontally() {
return this.#orientHorizontally;
} // end get orientHorizontally property
/**
* Sets the horizontal status of the diagram.
* @param {boolean} paramOrientHorizontally - The horizontal status of diagram.
*/
set orientHorizontally(paramOrientHorizontally) {
this.#orientHorizontally = paramOrientHorizontally;
} // end get orientHorizontally property
// ----- Features
/**
* Gets the enabled status of fret markers.
* @return {boolean} The enabled status of fret markers.
*/
get fretMarkersEnabled() {
return this.#fretMarkersEnabled;
} // end get fretMarkersEnabled property
/**
* Sets the enabled status of fret markers.
* @param {boolean} paramFretMarkersEnabled - The enabled status of fret markers.
*/
set fretMarkersEnabled(paramFretMarkersEnabled) {
this.#fretMarkersEnabled = paramFretMarkersEnabled;
} // end get fretMarkersEnabled property
/**
* Gets the starting fret number.
* @return {number} The starting fret number.
*/
get fretStartingNumber() {
return this.#fretStartingNumber;
} // end get fretStartingNumber property
/**
* Sets the starting fret number.
* @param {number} paramFretStartingNumber - The starting fret number.
*/
set fretStartingNumber(paramFretStartingNumber) {
this.#fretStartingNumber = paramFretStartingNumber;
} // end get fretStartingNumber property
/**
* Gets the enabled status of string names.
* @return {boolean} The enabled status of string names.
*/
get stringNamesEnabled() {
return this.#stringNamesEnabled;
} // end get stringNamesEnabled property
/**
* Sets the enabled status of string names.
* @param {boolean} paramStringNamesEnabled - The enabled status of string names.
*/
set stringNamesEnabled(paramStringNamesEnabled) {
this.#stringNamesEnabled = paramStringNamesEnabled;
} // end get stringNamesEnabled property
/**
* Gets the array of string names.
* @return {array} The array of string names.
*/
get stringNames() {
return this.#stringNames;
} // end get stringNames property
/**
* Sets the array of string names.
* @param {array} paramStringNames - The array of string names.
*/
set stringNames(paramStringNames) {
this.#stringNames = paramStringNames;
} // end get stringNames property
/**
* Gets the enabled status of the controls.
* @return {boolean} The enabled status of the controls.
*/
get controlsEnabled() {
return this.#controlsEnabled;
} // end get controlsEnabled property
/**
* Sets the enabled status of the controls.
* @param {boolean} paramControlsEnabled - The enabled status of the controls.
*/
set controlsEnabled(paramControlsEnabled) {
this.#controlsEnabled = paramControlsEnabled;
} // end get controlsEnabled property
/**
* Gets the enabled status of the download image button.
* @return {boolean} The enabled status of the download image button.
*/
get downloadImageEnabled() {
return this.#downloadImageEnabled;
} // end get downloadImageEnabled property
/**
* Sets the enabled status of the download image button.
* @param {boolean} paramDownloadImageEnabled - The enabled status of the download image button.
*/
set downloadImageEnabled(paramDownloadImageEnabled) {
this.#downloadImageEnabled = paramDownloadImageEnabled;
} // end get downloadImageEnabled property
/**
* Gets the enabled status of the change orientation button.
* @return {boolean} The enabled status of the change orientation button.
*/
get changeOrientationEnabled() {
return this.#changeOrientationEnabled;
} // end get changeOrientationEnabled property
/**
* Sets the enabled status of the change orientation button.
* @param {boolean} paramChangeOrientationEnabled - The enabled status of the change orientation button.
*/
set changeOrientationEnabled(paramChangeOrientationEnabled) {
this.#changeOrientationEnabled = paramChangeOrientationEnabled;
} // end get changeOrientationEnabled property
// ========== END properties
// ========== BEGIN private methods
// ========== END private methods
// ========== BEGIN public methods
/**
* Enables control functionality and all available individual controls by setting their enabled status to true.
*/
enableAllControls() {
this.controlsEnabled = true;
this.downloadImageEnabled = true;
this.changeOrientationEnabled = true;
} // end enableAllControls method
// ========== END public methods
// ========== BEGIN static methods
// ========== END static methods
} // end GuitarDiagramsJSConfig class

@ -0,0 +1,286 @@
/**
* @file Guitar Diagrams JS marker class.
* @module js/lib/Guitar-Diagrams-JS/guitar-diagrams-marker.mjs
* @link https://github.com/KCarlile/guitar-diagrams-js
*
* @author Kenny Carlile
* @link https://www.kcarlile.com/
* @link https://github.com/KCarlile
*/
/**
* Class representing a Guitar Diagrams JS marker.
*/
export class GuitarDiagramsJSMarker {
// ========== BEGIN private members
// ----- Position and scale
#string;
#fret;
#shape;
#scale = 1;
// ----- Event handling
#eventCallbacks; // map of events to callback functions
// ----- Appearance
#colorFill = "#000000";
#colorStroke = "#DDDDDD";
#colorFont = "#FFFFFF";
#text = "";
#relativePosX;
#relativePosY;
#posX;
#posY;
#playSound; // bool - whether or not the marker will play a sound when clicked
// ========== END private members
// ========== BEGIN static members
// ========== END static members
// ========== BEGIN constructors
/**
* Create a GuitarDiagramsJSMarker object instance.
*/
constructor() {} // end default constructor
// ========== END constructors
// ========== BEGIN properties
// ----- Position and scale
/**
* Gets the string number of the marker.
* @return {number} The string number.
*/
get string() {
return this.#string;
} // end get string property
/**
* Sets the string number of the marker.
* @param {number} paramString - The string number of the marker.
*/
set string(paramString) {
this.#string = paramString;
} // end get string property
/**
* Gets the fret number of the marker.
* @return {number} The fret number.
*/
get fret() {
return this.#fret;
} // end get fret property
/**
* Gets the shape of the marker.
* @return {string} The shape of the marker.
*/
get shape() {
return this.#shape;
} // end get shape property
/**
* Sets the shape of the marker.
* @param {string} paramShape - The shape of the marker.
*/
set shape(paramShape) {
this.#shape = paramShape;
} // end set shape property
/**
* Gets the playSound property of the marker
* @return {boolean} The playSound property
*/
get playSound() {
return this.#playSound;
} // end get playSound property
/**
* Sets the playSound property of the marker.
* @param {boolean} playsound - The playSound property of the marker.
*/
set playSound(playsound) {
this.#playSound = playsound;
} // end set playsound property
/**
* Sets the fret number of the marker.
* @param {number} paramFret - The fret number of the marker.
*/
set fret(paramFret) {
this.#fret = paramFret;
} // end get fret property
/**
* Gets the relative scale of the marker.
* @return {number} The relative scale of the marker.
*/
get scale() {
return this.#scale;
} // end get scale property
/**
* Sets the relative scale of the marker.
* @param {number} paramScale - The relative scale of the marker.
*/
set scale(paramScale) {
this.#scale = paramScale;
} // end get scale property
/**
* Gets the event callbacks for the marker.
* @return {Map} The map of events to their respective callbacks
*/
get eventCallbacks() {
return this.#eventCallbacks;
} // end get eventCallbacks property
/**
* Sets the event callbacks for the marker.
* @param {Map} The map of events to their respective callbacks
*/
set eventCallbacks(eventCallbacks) {
this.#eventCallbacks = eventCallbacks;
} // end get eventCallbacks property
// ----- Appearance
/**
* Gets the HTML color code for fill.
* @return {string} The HTML color code for fill.
*/
get colorFill() {
return this.#colorFill;
} // end get colorFill property
/**
* Sets the HTML color code for fill.
* @param {string} paramColorFill - The HTML color code for fill.
*/
set colorFill(paramColorFill) {
this.#colorFill = paramColorFill;
} // end get colorFill property
/**
* Gets the HTML color code for stroke.
* @return {string} The HTML color code for stroke.
*/
get colorStroke() {
return this.#colorStroke;
} // end get colorStroke property
/**
* Sets the HTML color code for stroke.
* @param {string} paramColorStroke - The HTML color code for stroke.
*/
set colorStroke(paramColorStroke) {
this.#colorStroke = paramColorStroke;
} // end get colorStroke property
/**
* Gets the HTML color code for font.
* @return {string} The HTML color code for font.
*/
get colorFont() {
return this.#colorFont;
} // end get colorFont property
/**
* Sets the HTML color code for font.
* @param {string} paramColorFont - The HTML color code for font.
*/
set colorFont(paramColorFont) {
this.#colorFont = paramColorFont;
} // end get colorFont property
/**
* Gets the X-position of the marker.
* @return {number|null} The event name or null if none set.
*/
get posX() {
return this.#posX;
} // end get posX property
/**
* Sets the X-position of the marker
* @param {number} posX - The x-position
*/
set posX(xPos) {
this.#posX = xPos;
} // end set posX property
/**
* Gets the relative X-position of the marker.
* @return {number|null} The relative x-position or null if none set.
*/
get relativePosX() {
return this.#relativePosX;
} // end get relativePosX property
/**
* Sets the relative X-position of the marker
* @param {number} relativePosX - The x-position
*/
set relativePosX(relativeXPos) {
this.#relativePosX = relativeXPos;
} // end set relativePosX property
/**
* Gets the Y-position of the marker.
* @return {number|null} The event name or null if none set.
*/
get relativePosY() {
return this.#relativePosY;
} // end get relativePosY property
/**
* Sets the relativer Y-position of the marker
* @param {number} posY - The y-position
*/
set relativePosY(relativeYPos) {
this.#relativePosY = relativeYPos;
} // end set relativePosY property
/**
* Gets the text.
* @return {string} The text.
*/
get text() {
return this.#text;
} // end get text property
/**
* Sets the text.
* @param {string} paramText - The text.
*/
set text(paramText) {
this.#text = paramText;
} // end get text property
// ========== END properties
// ========== BEGIN private methods
// ========== END private methods
// ========== BEGIN public methods
/**
* Returns a string representation of the marker GuitarDiagramsJSMarker instance.
* @returns {string} The string representation of the GuitarDiagramsJSMarker instance.
*/
toString() {
const newline = "\n";
const indent = " ";
let toString = "[GuitarDiagramsJSMarker] {" + newline;
toString += indent + "text: " + this.#text + "," + newline;
toString += indent + "string: " + this.#string + "," + newline;
toString += indent + "fret: " + this.#fret + "," + newline;
toString += "}";
return toString;
} // end toString method
// ========== END public methods
// ========== BEGIN static methods
// ========== END static methods
} // end GuitarDiagramsJSMarker class

@ -0,0 +1,931 @@
import { GuitarDiagramsJSConfig } from "./guitar-diagrams-config.mjs";
import { GuitarDiagramsJSMarker } from "./guitar-diagrams-marker.mjs";
import "./tone.js";
import { SampleLibrary } from "./tonejs-instruments/Tonejs-Instruments.js";
class GuitarDiagramsWebComponent extends HTMLElement {
// ========== BEGIN private members
#config;
#markers = [];
#events = [];
#eventTarget = null;
#outputSpeaker;
#docRoot;
// ========== BEGIN static members
// These are hard coded as they are relative sizes to each other.
// Sizing can be changed by using the scaling method.
static stringSpacing = 30;
static fretboardPadding = 20;
static nutThickness = 10;
static stringBaseWidth = 3;
static stringWidthFactor = 0.75;
static stringIndent = 20;
static stringSpacing = 30;
static fretSpacing = 100;
static fretThickness = 6;
static fretMarkerRadius = 8;
static markerRadius = 14;
static stringNameFontSize = 20;
static fretNumberFontSize = 24;
static stringNamePaddingFactor = 1.5;
/**
* Enumeration for marker shapes.
* @readonly
* @enum {string}
*/
static Shape = {
Circle: "circle",
Square: "square",
Triangle: "triangle",
Diamond: "diamond",
}; // end Shape enumeration
/**
* Enumeration for notation symbols.
* @readonly
* @enum {string}
*/
static Symbols = {
Flat: "♭",
Sharp: "♯",
Natural: "♮",
DoubleFlat: "𝄫",
DoubleSharp: "𝄪",
}; // end Symbols enumeration
// ========== END static members
/**
* Gets the target element for events.
* @return {Element|null} DOM element to attach events to
*/
get eventTarget() {
return this.#eventTarget;
} // end get eventTarget property
/**
* Sets the target element for events.
* @param {Element|null} paramEventTarget - DOM element to attach events to
*/
set eventTarget(paramEventTarget) {
this.#eventTarget = paramEventTarget;
} // end set eventTarget property
constructor() {
super();
this.attachShadow({ mode: "open" });
this.#config = new GuitarDiagramsJSConfig();
this.#outputSpeaker = SampleLibrary.load({
instruments: "guitar-acoustic",
ext: ".ogg",
});
this.#outputSpeaker.toMaster();
const canvasElement = document.createElement("canvas");
this.#config.canvasID = this.getAttribute("canvas-id");
canvasElement.id = this.#config.canvasID;
canvasElement.classList.add("guitar-diagrams-canvas");
this.#docRoot = this.shadowRoot;
this.shadowRoot.appendChild(canvasElement);
}
connectedCallback() {
this.drawNeck();
let markerList = Array.from(this.getElementsByTagName("marker"));
markerList.forEach((marker) => {
this.addMarker(
marker.getAttribute("string"),
marker.getAttribute("fret"),
marker.getAttribute("text") || "",
null,
true,
null,
);
});
let chordsButton = document.createElement("button");
const canvas = this.shadowRoot.getElementById(this.#config.canvasID);
chordsButton.setAttribute(
"onclick",
"this.getRootNode().host.playAllMarkers()",
);
chordsButton.innerHTML = "Play chord";
this.#docRoot.appendChild(chordsButton);
this.drawAllMarkers();
}
static get observedAttributes() {
return ["canvas-id", "orient-horizontally", "fret-count", "scale-factor"];
}
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "canvas-id":
this.#config.canvasID = newValue;
break;
case "orient-horizontally":
this.#config.orientHorizontally = newValue === "true";
break;
case "fret-count":
this.#config.fretCount = parseInt(newValue, 10);
break;
case "scale-factor":
this.#config.scaleFactor = parseFloat(newValue);
break;
}
this.drawNeck();
this.drawAllMarkers();
}
#initializeDrawing() {
this.eventTarget = this.shadowRoot.getElementById(this.#config.canvasID);
const canvas = this.shadowRoot.getElementById(this.#config.canvasID);
const context = canvas.getContext("2d");
let fretNumberFontSize = this.#scale(
GuitarDiagramsWebComponent.fretNumberFontSize,
);
let fretNumberIndent =
this.#config.fretStartingNumber == 0 ? 0 : fretNumberFontSize;
let stringNameFontSize = this.#scale(
GuitarDiagramsWebComponent.stringNameFontSize,
);
let stringNameIndent =
this.#config.stringNamesEnabled == false
? 0
: stringNameFontSize *
GuitarDiagramsWebComponent.stringNamePaddingFactor;
let canvasHeight =
this.#scale(this.#getFretboardLength()) + stringNameIndent;
let canvasWidth = this.#scale(this.#getFretboardWidth()) + fretNumberIndent;
if (this.#config.orientHorizontally == true) {
[canvasHeight, canvasWidth] = [canvasWidth, canvasHeight];
}
canvas.setAttribute("width", canvasWidth);
canvas.setAttribute("height", canvasHeight);
let colorDiagramBackground = this.#config.colorDiagramBackground;
if (colorDiagramBackground == "" || colorDiagramBackground == null) {
context.clearRect(0, 0, canvasWidth, canvasHeight);
} else {
context.fillStyle = this.#config.colorDiagramBackground;
context.fillRect(0, 0, canvasWidth, canvasHeight);
}
}
#drawStringNames() {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
if (this.#config.stringNamesEnabled) {
let stringSpacing = this.#scale(GuitarDiagramsWebComponent.stringSpacing);
let stringIndent = this.#scale(GuitarDiagramsWebComponent.stringIndent);
let stringNameFontSize = this.#scale(
GuitarDiagramsWebComponent.stringNameFontSize,
);
let fretNumberFontSize = this.#scale(
GuitarDiagramsWebComponent.fretNumberFontSize,
);
let stringNamesIndent =
this.#config.fretStartingNumber == 0 ? 0 : fretNumberFontSize;
let posX;
let posY;
let stringNames = [...this.#config.stringNames];
if (this.#config.orientHorizontally == true) {
stringNames = stringNames.reverse();
}
for (const [stringNumber, stringName] of Object.entries(stringNames)) {
if (this.#config.orientHorizontally == true) {
posX = stringNameFontSize / 2;
posY =
stringIndent + stringNumber * stringSpacing + stringNamesIndent;
} else {
posX =
stringIndent + stringNumber * stringSpacing + stringNamesIndent;
posY = stringNameFontSize / 2;
}
canvas.beginPath();
canvas.fillStyle = this.#config.colorLabel;
canvas.font = stringNameFontSize + "px Arial";
canvas.textAlign = "center";
canvas.textBaseline = "middle";
canvas.stroke();
canvas.fillText(stringName, posX, posY);
canvas.closePath();
}
if (this.#config.orientHorizontally == true) {
canvas.translate(
stringNameFontSize *
GuitarDiagramsWebComponent.stringNamePaddingFactor,
0,
);
} else {
canvas.translate(
0,
stringNameFontSize *
GuitarDiagramsWebComponent.stringNamePaddingFactor,
);
}
}
}
#drawFretNumber() {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let fretNumberFontSize = this.#scale(
GuitarDiagramsWebComponent.fretNumberFontSize,
);
if (this.#config.fretStartingNumber != 0) {
canvas.beginPath();
canvas.fillStyle = this.#config.colorLabel;
canvas.font = fretNumberFontSize + "px Arial";
canvas.textAlign = "right";
canvas.textBaseline = "middle";
canvas.stroke();
canvas.fillText(
this.#config.fretStartingNumber,
fretNumberFontSize / 2,
fretNumberFontSize / 2,
);
canvas.closePath();
if (this.#config.orientHorizontally == true) {
canvas.translate(0, fretNumberFontSize);
} else {
canvas.translate(fretNumberFontSize, 0);
}
}
}
#drawFretboard() {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let fretboardWidth = this.#scale(this.#getFretboardWidth());
let fretboardLength = this.#scale(this.#getFretboardLength());
if (this.#config.orientHorizontally == true) {
[fretboardWidth, fretboardLength] = [fretboardLength, fretboardWidth];
}
canvas.beginPath();
canvas.fillStyle = this.#config.colorFretboard;
canvas.fillRect(0, 0, fretboardWidth, fretboardLength);
canvas.closePath();
}
#drawNut() {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let fretboardWidth = this.#scale(this.#getFretboardWidth());
let nutThickness = this.#scale(GuitarDiagramsWebComponent.nutThickness);
if (this.#config.orientHorizontally == true) {
[fretboardWidth, nutThickness] = [nutThickness, fretboardWidth];
}
if (this.#config.fretStartingNumber == 0) {
canvas.beginPath();
canvas.fillStyle = this.#config.colorNut;
canvas.strokeStyle = this.#config.colorNutOutline;
canvas.rect(0, 0, fretboardWidth, nutThickness);
canvas.fill();
canvas.stroke();
canvas.closePath();
}
}
#drawAllFretMarkers() {
if (this.#config.fretMarkersEnabled) {
if (
this.#config.fretStartingNumber != 0 &&
this.#config.fretStartingNumber % 2 == 0
) {
this.#drawFretMarker(1);
this.#drawFretMarker(3);
} else {
if (this.#config.fretStartingNumber != 0) {
this.#drawFretMarker(0);
}
this.#drawFretMarker(2);
this.#drawFretMarker(4);
}
}
}
#drawFretMarker(paramFretNumber) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let fretboardWidth = this.#scale(this.#getFretboardWidth());
let fretSpacing = this.#scale(GuitarDiagramsWebComponent.fretSpacing);
let fretMarkerRadius = this.#scale(
GuitarDiagramsWebComponent.fretMarkerRadius,
);
let posX = fretboardWidth / 2;
let posY = fretSpacing / 2 + fretSpacing * paramFretNumber;
if (this.#config.orientHorizontally == true) {
[posX, posY] = [posY, posX];
}
canvas.beginPath();
canvas.arc(posX, posY, fretMarkerRadius, 0, 2 * Math.PI);
canvas.fillStyle = this.#config.colorFretMarkers;
canvas.fill();
canvas.closePath();
}
#drawAllFrets() {
for (let i = 0; i <= this.#config.fretCount; i++) {
if (i == 0 && this.#config.fretStartingNumber == 0) {
continue;
}
this.#drawFret(i);
}
}
#drawFret(paramFretNumber) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let fretThickness = this.#scale(GuitarDiagramsWebComponent.fretThickness);
let fretSpacing = this.#scale(GuitarDiagramsWebComponent.fretSpacing);
let fretboardWidth = this.#scale(this.#getFretboardWidth());
let posX = 0;
let posY = fretSpacing * paramFretNumber - fretThickness / 2;
if (posY < 0) {
posY = 0;
}
if (this.#config.orientHorizontally == true) {
[posX, posY] = [posY, posX];
[fretboardWidth, fretThickness] = [fretThickness, fretboardWidth];
}
canvas.beginPath();
canvas.fillStyle = this.#config.colorFrets;
canvas.rect(posX, posY, fretboardWidth, fretThickness);
canvas.fill();
canvas.closePath();
}
#drawAllStrings() {
for (let i = 1; i <= this.#config.stringNames.length; i++) {
this.#drawString(i);
}
}
#drawString(paramStringNumber) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
const stringCount = this.#config.stringNames.length;
let stringIndent = this.#scale(GuitarDiagramsWebComponent.stringIndent);
let stringSpacing = this.#scale(GuitarDiagramsWebComponent.stringSpacing);
let stringBaseWidth = this.#scale(
GuitarDiagramsWebComponent.stringBaseWidth,
);
let stringWidthFactor = this.#scale(
GuitarDiagramsWebComponent.stringWidthFactor,
);
let fretboardLength = this.#scale(this.#getFretboardLength());
let fretboardWidth = this.#scale(this.#getFretboardWidth());
let posX = 0;
let posY = 0;
let endX = 0;
let endY = 0;
if (this.#config.orientHorizontally == true) {
posX = 0;
posY =
fretboardWidth -
stringIndent -
(paramStringNumber - 1) * stringSpacing -
stringBaseWidth / 2;
endX = fretboardLength;
endY =
stringBaseWidth +
(stringCount - (paramStringNumber - 1)) * stringWidthFactor;
} else {
posX =
stringIndent +
(paramStringNumber - 1) * stringSpacing -
stringBaseWidth / 2;
posY = 0;
endX =
stringBaseWidth +
(stringCount - (paramStringNumber - 1)) * stringWidthFactor;
endY = fretboardLength;
}
canvas.beginPath();
canvas.fillStyle = this.#config.colorStrings;
canvas.moveTo(posX, posY);
canvas.rect(posX, posY, endX, endY);
canvas.fill();
canvas.closePath();
}
#drawMarker(paramMarker) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let stringCount = this.#config.stringNames.length;
let fretSpacing = this.#scale(GuitarDiagramsWebComponent.fretSpacing);
let nutThickness = this.#scale(GuitarDiagramsWebComponent.nutThickness);
let stringSpacing = this.#scale(GuitarDiagramsWebComponent.stringSpacing);
let stringIndent = this.#scale(GuitarDiagramsWebComponent.stringIndent);
let strokeWidth = this.#scale(this.#config.markerStrokeWidth);
let markerFontSize = this.#scale(this.#config.markerFontSize);
let posX =
stringIndent +
(stringCount - paramMarker.string) * stringSpacing +
strokeWidth * 0.5;
let posY = (paramMarker.fret - 1) * fretSpacing + fretSpacing / 2;
if (paramMarker.fret == 0) {
posY = nutThickness / 2;
}
if (this.#config.orientHorizontally == true) {
posX =
stringIndent +
(paramMarker.string - 1) * stringSpacing +
strokeWidth * 0.5;
[posX, posY] = [posY, posX];
}
const boundingRect = this.shadowRoot
.querySelector("canvas")
.getBoundingClientRect();
paramMarker.relativePosX = posX;
paramMarker.relativePosY = posY;
paramMarker.posX = paramMarker.relativePosX;
paramMarker.posY = paramMarker.relativePosY;
if (this.#config.orientHorizontally == true) {
paramMarker.posX +=
GuitarDiagramsWebComponent.stringNameFontSize *
GuitarDiagramsWebComponent.stringNamePaddingFactor *
this.#config.scaleFactor;
} else {
paramMarker.posY +=
GuitarDiagramsWebComponent.stringNameFontSize *
GuitarDiagramsWebComponent.stringNamePaddingFactor *
this.#config.scaleFactor;
}
// paramMarker.posX = posX + boundingRect.left - window.pageXOffset;
// paramMarker.posY =
// posY +
// boundingRect.top -
// window.pageYOffset +
// GuitarDiagramsWebComponent.stringNameFontSize *
// GuitarDiagramsWebComponent.stringNamePaddingFactor *
// this.#config.scaleFactor;
// if (this.#config.orientHorizontally == true) {
// paramMarker.posX +=
// GuitarDiagramsWebComponent.stringNameFontSize *
// GuitarDiagramsWebComponent.stringNamePaddingFactor *
// this.#config.scaleFactor;
// paramMarker.posY -=
// GuitarDiagramsWebComponent.stringNameFontSize *
// GuitarDiagramsWebComponent.stringNamePaddingFactor *
// this.#config.scaleFactor;
// }
canvas.beginPath();
canvas.fillStyle = paramMarker.colorFill;
canvas.strokeStyle = paramMarker.colorStroke;
canvas.lineWidth = strokeWidth;
switch (paramMarker.shape) {
case GuitarDiagramsWebComponent.Shape.Square:
this.#drawMarkerSquare(posX, posY);
break;
case GuitarDiagramsWebComponent.Shape.Triangle:
this.#drawMarkerTriangle(posX, posY);
break;
case GuitarDiagramsWebComponent.Shape.Diamond:
this.#drawMarkerDiamond(posX, posY);
break;
default:
this.#drawMarkerCircle(posX, posY);
}
canvas.closePath();
canvas.beginPath();
canvas.fillStyle = paramMarker.colorFont;
canvas.textAlign = "center";
canvas.textBaseline = "middle";
canvas.font = markerFontSize + "px Arial";
canvas.fillText(paramMarker.text, posX, posY + strokeWidth);
canvas.fill();
canvas.closePath();
}
#drawMarkerSquare(paramPosX, paramPosY) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let markerRadius = this.#scale(GuitarDiagramsWebComponent.markerRadius);
canvas.fillRect(
paramPosX - markerRadius,
paramPosY - markerRadius,
markerRadius * 2,
markerRadius * 2,
);
canvas.strokeRect(
paramPosX - markerRadius,
paramPosY - markerRadius,
markerRadius * 2,
markerRadius * 2,
);
}
#drawMarkerCircle(paramPosX, paramPosY) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let markerRadius = this.#scale(GuitarDiagramsWebComponent.markerRadius);
canvas.arc(paramPosX, paramPosY, markerRadius, 0, 2 * Math.PI);
canvas.fill();
canvas.stroke();
}
#drawMarkerTriangle(paramPosX, paramPosY) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let markerRadius = this.#scale(GuitarDiagramsWebComponent.markerRadius);
let triangleMarkerRadius = markerRadius * 1.25;
let triangleMarkerHeight = triangleMarkerRadius * 2 * (Math.sqrt(3) / 2);
canvas.moveTo(paramPosX, paramPosY - triangleMarkerRadius);
canvas.lineTo(
paramPosX + triangleMarkerRadius,
paramPosY - triangleMarkerRadius + triangleMarkerHeight,
);
canvas.lineTo(
paramPosX - triangleMarkerRadius,
paramPosY - triangleMarkerRadius + triangleMarkerHeight,
);
canvas.lineTo(paramPosX, paramPosY - triangleMarkerRadius);
canvas.fill();
canvas.stroke();
}
#drawMarkerDiamond(paramPosX, paramPosY) {
const canvas = this.shadowRoot.querySelector("canvas").getContext("2d");
let markerRadius = this.#scale(GuitarDiagramsWebComponent.markerRadius);
let diamondMarkerRadius = markerRadius * 1.25;
canvas.moveTo(paramPosX, paramPosY - diamondMarkerRadius);
canvas.lineTo(paramPosX + diamondMarkerRadius, paramPosY);
canvas.lineTo(paramPosX, paramPosY + diamondMarkerRadius);
canvas.lineTo(paramPosX - diamondMarkerRadius, paramPosY);
canvas.lineTo(paramPosX, paramPosY - diamondMarkerRadius);
canvas.fill();
canvas.stroke();
}
#addControls() {
if (this.#config.controlsEnabled) {
const controlMargins = ".5em .5em 0 0";
const controlClass = "guitar-diagrams-control";
const controlClassPrefix = "guitar-diagrams-";
const canvasElement = this.shadowRoot.querySelector("canvas");
const controlsDiv = this.#docRoot.createElement("div");
controlsDiv.style = "display: block; margin-top: .5em";
canvasElement.insertAdjacentElement("afterend", controlsDiv);
if (
this.#config.changeOrientationEnabled &&
!this.#docRoot.getElementById(
this.#config.canvasID + "-change-orientation-button",
)
) {
let changeOrientationButton = this.#docRoot.createElement("input");
changeOrientationButton.type = "button";
changeOrientationButton.id =
this.#config.canvasID + "-change-orientation-button";
changeOrientationButton.style = "margin: " + controlMargins + ";";
changeOrientationButton.classList.add(
controlClassPrefix + "change-orientation-button",
);
changeOrientationButton.classList.add(controlClass);
changeOrientationButton.value = String.fromCodePoint(0x1f500);
changeOrientationButton.addEventListener("click", () => {
this.#config.orientHorizontally = !this.#config.orientHorizontally;
this.drawNeck();
this.drawAllMarkers();
});
controlsDiv.insertAdjacentElement("afterend", changeOrientationButton);
}
if (
this.#config.downloadImageEnabled &&
!this.#docRoot.getElementById(
this.#config.canvasID + "-download-image-button",
)
) {
let downloadButton = this.#docRoot.createElement("input");
downloadButton.type = "button";
downloadButton.id = this.#config.canvasID + "-download-image-button";
downloadButton.style = "margin: " + controlMargins + ";";
downloadButton.classList.add(
controlClassPrefix + "download-image-button",
);
downloadButton.classList.add(controlClass);
downloadButton.value = String.fromCodePoint(0x1f4be);
downloadButton.addEventListener("click", () => {
const canvas = this.shadowRoot.querySelector("canvas");
const dataURL = canvas.toDataURL("image/png");
let a = this.#docRoot.createElement("a");
a.href = dataURL;
a.download = this.#config.canvasID + ".png";
a.click();
});
controlsDiv.insertAdjacentElement("afterend", downloadButton);
}
}
}
#getFretboardLength() {
let fretSpacing = GuitarDiagramsWebComponent.fretSpacing;
let fretLength = GuitarDiagramsWebComponent.fretThickness;
let fretboardLength = this.#config.fretCount * fretSpacing + fretLength / 2;
return fretboardLength;
}
#getFretboardWidth() {
let stringSpacing = GuitarDiagramsWebComponent.stringSpacing;
let stringIndent = GuitarDiagramsWebComponent.stringIndent;
let fretboardWidth =
this.#config.stringNames.length * stringSpacing + stringIndent / 2;
return fretboardWidth;
}
#scale(paramVector) {
let scale = paramVector;
scale = this.#config.scaleFactor * paramVector;
return scale;
}
drawNeck() {
this.#initializeDrawing();
this.#drawStringNames();
this.#drawFretNumber();
this.#drawFretboard();
this.#drawNut();
this.#drawAllFretMarkers();
this.#drawAllFrets();
this.#drawAllStrings();
this.#addControls();
}
addMarker(
paramString,
paramFret,
paramText = "",
paramShape = null,
playSound = false,
callbacks = null,
) {
let stringCount = this.#config.stringNames.length;
let minFret = 0;
let maxFret = this.#config.fretCount;
if (
paramString < 1 ||
paramString > stringCount ||
paramFret < minFret ||
paramFret > maxFret
) {
console.log(
"[State] GuitarDiagramsWebComponent.addMarker(): " +
"{ minFret: " +
minFret +
" | maxFret: " +
maxFret +
" }",
);
console.log(
"[Error] GuitarDiagramsWebComponent.addMarker(): Fret marker could not be placed on fretboard." +
" { paramString: " +
paramString +
" | paramFret: " +
paramFret +
" | paramText: " +
paramText +
" | paramShape: " +
paramShape +
" }",
);
} else {
let marker = new GuitarDiagramsJSMarker();
marker.string = paramString;
marker.fret = paramFret;
marker.text = paramText;
marker.playSound = playSound;
marker.shape =
paramShape == null
? GuitarDiagramsWebComponent.Shape.Circle
: paramShape;
marker.eventCallbacks = callbacks == null ? new Map() : callbacks;
if (playSound) {
marker.eventCallbacks.set("click", (elem) => {
this.#outputSpeaker.triggerAttackRelease(
this.markerToNoteFrequency(marker),
"2n",
);
});
marker.eventCallbacks.set("mousemove", (elem) => {
console.log("marker hovered");
});
}
marker.eventCallbacks.forEach((v, k) => {
if (!this.#events.includes(k)) {
this.#events.push(k);
}
});
this.#markers.push(marker);
}
}
setUpEventListeners() {
this.#events.forEach((event) => {
this.eventTarget.addEventListener(event, (event) => {
// Get the canvas element and its current bounding rectangle
const canvas = this.shadowRoot.querySelector("canvas");
const rect = canvas.getBoundingClientRect();
// Calculate mouse position relative to the canvas
const mouseX = event.clientX - rect.left;
const mouseY = event.clientY - rect.top;
let markersWithEvent = this.#markers.filter((marker) => {
if (marker.eventCallbacks.size > 0) {
let markerEvents = Array.from(marker.eventCallbacks.keys());
return markerEvents.length > 0 && markerEvents.includes(event.type);
}
});
if (event.type == "click") {
markersWithEvent.forEach((marker) => {
if (marker.shape == GuitarDiagramsWebComponent.Shape.Circle) {
if (
Math.pow(marker.posX - mouseX, 2) +
Math.pow(marker.posY - mouseY, 2) <
Math.pow(
GuitarDiagramsWebComponent.markerRadius *
this.#config.scaleFactor,
2,
)
) {
marker.eventCallbacks.get(event.type)();
}
}
});
}
if (event.type == "mousemove") {
console.log("x: " + mouseX + "\ty: " + mouseY);
let cursorPointer = false;
markersWithEvent.forEach((marker) => {
if (marker.shape == GuitarDiagramsWebComponent.Shape.Circle) {
if (
Math.pow(marker.posX - mouseX, 2) +
Math.pow(marker.posY - mouseY, 2) <
Math.pow(
GuitarDiagramsWebComponent.markerRadius *
this.#config.scaleFactor,
2,
)
) {
cursorPointer = true;
const canvas = this.shadowRoot
.querySelector("canvas")
.getContext("2d");
canvas.beginPath();
canvas.fillStyle = "yellow";
this.#drawMarkerCircle(
marker.relativePosX,
marker.relativePosY,
);
canvas.closePath();
} else {
const canvas = this.shadowRoot
.querySelector("canvas")
.getContext("2d");
canvas.beginPath();
canvas.fillStyle = marker.colorFill;
this.#drawMarkerCircle(
marker.relativePosX,
marker.relativePosY,
);
canvas.closePath();
}
}
});
if (cursorPointer == true) {
canvas.style.cursor = "pointer";
} else {
canvas.style.cursor = "default";
}
}
});
});
}
playAllMarkers() {
let allNoteFrequencies = this.#markers.map((marker) =>
this.markerToNoteFrequency(marker),
);
// play slowly
let timeGap = 0;
let now = Tone.now();
allNoteFrequencies.forEach((freq) => {
this.#outputSpeaker.triggerAttack(freq, now + timeGap);
timeGap += 0.5;
});
this.#outputSpeaker.triggerRelease(allNoteFrequencies, now + timeGap + 0.5);
timeGap += 0.5;
// play faster
allNoteFrequencies.forEach((freq) => {
this.#outputSpeaker.triggerAttack(freq, now + timeGap);
timeGap += 0.055;
});
this.#outputSpeaker.triggerRelease(allNoteFrequencies, now + timeGap + 2);
}
drawAllMarkers() {
const self = this;
this.#markers.forEach((marker) => {
this.#drawMarker(marker);
});
this.setUpEventListeners();
}
logAllMarkers() {
if (this.#markers.length > 0) {
this.#markers.forEach((marker) => {
console.log("Marker text: " + marker);
});
}
}
getCanvasElement() {
let canvasElement = document.createElement("canvas");
canvasElement.id = this.#config.canvasID;
canvasElement.classList.add("guitar-diagrams-canvas");
return canvasElement;
}
markerToNoteFrequency(marker) {
let baseFreq = 0;
if (marker.string == 1) {
baseFreq = 330;
} else if (marker.string == 2) {
baseFreq = 247;
} else if (marker.string == 3) {
baseFreq = 196;
} else if (marker.string == 4) {
baseFreq = 147;
} else if (marker.string == 5) {
baseFreq = 110;
} else {
baseFreq = 82;
}
const semitoneOffset = Math.pow(2, marker.fret / 12);
let finalNote = baseFreq * semitoneOffset;
return finalNote;
}
}
customElements.define("my-fretboard", GuitarDiagramsWebComponent);
export { GuitarDiagramsJSConfig } from "./guitar-diagrams-config.mjs";
export { GuitarDiagramsJSMarker } from "./guitar-diagrams-marker.mjs";

@ -0,0 +1,3 @@
<fretboard canvas-id="myCanvas" orient-horizontally="true" fret-count="12" scale-factor="1">
<marker string="1" fret="1"></marker>
</fretboard>

@ -0,0 +1,14 @@
<html>
<head>
<style>
cursor-pointer {
cursor: pointer;
}
</style>
<script type="module" src="index.js"></script>
<title>Test</title>
</head>
<body>
<div id="diagram-1"></div>
</body>
</html>

@ -0,0 +1,37 @@
import { GuitarDiagramsJS } from "./guitar-diagrams.mjs";
import { GuitarDiagramsJSMarker } from "./guitar-diagrams-marker.mjs";
let gdj2 = new GuitarDiagramsJS();
gdj2.config.canvasID = "diagram-2-canvas";
gdj2.config.stringNamesEnabled = true;
gdj2.config.orientHorizontally = true; // set horizontal orientation
gdj2.config.scaleFactor = 2;
gdj2.addCanvasToElement("diagram-1");
gdj2.drawNeck();
gdj2.addMarker(4, 2, "", null, true); // add a default (circle) marker on string 3, fret 1, text empty
gdj2.addMarker(3, 0, "", null, true);
gdj2.eventTarget = document.getElementById("diagram-2-canvas");
gdj2.addMarker(2, 2, "", null, true, null);
gdj2.addMarker(1, 0, "", null, true, null);
gdj2.addMarker(5, 0, "", null, true, null);
// gdj2.addMarker(
// 4,
// 3,
// "",
// null,
// new Map([
// [
// "click",
// function () {
// console.log("Marker clicked:", this.toString());
// },
// ],
// [
// "mouseenter",
// function () {
// console.log("Marker clicked:", this.toString());
// },
// ],
// ]),
// );
gdj2.drawAllMarkers(); // draw the markersgdj1.addCanvasToElement("diagram-1"); // add the canvas to the specified element ID on the page

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save