
Originariamente inviata da
lanvoel
Programmo per il gusto di programmare e vedere riuscire i codici.
Ottimo, e questo ci sta tutto
ma anche senza reinventare l'acqua calda potresti appoggiarti o prendere spunto da script già esistenti con cui ottenere questa funzionalità in modo più ottimale.

Originariamente inviata da
lanvoel
per ottenere il ritaglio non basta cliccare sul pulsante "ritaglia"
Le tue indicazioni erano chiare. Avevo già analizzato il tuo codice e visto anche già come "correggerlo".
Come anticipato, non ti nego che sia un bel garbuglio di errori logici e strutturali. A mio modesto parere sarebbe meglio buttare giù tutto e rifarlo da capo con criterio.
Cerco comunque di spiegarti i punti cruciali, sempre che riesca a farmi capire 
Inizialmente hai dichiarato la costante selectPhoto che fa riferimento all'elemento input #photo e gli hai impostato la gestione dell'evento change.
codice:
const selectPhoto = document.getElementById('photo');
selectPhoto.addEventListener('change', ...
Questa costante è globale perché l'hai definita nel livello principale dello script; quindi è accessibile in qualsiasi punto dello script. Fino a qui tutto ok.
Per il change hai applicato una funzione (listener) dove definisci l'oggetto img che fa riferimento all'elemento immagine da elaborare.
Tieni conto che la variabile img l'hai definita localmente dentro questo listener; questo significa che NON è accessibile al di fuori di tale funzione.
Poi hai impostato l'onclick per il pulsante ritaglia, che richiama la relativa funzione ritaglia().
Ora, dentro questa funzione, la costante selectPhoto (quella definita a livello globale) sarebbe anche visibile ed accessibile per ottenere il riferimento a quell'elemento #photo. Oltretutto l'evento change è già stato impostato inizialmente. Tuttavia non puoi accedere direttamente alla variabile img (che hai definito nel primo listener) dal momento che l'hai definita localmente dentro il listener.
L'errore sta nel fatto che dentro ritaglia() hai ridefinito tutto ciò che avevi già definito inizialmente: un'altra costante selectPhoto per quello stesso elemento e a cui vai ad impostare nuovamente il change con un nuovo listener in cui dentro hai una nuova variabile img locale.
Ogni volta che il pulsante ritaglia viene premuto, non fai altro che aggiungere una ulteriore gestione del change per la quale viene appunto atteso questo evento prima di procedere all'elaborazione attraverso la sua funzione listener.
Ci sono 3 principali errori logici da prendere in considerazione:
1 - la gestione del change non devi ridefinirla dentro ritaglia() ma "sfruttare" quella stessa che hai definito già a livello globale per ottenere in qualche modo il riferimento all'oggetto immagine che viene "selezionato" inizialmente.
2 - ridefinendo l'evento change dentro ritaglia() (che di per sé è comunque già sbagliato) viene attribuito un relativo listener che non vai però a rimuovere. Per cui, non solo devi riselezionare l'immagine, ma tali listener (aggiunti e mai rimossi) vanno a sommarsi man mano, ogni volta che si preme il pulsante ritaglia, creando quindi una catena di esecuzione molteplice dello stesso script presente nel listener stesso.
3 - la variabile img dentro il listener iniziale, è definita localmente; in questo modo non puoi accederci per analizzare quell'oggetto immagine che già è stato selezionato inizialmente.
In breve, non devi ridefinire la gestione del change dentro ritaglia(); piuttosto dovresti definire la variabile img a livello globale in modo che lo stesso oggetto immagine (definito con la selezione inizialmente) possa risultare accessibile anche dentro la funzione ritaglia().
Tra le altre cose, dentro ritaglia, vedo che non ripulisci il canvas crtx prima di ridisegnare l'immagine. Questo va a sovrapporre man mano i ritagli. Forse dovresti pulirlo come hai fatto per ctx.
Qui le correzioni che ho potuto apportare allo script:
codice:
const c = document.getElementById("lavagna");
const ctx = c.getContext("2d");
const cr = document.getElementById("lavagnaR");
const crtx = cr.getContext("2d");
let ratio = null;
let img = new Image(); // Questo oggetto, definito a livello globale, può essere visto in qualsiasi punto dello script
const selectPhoto = document.getElementById('photo');
selectPhoto.addEventListener('change', (e) => {
let url = URL.createObjectURL(e.target.files[0]);
// Qui viene definita l'immagine selezionata
img.src = url;
img.onload = () => {
(img.width > img.height) ? ratio = img.width / c.width: ratio = img.height / c.height // calcola il rappaorto tra le misure dell'immagine e del canvas
let newWidth;
let newHeight;
if (ratio <= 1) {
newWidth = img.width;
newHeight = img.height;
} else {
newWidth = img.width / ratio;
newHeight = img.height / ratio;
}
const x = (c.width - newWidth) / 2;
const y = (c.height - newHeight) / 2;
ctx.clearRect(0, 0, c.width, c.height);
ctx.drawImage(img, x, y, newWidth, newHeight);
URL.revokeObjectURL(url);
lpx.value = img.width;
apx.value = img.height;
lrpx.value = newWidth;
arpx.value = newHeight;
}
})
function ritaglia() {
let ratio = null;
// Qui non hai bisogno di reimpostare il sistema di selezione dell'immagine ma puoi riferirti direttamente all'oggetto "img" globale
const
oix1 = document.getElementById('oix').value,
oiy1 = document.getElementById('oiy').value,
lx1 = document.getElementById('lx').value,
ay1 = document.getElementById('ay').value,
oidx1 = document.getElementById('oidx').value,
oidy1 = document.getElementById('oidy').value,
ldx1 = document.getElementById('ldx').value,
ady1 = document.getElementById('ady').value
;
crtx.clearRect(0, 0, cr.width, cr.height); // Anche qui, come sopra, andrà ripulito il relativo canvas prima di ridisegnarci l'immagine
crtx.drawImage(img, oix1, oiy1, lx1, ay1, oidx1, oidy1, ldx1, ady1);
}
Ho commentato i punti salienti.
Spero sia tutto chiaro. Fai sapere.
[EDIT]
PS: chiaramente quoto in toto alka