How to resize then crop an image with canvas

后端 未结 3 1949
一向
一向 2020-11-28 07:17

I already know how to

-> resize an image:

var image = document.getElementById(\'myImage\'),
    canvas = document.createElement(\'canvas\'),
    ctx          


        
相关标签:
3条回答
  • 2020-11-28 07:29

    From the documentation, these are the parameters for drawImage:

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

    enter image description here

    So, to crop the outer 10 pixels from the source image (Assuming it's 100 * 50), and then to scale that up to 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)
    

    Example:

    var image = new Image(),
        canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d');
    
    image.src = 'https://i.stack.imgur.com/I4jXc.png';
    
    image.onload = function(){
        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://i.stack.imgur.com/I4jXc.png" /><br/>
    Canvas: <br/>
    <canvas id="canvas" width="275px" height="95px"></canvas>

    0 讨论(0)
  • 2020-11-28 07:32

    I took this tutorial as an example http://tympanus.net/codrops/2014/10/30/resizing-cropping-images-canvas/ and did the same thing in vanilla js. It probably needs some refacturing but it is working (at least on my windows labtop in chrome)

    One html file:

    <!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>
    
    0 讨论(0)
  • 2020-11-28 07:54

    Before drawing onto the canvas, specify the final canvas size with canvas.width and canvas.height otherwise the final will always be 300x150

    0 讨论(0)
提交回复
热议问题