如何使用画布调整大小并裁剪图像

47

我已经知道如何

-> 调整图片大小:

var image = document.getElementById('myImage'),
    canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d');
ctx.drawImage(image,0,0,400,300);

-> 或裁剪图像:

var image = document.getElementById('myImage'),
    canvas = document.createElement('canvas'),
    ctx = canvas.getContext('2d');
ctx.drawImage(image,50,50,image.width,image.height,0,0,50,50);

但是我不知道如何调整大小并裁剪图片。我该怎么做?谢谢。

4个回答

139

文档中,这些是drawImage的参数:

drawImage(image,
   sx, sy, sw, sh,
   dx, dy, dw, dh);

enter image description here

因此,要从源图像中裁剪掉外部的10个像素(假设它是100 * 50),然后将其缩放到160*60

ctx.drawImage(image,
    10, 10,   // Start at 10 pixels from the left and the top of the image (crop),
    80, 30,   // "Get" a `80 * 30` (w * h) area from the source image (crop),
    0, 0,     // Place the result at 0, 0 in the canvas,
    160, 60); // With as width / height: 160 * 60 (scale)

示例:

const image = new Image(),
      canvas = document.getElementById('canvas'),
      ctx = canvas.getContext('2d');

image.src = 'https://istack.dev59.com/I4jXc.webp';

image.addEventListener('load', () => {
    ctx.drawImage(image,
        70, 20,   // Start at 70/20 pixels from the left and the top of the image (crop),
        50, 50,   // "Get" a `50 * 50` (w * h) area from the source image (crop),
        0, 0,     // Place the result at 0, 0 in the canvas,
        100, 100); // With as width / height: 100 * 100 (scale)
});
Image: <br/>
<img src="https://istack.dev59.com/I4jXc.webp" /><br/>
Canvas: <br/>
<canvas id="canvas" width="275px" height="95px"></canvas>


非常感谢您的回答。顺便问一下,当O画布被裁剪后,如何将其调整为20x20尺寸? - Lewis
@Orion:实际的画布(就是HTML元素)?还是图像本身? - Cerbrus
我是指图像本身。我如何调整它的大小? - Lewis
1
你可以在 drawImage 调用的最后两个参数中设置大小(例如,在我最后一个示例中,100, 100 是所需的宽度/高度)。 - Cerbrus
1
你解释得就像文档一样清晰明了。很棒的例子! - Definity
显示剩余2条评论

6
在绘制到画布上之前,使用canvas.widthcanvas.height来指定最终的画布尺寸,否则最终的尺寸将始终为300x150。

请注意,不应使用CSS调整画布的大小,因为这将会拉伸画布。正如Jim所建议的那样,使用HTML属性可以正确地设置画布的实际大小。 - Cerbrus
你能举个例子(代码)来说明如何“指定最终画布大小”吗? - Azamat Uzbekov

2
我以http://tympanus.net/codrops/2014/10/30/resizing-cropping-images-canvas/这个教程为例,在纯JS中完成了相同的操作。它可能需要一些重构,但它可以工作(至少在我的Windows笔记本电脑上使用Chrome浏览器)。
一个HTML文件:
<!DOCTYPE html>
<html lang="en">
 <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    /* styles for resize container and image */
    .resize-container {
        position: relative;
        display: inline-block;
        cursor: move;
        margin: 0 auto;
        top: 0;
        left: 0;
    }

    .resize-container img {
        display: block
    }

    .resize-container:hover img,
    .resize-container:active img {
        outline: 2px dashed rgba(222,60,80,.9);
    }
    /* styles for resize handles */
    .resize-handle-ne,
    .resize-handle-se,
    .resize-handle-nw,
    .resize-handle-sw {
        position: absolute;
        display: block;
        width: 10px;
        height: 10px;
        background: rgba(222, 60, 80, .9);
        z-index: 999;
    }

    .resize-handle-nw {
        top: -5px;
        left: -5px;
        cursor: nw-resize;
    }

    .resize-handle-sw {
        bottom: -5px;
        left: -5px;
        cursor: sw-resize;
    }

    .resize-handle-ne {
        top: -5px;
        right: -5px;
        cursor: ne-resize;
    }

    .resize-handle-se {
        bottom: -5px;
        right: -5px;
        cursor: se-resize;
    }
    .overlay {
        position: absolute;
        left: 50%;
        top: 50%;
        margin-left: -100px;
        margin-top: -100px;
        z-index: 999;
        width: 200px;
        height: 200px;
        border: solid 2px rgba(222,60,80,.9);
        box-sizing: content-box;
        pointer-events: none;
    }

    .overlay:after,
    .overlay:before {
        content: '';
        position: absolute;
        display: block;
        width: 204px;
        height: 40px;
        border-left: dashed 2px rgba(222,60,80,.9);
        border-right: dashed 2px rgba(222,60,80,.9);
    }

    .overlay:before {
        top: 0;
        margin-left: -2px;
        margin-top: -40px;
    }

    .overlay:after {
        bottom: 0;
        margin-left: -2px;
        margin-bottom: -40px;
    }

    .overlay-inner:after,
    .overlay-inner:before {
        content: '';
        position: absolute;
        display: block;
        width: 40px;
        height: 204px;
        border-top: dashed 2px rgba(222,60,80,.9);
        border-bottom: dashed 2px rgba(222,60,80,.9);
    }

    .overlay-inner:before {
        left: 0;
        margin-left: -40px;
        margin-top: -2px;
    }

    .overlay-inner:after {
        right: 0;
        margin-right: -40px;
        margin-top: -2px;
    }

    .btn-crop {
        position: absolute;
        vertical-align: bottom;
        right: 5px;
        bottom: 5px;
        padding: 6px 10px;
        z-index: 999;
        background-color: rgb(222,60,80);
        border: none;
        border-radius: 5px;
        color: #FFF;
    }

    #result {
        position: absolute;
        top: 0;
        left: 0;
        width: 100vw;
        height: 150vh;
        z-index: -1;
        display: flex;
        align-items: flex-end;
    }
  </style>
 </head>
  <body>
 <div id="resize-container" class="resize-container">
    <span class="resize-handle resize-handle-nw"></span>
    <span class="resize-handle resize-handle-ne"></span>
    <span class="resize-handle resize-handle-sw"></span>
    <span class="resize-handle resize-handle-se"></span>
    <img id="img" class="resize-image" src="image.png" alt="Image" />
</div>
<div id="overlay" class="overlay">
    <div class="overlay-inner">
    </div>
</div>
<button id="js-crop" class="btn-crop">Crop</button>
<div id="result"></div>

<script>
    const resizeableImage = image_target => {
        // defining the variables 
        let constrain = false

        const overlay = document.getElementById('overlay')
        const cropBtn = document.getElementById('js-crop')
        const container = document.getElementById('resize-container')
        const orig_src = new Image()
        const event_state = {}
        const min_width = 60
        const min_height = 60
        const max_width = 800
        const max_height = 900
        
        const resize_canvas = document.createElement('canvas')

        const resizeImage = (width, height) => {
            resize_canvas.width = width 
            resize_canvas.height = height 
            resize_canvas.getContext('2d').drawImage(orig_src, 0, 0, width, height)
            image_target.src = resize_canvas.toDataURL("image/png")
        }

        // the resizing function is where most of the action happens. This function is contansly invoked 
        // while the user is dragging one of the resize handles. Every time this function is called we
        // work out the new with and height by taking the current position of the mouse relative to the
        // initial position of the corner we are dragging
        const resizing = e => {
            let width, height, left, top 

            const rec = container.getBoundingClientRect()
            const offset = {
                top: rec.top + window.scrollY,
                left: rec.left + window.scrollX
            }
            const mouse = {
                x: (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + window.screenLeft,
                y: (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + window.screenTop
            }

            const eTargetClass = event_state.evnt.target.classList[1]
            // if statements for being able to resize from each corner
            if (eTargetClass === 'resize-handle-se') { // south-east (bottom-right)
                width = mouse.x - event_state.container_left
                height = mouse.y  - event_state.container_top
                left = event_state.container_left
                top = event_state.container_top
            } else if (eTargetClass === 'resize-handle-sw') { // south-west (bottom-left)
                width = event_state.container_width - (mouse.x - event_state.container_left)
                height = mouse.y  - event_state.container_top
                left = mouse.x
                top = event_state.container_top
            } else if (eTargetClass === 'resize-handle-ne') { // north-east (top-right)
                width = mouse.x - event_state.container_left
                height = event_state.container_height - (mouse.y - event_state.container_top)
                left = event_state.container_left
                top = mouse.y

                if(constrain || e.shiftKey){
                    top = mouse.y - ((width / orig_src.width * orig_src.height) - height)
                }
            } else if (eTargetClass === 'resize-handle-nw') { // north-west (top-left)
                width = event_state.container_width - (mouse.x - event_state.container_left)
                height = event_state.container_height - (mouse.y - event_state.container_top)
                left = mouse.x
                top = mouse.y

                if(constrain || e.shiftKey){
                    top = mouse.y - ((width / orig_src.width * orig_src.height) - height)
                }
            }


            if (constrain || e.shiftKey) {
                height = width / orig_src.width * orig_src.height 
            }

            if (width > min_width && height > min_height && width < max_width && height < max_height) {
                container.style.top = `${top}px`
                container.style.left = `${left}px`
                resizeImage(width, height)
            }
        }

        // before we start tracking the mouse position we want to take
        // a snapshot of the container dimensions and other key data
        // points. We store these in a variable named event_state and
        // use them later as a point of reference while resizing to
        // work out the change in height and width
        const saveEventState = e => {
            event_state.container_width = container.getBoundingClientRect().width 
            event_state.container_height = container.getBoundingClientRect().height 
            event_state.container_left = container.offsetLeft
            event_state.container_top = container.offsetTop 
            // mouse coordinates
            event_state.mouse_x = (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + window.screenLeft,
            event_state.mouse_y = (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + window.screenTop,

            event_state.evnt = e
        }

        // this two functions do very little other that tell the 
        // browser to start paying attention to where the mouse
        // is moving and when to stop paying attention
        const startResize = e => {
            e.preventDefault()
            e.stopPropagation()
            saveEventState(e)
            
            document.addEventListener('mousemove', resizing)
            document.addEventListener('mouseup', endResize)
        }

        const endResize = e => {
            e.preventDefault()
            //  The touchend event fires when one or more touch points are
            // removed from the touch surface.
            document.removeEventListener('mousemove', resizing)
            document.removeEventListener('touchend', resizing)
            document.removeEventListener('mouseup', endResize)
            document.removeEventListener('touchmove', endResize)
        }

        // in this function we need to work out the new position of the top left edge of the 
        // container. This will be equal to the current position of the mouse, offset by the
        // distance the mouse was from the top left corner when we started dragging the image
        const moving = e => {
            const mouse = {
                x: (e.clientX || e.pageX) + window.screenLeft,
                y: (e.clientY || e.pageY) + window.screenTop
            }

            container.style.left = `${mouse.x - (event_state.mouse_y - event_state.container_top)}px`
            container.style.top = `${mouse.y - (event_state.mouse_y - event_state.container_top)}px`
        }

        // function for moving the image around
        const startMoving = e => {
            e.preventDefault()
            e.stopPropagation()
            saveEventState(e)

            document.addEventListener('mousemove', moving)
            document.addEventListener('mouseup', endMoving)
        }

        const endMoving = e => {
            e.preventDefault()

            document.removeEventListener('mouseup', endMoving)
            document.removeEventListener('mousemove', moving)
        }

        const crop = e => {
            const left = overlay.offsetLeft - container.offsetLeft 
            const top = overlay.offsetTop - container.offsetTop 
            const width = overlay.getBoundingClientRect().width
            const height = overlay.getBoundingClientRect().height

            const crop_canvas = document.createElement('canvas')
            crop_canvas.width = width 
            crop_canvas.height = height 

            crop_canvas.getContext('2d').drawImage(image_target, left, top, width, height, 0, 0, width, height)

            const croppedImage = document.createElement('img')
            croppedImage.src = crop_canvas.toDataURL("image/png")
            document.querySelector('#result').appendChild(croppedImage)
        }

        // this function is called immediately and makes a copy of the
        // original image used for resizing
        const init = () => {
            // create a new image with a copy of the original src
            // When resizing, we will always use this original copy
            // as the base
            orig_src.src = image_target.src

            // add events
            container.addEventListener('mousedown', startResize)
            image_target.addEventListener('mousedown', startMoving)
            cropBtn.addEventListener('click', crop)
        }

        init()
    }

    // initializing the canvas and the target image
    const image = document.getElementById('img')
    resizeableImage(image)
</script>

1

以下是去除图像中空白的步骤。

var canvas = document.getElementById("canvas");
var context = canvas.getContext('2d');

var imgWidth = canvas.width;
var imgHeight = canvas.height;

var imgData = context.getImageData(0, 0, imgWidth, imgHeight).data;

// get the corners of the content (not the spaces)
var cropTop = scanY(true);
var cropBottom = scanY(false);
var cropLeft = scanX(true);
var cropRight = scanX(false);

var cropXDiff = cropRight - cropLeft;
var cropYDiff = cropBottom - cropTop;

// get the content that doesn't contain outer spaces
var data = this.context!.getImageData(cropLeft, cropTop, cropXDiff, cropYDiff);

// set again width, height and image data
canvas.width = data.width;
canvas.height = data.height;
context.putImageData(data);

这里是scanX和scanY函数。

scanX = function (fromLeft) {
    var offset = fromLeft? 1 : -1;

    // loop through each column
    for(var x = fromLeft ? 0 : imgWidth - 1; fromLeft ? (x < imgWidth) : (x > -1); x += offset) {

        // loop through each row
        for(var y = 0; y < imgHeight; y++) {
            var rgb = getRBG(x, y);
            if (!isWhite(rgb)) {
                if (fromLeft) {
                    return x;
                } else {
                    return Math.min(x + 1, imgWidth);
                }
            }      
        }
    }
    return null; // all image is white
};

  scanY = function (fromTop) {
    var offset = fromTop ? 1 : -1;

    // loop through each row
    for(var y = fromTop ? 0 : imgHeight - 1; fromTop ? (y < imgHeight) : (y > -1); y += offset) {

        // loop through each column
        for(var x = 0; x < imgWidth; x++) {
            var rgb = getRBG(x, y);
            if (!isWhite(rgb)) {
                if (fromTop) {
                    return y;
                } else {
                    return Math.min(y + 1, imgHeight);
                }
            }
        }
    }
    return null; // all image is white
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接