Maison >interface Web >js tutoriel >Exemple de partage de code pour le recadrage d'images JavaScript sans déformation (image)
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 :
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.
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
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('image width', 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('image is too smaller to display'); 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 + 'px', height: this.idealHeight + 'px' }); var paddingTop = Math.abs((parseFloat(cropSectionStyle.height) - this.idealHeight) / 2); html.setStyle(this.cropSection, { 'paddingTop': paddingTop + 'px', 'paddingBottom': paddingTop + 'px' }); // keep original ratio of image if (imageRadio >= 1) { if (this.idealHeight * imageRadio >= this.idealWidth) { html.setStyle(this.viewerImage, 'height', this.idealHeight + 'px'); html.setStyle(this.baseImage, 'height', this.idealHeight + 'px'); } else { var properlyHeight = this._findProperlyValue(0, this.idealWidth, this.idealWidth, function(p) { return p * imageRadio; }); html.setStyle(this.viewerImage, 'height', properlyHeight + 'px'); html.setStyle(this.baseImage, 'height', properlyHeight + 'px'); } } else { if (this.idealWidth / imageRadio >= this.idealHeight) { html.setStyle(this.viewerImage, 'width', this.idealWidth + 'px'); html.setStyle(this.baseImage, 'width', this.idealWidth + 'px'); } else { var properlyWidth = this._findProperlyValue(0, this.idealHeight, this.idealHeight, function(p) { return p / imageRadio; }); html.setStyle(this.viewerImage, 'width', properlyWidth + 'px'); html.setStyle(this.baseImage, 'width', properlyWidth + 'px'); } } query('.hide-image', this.domNode).removeClass('hide-image'); 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 + 'px', left: this._currentLeft + 'px' }); html.setStyle(this.viewerImage, { top: this._currentTop + 'px', left: this._currentLeft + 'px' }); //sometimes zoomratio < 1; it'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 :
_resetImagePosition: function(clientX, clientY) { var delX = clientX - this._currentX; var delY = clientY - this._currentY; if (this._currentTop + delY >= 0) { html.setStyle(this.baseImage, 'top', 0); html.setStyle(this.viewerImage, 'top', 0); this._currentY = clientY; this._currentTop = 0; } else if (this._currentTop + delY <= this._maxOffsetTop) { html.setStyle(this.baseImage, 'top', this._maxOffsetTop + 'px'); html.setStyle(this.viewerImage, 'top', this._maxOffsetTop + 'px'); this._currentY = clientY; this._currentTop = this._maxOffsetTop; } else { html.setStyle(this.baseImage, 'top', this._currentTop + delY + 'px'); html.setStyle(this.viewerImage, 'top', this._currentTop + delY + 'px'); this._currentY = clientY; this._currentTop += delY; } if (this._currentLeft + delX >= 0) { html.setStyle(this.baseImage, 'left', 0); html.setStyle(this.viewerImage, 'left', 0); this._currentX = clientX; this._currentLeft = 0; } else if (this._currentLeft + delX <= this._maxOffsetLeft) { html.setStyle(this.baseImage, 'left', this._maxOffsetLeft + 'px'); html.setStyle(this.viewerImage, 'left', this._maxOffsetLeft + 'px'); this._currentX = clientX; this._currentLeft = this._maxOffsetLeft; } else { html.setStyle(this.baseImage, 'left', this._currentLeft + delX + 'px'); html.setStyle(this.viewerImage, 'left', this._currentLeft + delX + 'px'); 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('zoomin'); html.setStyle(this.baseImage, { top: this._currentTop -delImageTop + 'px', left: this._currentLeft -delImageLeft + 'px' }); html.setStyle(this.viewerImage, { top: this._currentTop -delImageTop + 'px', left: this._currentLeft -delImageLeft + 'px' }); } else { console.log('zoomout'); 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 + 'px', left: left + 'px' }); html.setStyle(this.viewerImage, { top: top + 'px', left: left + 'px' }); }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: 'Bad base64 code format' }); } var fileFormat = base64Img.match(/^data:image\/(.*);base64,/)[1]; var base64Data = base64Img.replace(/^data:image\/.*;base64,/, ""); var maxSize = req.body.maxSize; maxSize = maxSize.split(','); var cropOptions = req.body.cropOptions; cropOptions = cropOptions.split(','); try{ var buf = new Buffer(base64Data, 'base64'); var jimp = new Jimp(buf, 'image/' + 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('image/' + fileFormat, function(b) { var base64String = "data:image/" + fileFormat + ";base64," + b.toString('base64'); res.send({ success: true, source: base64String }); }); }catch(err) { logger.error(err); res.send({ success: false, message: 'unable to complete operations' }); } };
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!