Maison >interface Web >js tutoriel >Exemple de partage de code pour le recadrage d'images JavaScript sans déformation (image)

Exemple de partage de code pour le recadrage d'images JavaScript sans déformation (image)

黄舟
黄舟original
2017-03-13 16:51:141821parcourir

J'ai récemment parcouru de nombreux effets de recadrage d'images de sites Web. La plupart des méthodes sont présentées dans l'image ci-dessous (en empruntant une image à Script Home). Sélectionnez la position appropriée en modifiant la taille de la zone de recadrage.

Mais cet article présente une autre méthode de recadrage. La zone de recadrage est déterminée par le développeur et la taille de l'image est déterminée par l'utilisateur. . Sélectionnez la position appropriée en zoomant et en faisant glisser l'image, et conservez toujours le rapport hauteur/largeur de l'image dans le processus, comme indiqué dans l'image ci-dessus à droite.

Cette méthode présente principalement les avantages suivants :

  1. La largeur et la hauteur du cadre de recadrage doivent être cohérentes avec le rapport hauteur/largeur réel utilisé pour éviter la déformation de l'image problèmes.

  2. Ne limite pas la taille d'affichage de l'image et garantit la proportion originale de l'image. La taille originale peut être obtenue par mise à l'échelle

  3. <.>
  4. Pour un recadrage partiel, c'est plus convivial. Par exemple, pour capturer une très petite partie d'une image haute définition, il suffit d'agrandir l'image et de la faire glisser dans la zone de recadrage. la boîte de recadrage doit être ajustée à une très petite taille, ce qui n'est pas propice au fonctionnement de l'utilisateur

Cela dit, je devrais aussi parler des défauts. L'inconvénient est que la difficulté est. augmenté d’un ordre de grandeur. . . .

L'idée principale est d'utiliser deux images, de les positionner

absolument, de placer l'une à l'intérieur du cadre de recadrage et l'autre à l'extérieur du cadre de recadrage et de définir l'effet de transparence, le cadre de recadrage overflow est masqué, gardant les deux images absolument synchronisées à tout moment.

<p class="jimu-crop-image" data-dojo-attach-point="cropSection">
    <p class="viewer-box" data-dojo-attach-point="viewerBox">
        <p class="viewer-content" data-dojo-attach-point="viewerContent">
            <img class="viewer-image hide-image" data-dojo-attach-point="viewerImage" src="">
        </p>
        <img class="base-image hide-image" data-dojo-attach-point="baseImage" data-dojo-attach-event="mousedown:_onViewerMouseDown,mouseup:_onViewerMouseUp">

        <p class="controller">
            <p class="zoom-out" data-dojo-attach-event="click:_onZoomOutClick">-</p>
            <p class="slider" data-dojo-attach-point="sliderNode">
                <p class="button" data-dojo-attach-point="sliderButton" data-dojo-attach-event="mousedown:_onSliderMouseDown,mouseup:_onSliderMouseUp"></p>
                <p class="horizontal"></p>
            </p>
            <p class="zoom-in" data-dojo-attach-event="click:_onZoomInClick">+</p>
        </p>
    </p>
</p>

Liez d'abord les événements mousemove et mousedown

du document dans postCreate. sera toujours Vous pouvez continuer à faire glisser ou à zoomer. Le prochain travail principal concerne les fonctions startup et _init. Les amis qui ne sont pas familiers avec le dojo doivent seulement savoir que postCreate sera exécuté avant le démarrage.

startup: function() {
                var timeOut = /data:image\/(.*);base64/.test(this.imageSrc) ? 50 : 500;
                var tic = lang.hitch(this, function() {
                    var imageStyle = html.getComputedStyle(this.baseImage);
                    var imageWidth = parseFloat(imageStyle.width);
                    console.log(&#39;image width&#39;, imageWidth);
                    if (isFinite(imageWidth) && imageWidth > 0) {
                        this._init();
                    } else {
                        setTimeout(tic, timeOut);
                    }
                });

                setTimeout(tic, timeOut);
            },

_init: function() {
                debugger;
                var cropSectionStyle = html.getComputedStyle(this.cropSection);
                var cropSectionContentBox = html.getContentBox(this.cropSection);
                var imageStyle = html.getComputedStyle(this.baseImage);
                var imageWidth = parseFloat(imageStyle.width);
                var imageHeight = parseFloat(imageStyle.height);
                var imageRadio = imageWidth / imageHeight;

                this._maxImageWidth = imageWidth;
                this._maxImageHeight = imageHeight;

                if (imageHeight < this.realHeight && imageWidth < this.realWidth) {
                    alert(&#39;image is too smaller to display&#39;);
                    return;
                }

                //create a box which keep the ratio of width and height to full fill the content of popup
                this.idealWidth = this.realWidth;
                this.idealHeight = this.realHeight;

                this.ratio = this.ratio ? this.ratio : this.realWidth / this.realHeight;
                if (this.ratio >= 1) {
                    if (this.realWidth <= cropSectionContentBox.w) {
                        this.idealWidth += (cropSectionContentBox.w - this.realWidth) / 2;
                    } else {
                        this.idealWidth = cropSectionContentBox.w;
                    }
                    this.idealHeight = this.idealWidth / this.ratio;
                } else {
                    if (this.realHeight <= cropSectionContentBox.h) {
                        this.idealHeight += (cropSectionContentBox.h - this.idealHeight) / 2;
                    } else {
                        this.idealHeight = cropSectionContentBox.h;
                    }
                    this.idealWidth = this.idealHeight * this.ratio;
                }

                html.setStyle(this.viewerBox, {
                    width: this.idealWidth + &#39;px&#39;,
                    height: this.idealHeight + &#39;px&#39;
                });

                var paddingTop = Math.abs((parseFloat(cropSectionStyle.height) - this.idealHeight) / 2);
                html.setStyle(this.cropSection, {
                    &#39;paddingTop&#39;: paddingTop + &#39;px&#39;,
                    &#39;paddingBottom&#39;: paddingTop + &#39;px&#39;
                });

                // keep original ratio of image
                if (imageRadio >= 1) {
                    if (this.idealHeight * imageRadio >= this.idealWidth) {
                        html.setStyle(this.viewerImage, &#39;height&#39;, this.idealHeight + &#39;px&#39;);
                        html.setStyle(this.baseImage, &#39;height&#39;, this.idealHeight + &#39;px&#39;);
                    } else {
                        var properlyHeight = this._findProperlyValue(0, this.idealWidth, this.idealWidth, function(p) {
                            return p * imageRadio;
                        });
                        html.setStyle(this.viewerImage, &#39;height&#39;, properlyHeight + &#39;px&#39;);
                        html.setStyle(this.baseImage, &#39;height&#39;, properlyHeight + &#39;px&#39;);
                    }
                } else {
                    if (this.idealWidth / imageRadio >= this.idealHeight) {
                        html.setStyle(this.viewerImage, &#39;width&#39;, this.idealWidth + &#39;px&#39;);
                        html.setStyle(this.baseImage, &#39;width&#39;, this.idealWidth + &#39;px&#39;);
                    } else {
                        var properlyWidth = this._findProperlyValue(0, this.idealHeight, this.idealHeight, function(p) {
                            return p / imageRadio;
                        });
                        html.setStyle(this.viewerImage, &#39;width&#39;, properlyWidth + &#39;px&#39;);
                        html.setStyle(this.baseImage, &#39;width&#39;, properlyWidth + &#39;px&#39;);
                    }
                }

                query(&#39;.hide-image&#39;, this.domNode).removeClass(&#39;hide-image&#39;);

                imageStyle = html.getComputedStyle(this.baseImage);
                imageWidth = parseFloat(imageStyle.width);
                imageHeight = parseFloat(imageStyle.height);
                this._minImageWidth = imageWidth;
                this._minImageHeight = imageHeight;

                this._currentImageWidth = imageWidth;
                this._currentImageHeight = imageHeight;

                this._currentTop = -(imageHeight - this.idealHeight) / 2;
                this._currentLeft = -(imageWidth - this.idealWidth) / 2;
                html.setStyle(this.baseImage, {
                    top: this._currentTop + &#39;px&#39;,
                    left: this._currentLeft + &#39;px&#39;
                });
                html.setStyle(this.viewerImage, {
                    top: this._currentTop + &#39;px&#39;,
                    left: this._currentLeft + &#39;px&#39;
                });
                //sometimes zoomratio < 1; it&#39;s should be not allowed to zoom
                this._zoomRatio = this._maxImageWidth / this._minImageWidth;

                if (!this._latestPercentage) {
                    this._latestPercentage = 0;
                }
            },
Les choses suivantes sont faites ici :

  1. Attendez que l'image soit chargée et obtenez la taille originale de l'image, qui sera utilisée lors du calcul le facteur d'échelle plus tard

  2. Tout en garantissant le rapport hauteur/largeur de la zone de recadrage, laissez la zone de recadrage remplir autant que possible la zone de travail. La chose la plus importante dans le travail de recadrage ici est d'éviter que l'image ne soit déformée. Ainsi, tant que le rapport hauteur/largeur est cohérent, la zone recadrée peut être agrandie de manière appropriée.

  3. Tout en conservant le rapport hauteur/largeur d'origine de l'image, gardez l'image aussi proche que possible du cadre de recadrage

  4. Définissez la position initiale de l'image une fois le calcul sur machine terminé. Le processus de centrage du cadre de recadrage par rapport à l'image

est relativement simple. Il vous suffit d'enregistrer les changements de position relative de la souris. pendant le mouvement, et changez continuellement la gauche et le haut dans le coin supérieur gauche de l'image. PreventDefault dans les événements dragstart et selectstart empêche les éléments d'être sélectionnés et de devenir bleus.

_resetImagePosition: function(clientX, clientY) {
                var delX = clientX - this._currentX;
                var delY = clientY - this._currentY;

                if (this._currentTop + delY >= 0) {
                    html.setStyle(this.baseImage, &#39;top&#39;, 0);
                    html.setStyle(this.viewerImage, &#39;top&#39;, 0);
                    this._currentY = clientY;
                    this._currentTop = 0;
                } else if (this._currentTop + delY <= this._maxOffsetTop) {
                    html.setStyle(this.baseImage, &#39;top&#39;, this._maxOffsetTop + &#39;px&#39;);
                    html.setStyle(this.viewerImage, &#39;top&#39;, this._maxOffsetTop + &#39;px&#39;);
                    this._currentY = clientY;
                    this._currentTop = this._maxOffsetTop;
                } else {
                    html.setStyle(this.baseImage, &#39;top&#39;, this._currentTop + delY + &#39;px&#39;);
                    html.setStyle(this.viewerImage, &#39;top&#39;, this._currentTop + delY + &#39;px&#39;);
                    this._currentY = clientY;
                    this._currentTop += delY;
                }

                if (this._currentLeft + delX >= 0) {
                    html.setStyle(this.baseImage, &#39;left&#39;, 0);
                    html.setStyle(this.viewerImage, &#39;left&#39;, 0);
                    this._currentX = clientX;
                    this._currentLeft = 0;
                } else if (this._currentLeft + delX <= this._maxOffsetLeft) {
                    html.setStyle(this.baseImage, &#39;left&#39;, this._maxOffsetLeft + &#39;px&#39;);
                    html.setStyle(this.viewerImage, &#39;left&#39;, this._maxOffsetLeft + &#39;px&#39;);
                    this._currentX = clientX;
                    this._currentLeft = this._maxOffsetLeft;
                } else {
                    html.setStyle(this.baseImage, &#39;left&#39;, this._currentLeft + delX + &#39;px&#39;);
                    html.setStyle(this.viewerImage, &#39;left&#39;, this._currentLeft + delX + &#39;px&#39;);
                    this._currentX = clientX;
                    this._currentLeft += delX;
                }
            },
Le principe principal de la mise à l'échelle est de

garder la position relative du point central du cadre de recadrage inchangée avant et après la mise à l'échelle.

Afin de déplacer le point central de la zone de recadrage d'origine mise à l'échelle vers sa position d'origine, nous devons calculer la médiane de deux valeurs : taille de l'image Montant du changement, la quantité de mouvement du coin supérieur gauche de l'image.

var delImageWidth = this._minImageWidth * (this._zoomRatio - 1) * leftPercentage / 100;
var delImageHeight = this._minImageHeight * (this._zoomRatio - 1) * leftPercentage / 100;

var imageStyle = html.getComputedStyle(this.baseImage);
                this._currentLeft = parseFloat(imageStyle.left);
                this._currentTop = parseFloat(imageStyle.top);
var delImageLeft = (Math.abs(this._currentLeft) + this.idealWidth / 2) *
                    ((this._minImageWidth + delImageWidth) / this._currentImageWidth - 1);
var delImageTop = (Math.abs(this._currentTop) + this.idealHeight / 2) *
                    ((this._minImageWidth + delImageWidth) / this._currentImageWidth - 1);
Où _zoomRatio = _maxImageWidth / _minImageWidth; _maxImageWidth est la taille d'origine de l'image et _minImageWidth est la largeur minimale qui rapproche l'image de la zone de recadrage.

leftPercentage est le pourcentage de déplacement du bouton coulissant par rapport à la barre coulissante.

_currentLeft et _currentTop sont la position absolue de l'image par rapport au cadre de recadrage avant cette mise à l'échelle (position : absolue).

_currentImageWidth et _currentImageHeight sont la taille de l'image avant cette mise à l'échelle.

La seule chose qui reste à faire est d'éviter l'espace vide dans la zone de recadrage. Supposons que l'utilisateur agrandisse l'image et la fasse glisser vers la bordure pour qu'elle coïncide avec la bordure de la zone de recadrage. si l'image est réduite, la zone de recadrage sera Un espace vide apparaît. Afin d’éviter cette situation, nous devons également prendre des mesures correspondantes.

Lorsque la bordure supérieure gauche de l'image coïncide avec la bordure supérieure gauche de la zone de recadrage, peu importe la façon dont vous effectuez un zoom arrière, la gauche et le haut de l'image seront toujours nuls, et seule la taille du l'image va changer.

Lorsque la bordure inférieure droite de l'image coïncide avec la bordure inférieure droite de la zone de recadrage, la gauche et le haut appropriés peuvent être calculés en fonction de la taille de l'image et de la taille de la zone de recadrage

//prevent image out the crop box
                if (leftPercentage - _latestPercentage >= 0) {
                    console.log(&#39;zoomin&#39;);
                    html.setStyle(this.baseImage, {
                        top: this._currentTop -delImageTop + &#39;px&#39;,
                        left: this._currentLeft -delImageLeft + &#39;px&#39;
                    });
                    html.setStyle(this.viewerImage, {
                        top: this._currentTop -delImageTop + &#39;px&#39;,
                        left: this._currentLeft -delImageLeft + &#39;px&#39;
                    });
                } else {
                    console.log(&#39;zoomout&#39;);
                    var top = 0;
                    var left = 0;
                    if (this._currentTop - delImageTop >= 0) {
                        top = 0;
                    } else if (this._currentTop - delImageTop +
                        this._minImageHeight + delImageHeight <=
                        this.idealHeight) {
                        top = this.idealHeight - this._minImageHeight - delImageHeight;
                    } else {
                        top = this._currentTop - delImageTop;
                    }
                    console.log(this._currentLeft, delImageLeft);
                    if (this._currentLeft - delImageLeft >= 0) {
                        left = 0;
                    } else if (this._currentLeft - delImageLeft +
                        this._minImageWidth + delImageWidth <=
                        this.idealWidth) {
                        left =this.idealWidth - this._minImageWidth - delImageWidth;
                    } else {
                        left = this._currentLeft - delImageLeft;
                    }

                    html.setStyle(this.baseImage, {
                        top: top + &#39;px&#39;,
                        left: left + &#39;px&#39;
                    });
                    html.setStyle(this.viewerImage, {
                        top: top + &#39;px&#39;,
                        left: left + &#39;px&#39;
                    });
                }
Ce qui précède représente les idées de mise en œuvre finale du client. Tous les codes, prise en charge des navigateurs : les navigateurs modernes et ie9, ie8 seront également pris en charge ultérieurement.

服务器端使用nodejs+express框架,主要代码如下:

/**********
body: {
  imageString: base64 code
  maxSize: w,h
  cropOptions: w,h,t,l
}
************/
exports.cropImage = function(req, res) {
  var base64Img = req.body.imageString;
  if(!/^data:image\/.*;base64,/.test(base64Img)){
    res.send({
      success: false,
      message: &#39;Bad base64 code format&#39;
    });
  }
  var fileFormat = base64Img.match(/^data:image\/(.*);base64,/)[1];
  var base64Data = base64Img.replace(/^data:image\/.*;base64,/, "");
  var maxSize = req.body.maxSize;
  maxSize = maxSize.split(&#39;,&#39;);
  var cropOptions = req.body.cropOptions;
  cropOptions = cropOptions.split(&#39;,&#39;);

  try{
    var buf = new Buffer(base64Data, &#39;base64&#39;);
    var jimp = new Jimp(buf, &#39;image/&#39; + fileFormat, function() {
      var maxW = parseInt(maxSize[0], 10);
      var maxH = parseInt(maxSize[1], 10);
      var cropW = parseInt(cropOptions[0], 10);
      var cropH = parseInt(cropOptions[1], 10);
      var cropT = parseInt(cropOptions[2], 10);
      var cropL = parseInt(cropOptions[3], 10);
      this.resize(maxW, maxH)
      .crop(cropT, cropL, cropW, cropH);
    });

    jimp.getBuffer(&#39;image/&#39; + fileFormat, function(b) {
      var base64String = "data:image/" + fileFormat + ";base64," + b.toString(&#39;base64&#39;);
      res.send({
        success: true,
        source: base64String
      });
    });
  }catch(err) {
    logger.error(err);
    res.send({
      success: false,
      message: &#39;unable to complete operations&#39;
    });
  }
};


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn