Meine Intention: Auf einer Website soll man ein hochgeladenes Bild wie rechts abgebildet auf Wunsch beschneiden können. Das Resultat landet wieder als (neue) Bilddatei auf dem Webserver.

Die JavaScript-Funktionen, um den Beschneidungsbereich im Browser auswählen zu können, liefert hierbei die Bibliothek cropper.
Das beschnittene Bild wird dann im Hintergrund mit Hilfe von PHP erstellt (cropper bietet hier zwar eine direkte Lösung, allerdings muss der eingesetzte Browser hierfür HTMLCanvasElement.toBlob verstehen und das sind mir für meinen Geschmack zu wenige Browser, die das können).

HTML

Das im Browser dargestellte Bild (welches beschnitten werden soll) muss innerhalb eines Containers platziert sein.
Der Container hat bei mir eine vorgegebene maximale Breite und damit das Bild nicht abgeschnitten angezeigt wird, helfe ich mir mit
max-width:100%
aus.

Von den ganzen Dateien, die cropper auf github zum Download feilbietet, brauchen wir nur zwei einzubinden.

<script language='JavaScript' type='text/javascript' src='cropper.js'></script>
<link rel='stylesheet' type='text/css' href='cropper.css'/>

<div id='vorgabe'>
   <img id='vorgabe_img' src='/pfad/zu/bild.jpg' style='max-width:100%;'>
</div>

JavaScript

Mit folgenden Zeilen wird das Bild mit einem blauen Rahmen und Anfassern ausgestattet, welche sich dann im Browserfenster zurecht ziehen lassen, um den beschnitteten Bereich zu markieren.

Die mitgegebenen Optionen sind eigentlich nicht notwendig, allerdings benötige ich die responsive-Option nicht (welche standardmäßig bei jeder Änderung der Fenstergröße alles neu rendert), die guides, welche standardmäßig mit in den Ausschnitt-Bereich rein gezeichnet werden, stören und mit autoCropArea:1 sorgt man dafür, dass zu Anfang das gesamte Bild ausgewählt ist (Standard-Wert ist wieso-auch-immer 0.8).

var cropper = $('#vorgabe_img').cropper({
  responsive: false,
  guides:false,
  autoCropArea:1
  });

Da in der Beschreibung nicht so ganz offensichtlich: Rückgängig macht man diese ganze Sache wieder mit einem:

$('#vorgabe_img').cropper("destroy");

Hat der Benutzer sich das Rechteck zurecht gezogen und will sich das beschnittene Bild generieren lassen, greifen wir auf folgenden Codeschnipsel zurück:

// Liefert ein Objekt wie z.B. {left: 29, top: 17.488008342022937, width: 228, height: 155.02398331595413}
// Achtung: Die Werte beziehen sich nicht auf das Originalbild
// (welches evtl. im Browser skaliert wurde), sondern auf die Größe des Bildes im Browser!
var box = $('#vorgabe_img').cropper('getCropBoxData');

// Um später in PHP die Werte für die Originalgröße berechnen zu können
// holen wir uns noch die Größe des Bildes, wie es im Browser angezeigt wird
// Achtung: Das Img-Objekt #vorgabe_img wurde beim Initiieren von cropper
// ausgeblendet und liefert keine Werte. Ich nehme den darüberliegenden Container
box.imgWidth  = $("#vorgabe").width();
box.imgHeight = $("#vorgabe").height();

// Per AJAX an ein PHP-Skript senden und nach abgeschlossener Bearbeitung
// seitens PHP können wir die Cropper-Funktion entfernen.
$.ajax('/pfad/zum/skript.php', {
    method: "POST",
    data: box,
    success: function () {
      $('#vorgabe_img').cropper("destroy");
      },
    error: function () {
      console.log('Oh...da ist was schief gelaufen.');
      }
    });

PHP

Hier greifen wir auf die GD-/Image-Funktionen von PHP zurück, die schon seit PHP 4 existieren.
// Originalbild in Speicher lesen und einen Dummy für das neue Bild öffnen
$oldimg  = imagecreatefromjpeg("/pfad/zu/bild.jpg");
$oHeight = imagesy($oldimg);                // Bildhöhe Originalbild
$faktor  = $oHeight/$_POST['imgHeight'];    // Skalierungsfaktor Originalbild/Browseransicht
$nWidth  = round($_POST['width']*$faktor);  // Breite beschnittenes Bild
$nHeight = round($_POST['height']*$faktor); // Höhe beschnittenes Bild
$newimg  = imagecreatetruecolor($nWidth, $nHeight);
// Jetzt das eigentliche Beschneiden
imagecopyresampled($newimg, $oldimg, 0, 0, round($_POST['left']*$faktor), round($_POST['top']*$faktor), $nWidth, $nHeight, $nWidth, $nHeight);
// Das beschnittene Bild speichern wir als neue JPEG-Datei mit 80% Qualität
imagejpeg($newimg, "/pfad/zu/neuem_bild.jpg", 80);