-->

Resizing the images while keeping proportion using

2019-06-03 07:48发布

问题:

I'm using this in my app:

http://www.html5canvastutorials.com/labs/html5-canvas-drag-and-drop-resize-and-invert-images/

have use anchors for resizing the image ..

What I want is to resize the image and keep its ratio maintained. I don't want it to stretch I'm able to achieve that using this peace of code.:..

  if(width) {
        image.setSize(width);
      }

But this is screwing up the anchors .. Anyone ever tried something like this..

回答1:

I know this is a horribly old post but I needed to implement this exact thing and found the accepted answer is quite flawed - the left two drag handles move the entire image and act very much unlike an image editing program would, for instance. I am posting my solution in case someone stumbles upon this from a Google search like I did.

// Update the positions of handles during drag.
// This needs to happen so the dimension calculation can use the
// handle positions to determine the new width/height.
switch (activeHandleName) {
    case "topLeft":
        topRight.setY(activeHandle.getY());
        bottomLeft.setX(activeHandle.getX());
        break;
    case "topRight":
        topLeft.setY(activeHandle.getY());
        bottomRight.setX(activeHandle.getX());
        break;
    case "bottomRight":
        bottomLeft.setY(activeHandle.getY());
        topRight.setX(activeHandle.getX());
        break;
    case "bottomLeft":
        bottomRight.setY(activeHandle.getY());
        topLeft.setX(activeHandle.getX());
        break;
}

// Calculate new dimensions. Height is simply the dy of the handles.
// Width is increased/decreased by a factor of how much the height changed.
newHeight = bottomLeft.getY() - topLeft.getY();
newWidth = image.getWidth() * newHeight / image.getHeight();

// Move the image to adjust for the new dimensions.
// The position calculation changes depending on where it is anchored.
// ie. When dragging on the right, it is anchored to the top left,
//     when dragging on the left, it is anchored to the top right.
if(activeHandleName === "topRight" || activeHandleName === "bottomRight") {
    image.setPosition(topLeft.getX(), topLeft.getY());
} else if(activeHandleName === "topLeft" || activeHandleName === "bottomLeft") {
    image.setPosition(topRight.getX() - newWidth, topRight.getY());
}

imageX = image.getX();
imageY = image.getY();

// Update handle positions to reflect new image dimensions
topLeft.setPosition(imageX, imageY);
topRight.setPosition(imageX + newWidth, imageY);
bottomRight.setPosition(imageX + newWidth, imageY + newHeight);
bottomLeft.setPosition(imageX, imageY + newHeight);

// Set the image's size to the newly calculated dimensions
if(newWidth && newHeight) {
     image.setSize(newWidth, newHeight);
}

Modified KineticJS example in JSBin: http://jsbin.com/iyimuy/1/edit



回答2:

Change lines:

var width = topRight.attrs.x - topLeft.attrs.x;
var height = bottomLeft.attrs.y - topLeft.attrs.y;
if(width && height) {
    image.setSize(width, height);
}

To:

var height = bottomLeft.attrs.y - topLeft.attrs.y;
var width = image.getWidth()*height/image.getHeight();

topRight.attrs.x = topLeft.attrs.x + width;
topRight.attrs.y = topLeft.attrs.y;
bottomRight.attrs.x = topLeft.attrs.x + width;
bottomRight.attrs.y = topLeft.attrs.y + height;

if(width && height) {
    image.setSize(width, height);
}

Example



回答3:

Here it is: JSFIDDLE

JS:

function update(group, activeHandle) {
        var topLeft = group.get(".topLeft")[0],
            topRight = group.get(".topRight")[0],
            bottomRight = group.get(".bottomRight")[0],
            bottomLeft = group.get(".bottomLeft")[0],
            image = group.get(".image")[0],
            activeHandleName = activeHandle.getName(),
            newWidth,
            newHeight,
            minWidth = 32,
            minHeight = 32,
            oldX,
            oldY,
            imageX,
            imageY;

        // Update the positions of handles during drag.
        // This needs to happen so the dimension calculation can use the
        // handle positions to determine the new width/height.
        switch (activeHandleName) {
            case "topLeft":
                oldY = topRight.getY();
                oldX = bottomLeft.getX();
                topRight.setY(activeHandle.getY());
                bottomLeft.setX(activeHandle.getX());
                break;
            case "topRight":
                oldY = topLeft.getY();
                oldX = bottomRight.getX();
                topLeft.setY(activeHandle.getY());
                bottomRight.setX(activeHandle.getX());
                break;
            case "bottomRight":
                oldY = bottomLeft.getY();
                oldX = topRight.getX();
                bottomLeft.setY(activeHandle.getY());
                topRight.setX(activeHandle.getX());
                break;
            case "bottomLeft":
                oldY = bottomRight.getY();
                oldX = topLeft.getX();
                bottomRight.setY(activeHandle.getY());
                topLeft.setX(activeHandle.getX());
                break;
        }



        // Calculate new dimensions. Height is simply the dy of the handles.
        // Width is increased/decreased by a factor of how much the height changed.
        newHeight = bottomLeft.getY() - topLeft.getY();
        newWidth = image.getWidth() * newHeight / image.getHeight();

        // It's too small: move the active handle back to the old position
        if( newWidth < minWidth || newHeight < minHeight ){
          activeHandle.setY(oldY);
          activeHandle.setX(oldX);
          switch (activeHandleName) {
            case "topLeft":
                topRight.setY(oldY);
                bottomLeft.setX(oldX);
                break;
            case "topRight":
                topLeft.setY(oldY);
                bottomRight.setX(oldX);
                break;
            case "bottomRight":
                bottomLeft.setY(oldY);
                topRight.setX(oldX);
                break;
            case "bottomLeft":
                bottomRight.setY(oldY);
                topLeft.setX(oldX);
                break;
          }
        }


        newHeight = bottomLeft.getY() - topLeft.getY();
        //comment the below line and uncomment the line below tha line to allow free resize of the images because the below line preserves the scale and aspect ratio
        newWidth = image.getWidth() * newHeight / image.getHeight();//for restricted resizing
        //newWidth = topRight.getX() - topLeft.getX();//for free resizing

        // Move the image to adjust for the new dimensions.
        // The position calculation changes depending on where it is anchored.
        // ie. When dragging on the right, it is anchored to the top left,
        //     when dragging on the left, it is anchored to the top right.
        if(activeHandleName === "topRight" || activeHandleName === "bottomRight") {
            image.setPosition(topLeft.getX(), topLeft.getY());
        } else if(activeHandleName === "topLeft" || activeHandleName === "bottomLeft") {
            image.setPosition(topRight.getX() - newWidth, topRight.getY());
        }

        imageX = image.getX();
        imageY = image.getY();

        // Update handle positions to reflect new image dimensions
        topLeft.setPosition(imageX, imageY);
        topRight.setPosition(imageX + newWidth, imageY);
        bottomRight.setPosition(imageX + newWidth, imageY + newHeight);
        bottomLeft.setPosition(imageX, imageY + newHeight);

        // Set the image's size to the newly calculated dimensions
        if(newWidth && newHeight) {
            image.setSize(newWidth, newHeight);
        }
      }
      function addAnchor(group, x, y, name) {
        var stage = group.getStage();
        var layer = group.getLayer();

        var anchor = new Kinetic.Circle({
          x: x,
          y: y,
          stroke: "#666",
          fill: "#ddd",
          strokeWidth: 2,
          radius: 8,
          name: name,
          draggable: true
        });

        anchor.on("dragmove", function() {
          update(group, this);
          layer.draw();
        });
        anchor.on("mousedown touchstart", function() {
          group.setDraggable(false);
          this.moveToTop();
        });
        anchor.on("dragend", function() {
          group.setDraggable(true);
          layer.draw();
        });
        // add hover styling
        anchor.on("mouseover", function() {
          var layer = this.getLayer();
          document.body.style.cursor = "pointer";
          this.setStrokeWidth(4);
          layer.draw();
        });
        anchor.on("mouseout", function() {
          var layer = this.getLayer();
          document.body.style.cursor = "default";
          this.setStrokeWidth(2);
          layer.draw();
        });

        group.add(anchor);
      }
      function loadImages(sources, callback) {
        var images = {};
        var loadedImages = 0;
        var numImages = 0;
        for(var src in sources) {
          numImages++;
        }
        for(var src in sources) {
          images[src] = new Image();
          images[src].onload = function() {
            if(++loadedImages >= numImages) {
              callback(images);
            }
          };
          images[src].src = sources[src];
        }
      }
      function initStage(images) {
        var stage = new Kinetic.Stage({
          container: "container",
          width: 578,
          height: 400
        });
        var darthVaderGroup = new Kinetic.Group({
          x: 270,
          y: 100,
          draggable: true
        });
        var yodaGroup = new Kinetic.Group({
          x: 100,
          y: 110,
          draggable: true
        });
        var layer = new Kinetic.Layer();

        /*
         * go ahead and add the groups
         * to the layer and the layer to the
         * stage so that the groups have knowledge
         * of its layer and stage
         */
        layer.add(darthVaderGroup);
        layer.add(yodaGroup);
        stage.add(layer);

        // darth vader
        var darthVaderImg = new Kinetic.Image({
          x: 0,
          y: 0,
          image: images.darthVader,
          width: 200,
          height: 138,
          name: "image"
        });

        darthVaderGroup.add(darthVaderImg);
        addAnchor(darthVaderGroup, 0, 0, "topLeft");
        addAnchor(darthVaderGroup, 200, 0, "topRight");
        addAnchor(darthVaderGroup, 200, 138, "bottomRight");
        addAnchor(darthVaderGroup, 0, 138, "bottomLeft");

        darthVaderGroup.on("dragstart", function() {
          this.moveToTop();
        });
        // yoda
        var yodaImg = new Kinetic.Image({
          x: 0,
          y: 0,
          image: images.yoda,
          width: 93,
          height: 104,
          name: "image"
        });

        yodaGroup.add(yodaImg);
        addAnchor(yodaGroup, 0, 0, "topLeft");
        addAnchor(yodaGroup, 93, 0, "topRight");
        addAnchor(yodaGroup, 93, 104, "bottomRight");
        addAnchor(yodaGroup, 0, 104, "bottomLeft");

        yodaGroup.on("dragstart", function() {
          this.moveToTop();
        });

        stage.draw();
      }

      var sources = {
        darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
        yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
      };
      loadImages(sources, initStage);


回答4:

I know is not the question but Ive lost a lot of time trying to solve the problem when the anchors go beyond the image 0 size, which throws a javasacript error:

IndexSizeError: Index or size is negative or greater than the allowed amount

What I did is adding Math.abs to the image size calcuation formula :

newHeight = Math.abs(bottomLeft.getY() - topLeft.getY()); 
newWidth = Math.abs(image.getWidth() * newHeight / image.getHeight());

It works fine with both bottom anchors, but not so well with top anchors, I just removed the top anchors which is good enough solution for my project....

Hope this helps someone!