>웹 프론트엔드 >JS 튜토리얼 >변형 없이 JavaScript 이미지 자르기를 위한 샘플 코드 공유(그림)

변형 없이 JavaScript 이미지 자르기를 위한 샘플 코드 공유(그림)

黄舟
黄舟원래의
2017-03-13 16:51:141819검색

최근 웹사이트에서 이미지 자르기 효과를 많이 찾아봤는데 대부분의 방법은 아래 그림과 같습니다(스크립트 홈에서 그림을 빌려옴). 자르기 상자의 크기를 변경하여 적절한 위치를 선택합니다.

하지만 이 글에서는 또 다른 자르기 방법을 소개하고 있는데, 자르기 상자는 개발자가 결정하고, 이미지 크기는 사용자가 결정합니다. . 이미지를 확대/축소하고 드래그하여 적절한 위치를 선택하고, 위 오른쪽 그림과 같이 이 과정에서 항상 이미지의 가로 세로 비율을 유지하십시오.

이 방법은 주로 다음과 같은 장점이 있습니다.

  1. 자르기 프레임의 너비와 높이는 이미지 변형을 방지하는 데 사용되는 실제 가로 세로 비율과 일치해야 합니다. 문제

  2. 이미지의 표시 크기를 제한하지 않고 이미지의 원래 비율을 보장합니다.

  3. 부분 자르기의 경우 예를 들어 고화질 사진의 아주 작은 부분을 캡처하려면 사진을 확대하여 자르기 상자로 드래그하기만 하면 됩니다. 아주 작은 크기로 자르기 상자를 조정해야 하는데 이는 사용자 작업에 도움이 되지 않습니다.

그런데 단점도 말씀드리고 싶습니다. 한 단계씩 증가했습니다. . . .

주요 아이디어는 두 장의 사진을 사용하여 절대로 배치하고, 하나는 자르기 프레임 안에, 다른 하나는 자르기 프레임 밖에 놓고 투명도 효과인 자르기 프레임을 설정하는 것입니다 Overflow는 숨겨져 있어 두 그림이 항상 완전히 동기화된 상태로 유지됩니다.

<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>

먼저 postCreate에서 document의 mousemove 및 mousedown 이벤트 를 바인딩하고 마우스가 작업 공간을 떠난 후에도 계속할 수 있습니다. . 드래그하거나 확대/축소하세요. 다음 주요 작업은 시작과 _init함수입니다. 도장에 익숙하지 않은 친구들은 시작하기 전에 postCreate가 실행된다는 것만 알면 됩니다.

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;
                }
            },

여기에서는 다음 작업이 수행됩니다.

  1. 이미지가 로드될 때까지 기다리고 이미지의 원본 크기를 가져옵니다. 이 크기는 후속 계산에 사용됩니다. 배율 인수

  2. 자르기 영역의 가로 세로 비율을 보장하면서 자르기 영역이 작업 영역을 최대한 채우도록 합니다. 여기서 크롭 작업에서 가장 중요한 것은 이미지가 변형되는 것을 방지하는 것이므로 화면 비율이 일정하다면 크롭 영역을 적절하게 확대할 수 있습니다.

  3. 이미지의 원래 화면 비율을 유지하면서 이미지를 자르기 프레임에 최대한 가깝게 유지

  4. 초기 위치 설정 기계 내 계산이 완료된 후 이미지

를 기준으로 자르기 프레임을 중앙에 맞추는 과정은 비교적 간단합니다. 이동하는 동안 그림의 왼쪽 상단 모서리에 있는 왼쪽과 위쪽을 지속적으로 변경합니다. dragstart 및 selectstart 이벤트의 PreventDefault는 요소가 선택되어 파란색으로 바뀌는 것을 방지합니다.

_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;
                }
            },

크기 조정의 주요 원칙은 크기 조정 전후에 자르기 프레임의 중심점의 상대적 위치를 변경하지 않고 유지하는 것입니다.

크기가 조정된 원본 자르기 상자의 중심점을 원래 위치로 다시 이동하려면 두 가지 값: 이미지 크기 변경량, 그림 왼쪽 상단 모서리의 이동량.

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);

여기서 _zoomRatio = _maxImageWidth / _minImageWidth; _maxImageWidth는 이미지의 원래 크기이고 _minImageWidth는 이미지를 자르기 상자에 가깝게 만드는 최소 너비입니다.

leftPercentage는 슬라이딩 막대에 대한 슬라이딩 버튼의 변위 비율입니다.

_currentLeft 및 _currentTop은 이 크기 조정 이전의 자르기 프레임을 기준으로 한 이미지의 절대 위치입니다(위치: 절대).

_currentImageWidth 및 _currentImageHeight는 이 크기 조정 전의 이미지 크기입니다.

이때 사용자가 사진을 확대해서 테두리에 맞춰서 자르기 상자에 공백이 생기지 않도록 하는 일만 남았습니다. 사진이 축소되면 자르기 상자가 나타납니다. 공백이 나타납니다. 이러한 상황을 방지하기 위해서는 우리도 상응하는 조치를 취해야 합니다.

이미지의 왼쪽 상단 테두리가 자르기 상자의 왼쪽 상단 테두리와 일치하는 경우 아무리 축소해도 이미지의 왼쪽과 상단은 항상 0이 되며, 이미지의 크기만 이미지가 바뀌게 됩니다.

사진의 오른쪽 하단 테두리가 자르기 상자의 오른쪽 하단 테두리와 일치할 경우 사진의 크기와 자르기 상자의 크기를 기준으로 적절한 왼쪽 및 상단을 계산할 수 있습니다

//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;
                    });
                }

위는 클라이언트 아이디어를 구현한 것입니다. 모든 코드, 브라우저 지원: 최신 브라우저와 ie9+, ie8도 나중에 지원될 예정입니다.

服务器端使用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;
    });
  }
};


위 내용은 변형 없이 JavaScript 이미지 자르기를 위한 샘플 코드 공유(그림)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.