import { GuitarDiagramsJSConfig } from "./guitar-diagrams-config.mjs"; import { GuitarDiagramsJSMarker } from "./guitar-diagrams-marker.mjs"; import "./tone.js"; import { SampleLibrary } from "./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() { if (this.getAttribute("center-align") == "true") { let canvas = this.#docRoot.getElementById(this.#config.canvasID); // canvas.style["position"] = "absolute"; // canvas.style["left"] = "50vw"; // canvas.style["transform"] = "translate(-50%, -50%)"; // // canvas.style["padding-left"] = 0; // canvas.style["padding-right"] = 0; // canvas.style["margin-left"] = "auto"; // canvas.style["margin-right"] = "auto"; // canvas.style["display"] = "block"; // canvas.style["width"] = "1000px"; // canvas.style.width = "50vw"; canvas.style.display = "block"; canvas.style.margin = "0 auto"; canvas.style.position = "relative"; canvas.style.left = "50%"; canvas.style["padding-bottom"] = "1em"; canvas.style.transform = "translateX(-50%)"; } 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, ); }); if (markerList.length > 0 && this.getAttribute("show-chord") == "true") { 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", "center-align", ]; } 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; case "center-align": if (newValue == "true") { let canvas = this.#docRoot.getElementById(this.#config.canvasID); // canvas.style["position"] = "absolute"; // canvas.style["left"] = "50vw"; // canvas.style["transform"] = "translate(-50%, -50%)"; // // canvas.style["padding-left"] = 0; // canvas.style["padding-right"] = 0; // canvas.style["margin-left"] = "auto"; // canvas.style["margin-right"] = "auto"; // canvas.style["display"] = "block"; // canvas.style["width"] = "1000px"; // canvas.style.width = "50vw"; canvas.style.display = "block"; canvas.style.margin = "0 auto"; canvas.style.position = "relative"; canvas.style.left = "50%"; canvas.style.transform = "translateX(-50%)"; } } 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]; } // if (this.#docRoot.getElementById(this.#config.canvasID).style.width) { // canvasWidth = this.#docRoot.getElementById(this.#config.canvasID).style // .width; // } canvas.setAttribute("width", canvasWidth); if (this.getAttribute("center-align") == "true") { canvas.style["width"] = canvasWidth + "px"; } 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); this.#drawFretMarker(6); } } } #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";