<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>html</title>
</head>

<body>
    <div style="margin-bottom: 10px">
        上传图片:<input type="file" onchange="onChange(this.files[0])">
    </div>
    <canvas id="cvs"></canvas>
    <canvas id="clipCvs"></canvas>
    <button id="download">下载图片</button>
</body>
<script>
    const cvs = document.getElementById('cvs')
    const clipCvs = document.getElementById('clipCvs')
    const download = document.getElementById('download')
    const ctx = cvs.getContext('2d')
    const clipCtx = clipCvs.getContext('2d')
    const img = new Image()
    let size = 150
    let maxW = 400
    const p = {
        left: 0,
        top: 0,
        stepX: 0,
        stepY: 0
    }
    const onChange = (file) => {
        onInit(URL.createObjectURL(file))
    }
    // 加载图片,并初始化裁剪功能
    const onInit = (src) => {
        clipCvs.width = clipCvs.height = size
        img.src = src
        img.onload = () => {
            let width = img.width
            let height = img.height
            if (width > maxW) {
                
                height = maxW / width * height
                width = maxW
            }
            cvs.width = width
            cvs.height = height
            render(width / 2 - size / 2, height / 2 - size / 2)
        }
    }
    // 渲染裁剪前canvas
    const render = (left = 0, top = 0) => {
        ctx.clearRect(0, 0, cvs.width, cvs.height)
        ctx.drawImage(img, 0, 0, cvs.width, cvs.height)
        if (left < 0) {
            left = 0
        }
        if (left > cvs.width - size) {
            left = cvs.width - size
        }
        if (top < 0) {
            top = 0
        }
        if (top > cvs.height - size) {
            top = cvs.height - size
        }
        clipPic(ctx.getImageData(left, top, size, size))
        ctx.beginPath()
        ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'
        ctx.fillRect(left, top, size, size)
        p.left = left
        p.top = top
    }
    // 裁剪图片,并显示在右侧
    const clipPic = (data) => {
        clipCtx.clearRect(0, 0, clipCvs.width, clipCvs.height)
        clipCtx.putImageData(data, 0, 0)
    }
    
    let isMoving = false
    cvs.onmousedown = (e) => {
        p.stepX = e.pageX - p.left
        p.stepY = e.pageY - p.top
        isMoving = true
    }
    cvs.onmousemove = (e) => {
        if (isMoving) {
            render(e.pageX - p.stepX, e.pageY - p.stepY)
        }
    }
    document.onmouseup = () => {
        isMoving = false
    }
    download.onclick = async () => {
        const res = await fetch(clipCvs.toDataURL('image/png'))
        const blob = await res.blob()
        const a = document.createElement('a')
        a.setAttribute('download', 'clip.png')
        a.href = URL.createObjectURL(blob)
        a.click()
    }
    onInit('./images/girl.png')
</script>
</html>