63 votes

Recadrage des images dans le navigateur AVANT le téléchargement

De nombreuses bibliothèques que j'ai trouvées, comme Jcrop, ne font pas réellement le recadrage, elles créent seulement une interface utilisateur de recadrage d'image. Elle dépend alors du serveur qui effectue le recadrage réel.

Comment puis-je faire le recadrage de l'image côté client en utilisant des Fonctionnalité HTML5 sans utiliser de code côté serveur.

Si oui, existe-t-il des exemples ou des conseils ?

45voto

gion_13 Points 15594

Oui, c'est possible.
Il est basé sur le nouvel attribut html5 "download" des balises d'ancrage.
Le flux devrait être quelque chose comme ceci :

  1. charger l'image
  2. dessine l'image dans un canevas avec les limites de recadrage spécifiées
  3. récupérer les données de l'image depuis le canevas et en faire une href pour une balise d'ancrage dans le dom
  4. ajouter l'attribut de téléchargement ( download="desired-file-name" ) à cette a élément C'est tout. Tout ce que l'utilisateur doit faire est de cliquer sur votre "lien de téléchargement" et l'image sera téléchargée sur son ordinateur.

Je reviendrai avec une démo quand j'en aurai l'occasion.

Mise à jour
Voici la démo en direct comme je l'ai promis. Il prend le logo de jsfiddle et coupe 5px de chaque marge.
Le code ressemble à ceci :

var img = new Image();
img.onload = function(){
    var cropMarginWidth = 5,
        canvas = $('<canvas/>')
                    .attr({
                         width: img.width - 2 * cropMarginWidth,
                         height: img.height - 2 * cropMarginWidth
                     })
                    .hide()
                    .appendTo('body'),
        ctx = canvas.get(0).getContext('2d'),
        a = $('<a download="cropped-image" title="click to download the image" />'),
        cropCoords = {
            topLeft : {
                x : cropMarginWidth,
                y : cropMarginWidth 
            },
            bottomRight :{
                x : img.width - cropMarginWidth,
                y : img.height - cropMarginWidth
            }
        };

    ctx.drawImage(img, cropCoords.topLeft.x, cropCoords.topLeft.y, cropCoords.bottomRight.x, cropCoords.bottomRight.y, 0, 0, img.width, img.height);
    var base64ImageData = canvas.get(0).toDataURL();

    a
        .attr('href', base64ImageData)
        .text('cropped image')
        .appendTo('body');

    a
        .clone()
        .attr('href', img.src)
        .text('original image')
        .attr('download','original-image')
        .appendTo('body');

    canvas.remove();
}
img.src = 'some-image-src';

Mise à jour II
J'ai oublié de mentionner qu'il y a bien sûr un inconvénient :(.
En raison de la politique d'origine identique appliquée aux images, si vous voulez accéder aux données d'une image (par la méthode canvas toDataUrl ).
Vous aurez donc toujours besoin d'un proxy côté serveur qui servira votre image comme si elle était hébergée sur votre domaine.

Mise à jour III Bien que je ne puisse pas fournir de démonstration en direct (pour des raisons de sécurité), voici un exemple de code php qui résout le problème de la politique d'origine identique :

fichier proxy.php :

$imgData = getimagesize($_GET['img']);
header("Content-type: " . $imgData['mime']);
echo file_get_contents($_GET['img']);  

De cette façon, au lieu de charger l'image externe directement depuis son origine :

img.src = 'http://some-domain.com/imagefile.png';

Vous pouvez le charger via votre proxy :

img.src = 'proxy.php?img=' + encodeURIComponent('http://some-domain.com/imagefile.png');  

Et voici un exemple de code php pour enregistrer les données de l'image (base64) dans une image réelle :

fichier save-image.php :

$data = preg_replace('/data:image\/(png|jpg|jpeg|gif|bmp);base64/','',$_POST['data']);
$data = base64_decode($data);
$img = imagecreatefromstring($data);

$path = 'path-to-saved-images/';
// generate random name
$name  = substr(md5(time()),10);
$ext = 'png';
$imageName = $path.$name.'.'.$ext;

// write the image to disk
imagepng($img,  $imageName);
imagedestroy($img);
// return the image path
echo $imageName;

Tout ce que vous avez à faire, c'est d'envoyer les données de l'image dans ce fichier et il enregistrera l'image sur le disque et vous renverra le nom de fichier de l'image existante.

Bien sûr, tout cela peut sembler un peu compliqué, mais je voulais vous montrer que ce que vous essayez de faire est possible.

13voto

user2626061 Points 230

Oui. HTML 5 peut vous aider à le réaliser. Mais c'est assez compliqué, car il faut tenir compte du problème de la compatibilité et des étapes complexes. J'ai cherché pendant presque une journée et je n'ai pas trouvé de plugin js gratuit. Cependant, j'ai trouvé une extension Jcrop bien intégrée et multi-navigateur "Jcrop & Upload" qui utilise les technologies suivantes le plugin Jcrop pour recadrer les images, dessiner la zone recadrée dans l'élément HTML 5 canvas, convertir le canvas en un blob et le télécharger (jpg, png, etc.) vers le serveur par AJAX. . Par conséquent, il n'a pas besoin d'être converti en backend. Il a plus de 2000 LOC et le code principal est le suivant :

function canvastoBlob (canvas, type, quality) {
    var dataURL = canvas.toDataURL(type, quality);
    var lines = dataURL.split(',');
    var byteString;
    if (lines[0].indexOf('base64') == -1) {
        // Convert base64/URLEncoded data to raw binary data
        byteString = decodeURIComponent(lines[1]);
    } else {
        // Convert base64 to raw binary data held in a string
        byteString = atob(lines[1]);
    }
    // Write the bytes to an ArrayBuffer
    var arrayBuffer = new ArrayBuffer(byteString.length);
    var intArray = new Uint8Array(arrayBuffer);
    for (var i = 0; i < byteString.length; i += 1) {
        intArray[i] = byteString.charCodeAt(i);
    }
    // get the mime type
    var mimeString = lines[0].split(':')[1].split(';')[0];
    // Write the ArrayBuffer to a blob
    var blob = new BlobBuilder();
    blob.append(arrayBuffer);
    blob = blob.getBlob(mimeString);
    return blob
}

Le code ci-dessus ne tient pas compte de la compatibilité entre les navigateurs. Ici est une démo en direct et le code complet est aquí . Il a deux fichiers js, mais vous pouvez les fusionner en un seul fichier js par copier-coller comme je l'ai fait. J'espère que cela pourra vous aider.

Au fait, je l'ai importé sur le site web de notre entreprise, et il fonctionne assez bien sur tous les principaux navigateurs (le Jcrop original semble ne pas supporter IE 11, mais cette extension le supporte, ce qui est une bonne amélioration). L'inconvénient est qu'il n'est pas gratuit. Mais je pense qu'elle en vaut la peine.

Mise à jour :

J'ai trouvé une autre bibliothèque Recadrage et téléchargement sur le même site web. Il peut combiner n'importe quelle interface utilisateur de recadrage, comme YUI, imgAreaSelect et Jcrop, pour recadrer et télécharger l'image sur le serveur avec un seul appel de fonction. Vous pouvez voir la démo aquí .

7voto

apsillers Points 29372

El Pixastic la bibliothèque fait exactement ce que vous voulez . Cependant, il ne fonctionnera que sur les navigateurs qui prennent en charge le canevas. Pour ces navigateurs plus anciens, vous devrez soit :

  1. fournir une solution de repli côté serveur, ou
  2. dites à l'utilisateur que vous êtes vraiment désolé, mais qu'il devra se procurer un navigateur plus moderne.

Bien sûr, l'option n° 2 n'est pas très conviviale. Cependant, si votre intention est de fournir un outil purement client et/ou si vous ne pouvez pas prendre en charge un croqueur de back-end de secours (par exemple, si vous écrivez une extension de navigateur ou une application Chrome hors ligne, ou si vous ne pouvez pas vous permettre un hébergeur décent qui fournit des bibliothèques de manipulation d'images), alors il est probablement juste de limiter votre base d'utilisateurs aux navigateurs modernes.

EDIT : Si vous ne voulez pas apprendre Pixastic, j'ai ajouté un cropper très simple sur jsFiddle. aquí . Il devrait être possible de modifier, d'intégrer et d'utiliser le système d'information de l drawCroppedImage avec Jcrop.

3voto

user1925970 Points 36

#change-avatar-file est une entrée de fichier #change-avatar-file est un tag img (la cible de jcrop) La "clé" est l'événement FR.onloadend https://developer.mozilla.org/en-US/docs/Web/API/FileReader

$('#change-avatar-file').change(function(){
        var currentImg;
        if ( this.files && this.files[0] ) {
            var FR= new FileReader();
            FR.onload = function(e) {
                $('#avatar-change-img').attr( "src", e.target.result );
                currentImg = e.target.result;
            };
            FR.readAsDataURL( this.files[0] );
            FR.onloadend = function(e){
                //console.log( $('#avatar-change-img').attr( "src"));
                var jcrop_api;

                $('#avatar-change-img').Jcrop({
                    bgFade:     true,
                    bgOpacity: .2,
                    setSelect: [ 60, 70, 540, 330 ]
                },function(){
                    jcrop_api = this;
                });
            }
        }
    });

2voto

8xx8 Points 76

J'ai écrit un plugin jQuery basé sur imgAreaSelect pour résoudre ce problème.

https://github.com/Andrew8xx8/awesome-cropper

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X