import { Conductor } from "./conductor.js"; export class Renderer { timeIndicator; currStaveNumber; numStaves; sheetWindow; canvases = []; iconCache = []; iconScale = 0.20; constructor() { this.numStaves = 6; this.currStaveNumber = 1; this.timeIndicator = document.getElementById("time-indicator"); this.sheetWindow = document.getElementById("music-window"); window.addEventListener("resize", () => { this.redrawIcons(); }); this.timeIndicator.addEventListener("animationiteration", () => { this.currStaveNumber++; if (this.currStaveNumber > this.numStaves) { this.cleanUpAndRestart(); } }); for (let i = 1; i <= this.numStaves; i++) { const staveWrapper = document.createElement("div"); staveWrapper.classList.add("stave-wrapper"); staveWrapper.setAttribute("id", `stave-wrapper-${i}`); staveWrapper.style.position = "relative"; const staveObject = document.createElement("object"); staveObject.type = "image/svg+xml"; staveObject.data = "assets/svg/stave.svg"; staveObject.className = "stave-svg"; // Fixed className const canvas = document.createElement("canvas"); canvas.id = `canvas-${i}`; canvas.className = "event-canvas"; const ctx = canvas.getContext("2d"); this.canvases.push({ canvas, ctx }); staveWrapper.appendChild(staveObject); staveWrapper.appendChild(canvas); this.sheetWindow.appendChild(staveWrapper); // Your exact sizing method + minimal DPR adjustment requestAnimationFrame(() => { const dpr = window.devicePixelRatio || 1; // Keep your offset measurements const displayWidth = canvas.offsetWidth; const displayHeight = canvas.offsetHeight; // Apply to actual canvas buffer canvas.width = Math.round(displayWidth * dpr); canvas.height = Math.round(displayHeight * dpr); // Maintain your display size canvas.style.width = `${displayWidth}px`; canvas.style.height = `${displayHeight}px`; // Scale context to compensate ctx.scale(dpr , dpr); }); } } async placeIcon(instrument) { if (this.currStaveNumber > this.numStaves) { this.currStaveNumber = 1; this.iconCache.length = 0; } const rect = this.timeIndicator.getBoundingClientRect(); const sheetLeft = this.sheetWindow.getBoundingClientRect().left; const xPosition = rect.left + window.scrollX - sheetLeft; const canvasEntry = this.canvases[this.currStaveNumber - 1]; const canvas = canvasEntry.canvas; const ctx = canvasEntry.ctx; const yPercent = parseFloat(instrument.yPos); const y = (yPercent / 100) * canvas.height; const width = instrument.width * this.iconScale; const height = instrument.height * this.iconScale; ctx.drawImage(instrument.image, xPosition, y, width, height); this.iconCache.push({ image: instrument.image, x: xPosition, y, width, height, stave: this.currStaveNumber, }); } redrawIcons() { this.canvases.forEach(({ ctx, canvas }) => { ctx.clearRect(0, 0, canvas.width, canvas.height); }); this.iconCache.forEach(({ image, x, y, width, height, stave }) => { const ctx = this.canvases[stave - 1].ctx; ctx.drawImage(image, x, y, width, height); }); } cleanUpAndRestart(reload = false) { this.iconCache = []; this.currStaveNumber = 1; this.canvases.forEach(({ ctx, canvas }) => { ctx.clearRect(0, 0, canvas.width, canvas.height); }); Conductor.setTitle(); if (reload) location.reload(); } }