上传图片实现裁剪功能

下面效果图

 

下面是源代码

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8" />
    <title>图片上传并裁剪</title>
    <style>
        body {
            font-family: sans-serif;
            padding: 20px;
        }

        .preview-container {
            position: relative;
            display: inline-block;
            border: 1px solid #ccc;
            margin-top: 10px;
        }

        #preview {
            max-width: 500px;
            max-height: 500px;
            display: block;
        }

        .crop-box {
            position: absolute;
            border: 2px dashed red;
            cursor: move;
            background-color: rgba(255, 255, 255, 0.2);
            box-sizing: border-box;
        }

        .resize-handle {
            position: absolute;
            width: 10px;
            height: 10px;
            background: red;
            z-index: 10;
        }

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

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

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

        .resize-handle.se {
            bottom: -5px;
            right: -5px;
            cursor: nwse-resize;
        }

        .resize-handle.n {
            top: -5px;
            left: 50%;
            transform: translateX(-50%);
            cursor: ns-resize;
        }

        .resize-handle.s {
            bottom: -5px;
            left: 50%;
            transform: translateX(-50%);
            cursor: ns-resize;
        }

        .resize-handle.e {
            right: -5px;
            top: 50%;
            transform: translateY(-50%);
            cursor: ew-resize;
        }

        .resize-handle.w {
            left: -5px;
            top: 50%;
            transform: translateY(-50%);
            cursor: ew-resize;
        }

        canvas {
            margin-top: 20px;
        }
    </style>
</head>

<body>
    <h2>上传图片并裁剪</h2>
    <input type="file" id="fileInput" accept="image/*" />
    <div class="preview-container" id="container">
        <img id="preview" />
        <div id="cropBox" class="crop-box" style="width: 100px; height: 100px; top: 50px; left: 50px;">
            <div class="resize-handle nw"></div>
            <div class="resize-handle ne"></div>
            <div class="resize-handle sw"></div>
            <div class="resize-handle se"></div>
            <div class="resize-handle n"></div>
            <div class="resize-handle s"></div>
            <div class="resize-handle e"></div>
            <div class="resize-handle w"></div>
        </div>
    </div>
    <br />
    <button onclick="cropImage()">裁剪图片</button>

    <h3>裁剪结果:</h3>
    <canvas id="canvas"></canvas>

    <script>
        const fileInput = document.getElementById('fileInput');
        const preview = document.getElementById('preview');
        const cropBox = document.getElementById('cropBox');
        const container = document.getElementById('container');

        let imgLoaded = false;

        // 1. 图片上传并显示
        fileInput.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (!file) return;
            const reader = new FileReader();
            reader.onload = (event) => {
                preview.src = event.target.result;
                preview.style.display = 'block';
                preview.onload = () => {
                    imgLoaded = true;
                    // 设置图片最大宽高
                    let maxW = 500, maxH = 500;
                    let ratio = Math.min(maxW / preview.naturalWidth, maxH / preview.naturalHeight, 1);
                    preview.width = preview.naturalWidth * ratio;
                    preview.height = preview.naturalHeight * ratio;
                    container.style.width = preview.width + 'px';
                    container.style.height = preview.height + 'px';
                    // 裁剪框居中
                    cropBox.style.left = (preview.width / 2 - cropBox.offsetWidth / 2) + 'px';
                    cropBox.style.top = (preview.height / 2 - cropBox.offsetHeight / 2) + 'px';
                };
            };
            reader.readAsDataURL(file);
        });

        // 2. 拖动裁剪框
        let isDragging = false;
        let dragStartX, dragStartY, boxStartLeft, boxStartTop;

        cropBox.addEventListener('mousedown', (e) => {
            if (e.target.classList.contains('resize-handle')) return;
            isDragging = true;
            dragStartX = e.clientX;
            dragStartY = e.clientY;
            boxStartLeft = parseInt(cropBox.style.left);
            boxStartTop = parseInt(cropBox.style.top);
            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                let dx = e.clientX - dragStartX;
                let dy = e.clientY - dragStartY;
                let newLeft = Math.max(0, Math.min(boxStartLeft + dx, container.offsetWidth - cropBox.offsetWidth));
                let newTop = Math.max(0, Math.min(boxStartTop + dy, container.offsetHeight - cropBox.offsetHeight));
                cropBox.style.left = newLeft + 'px';
                cropBox.style.top = newTop + 'px';
            } else if (isResizing) {
                // 缩放逻辑见下
                const deltaX = e.clientX - resizeStartX;
                const deltaY = e.clientY - resizeStartY;
                let newWidth = resizeStartWidth;
                let newHeight = resizeStartHeight;
                let newLeft = resizeStartLeft;
                let newTop = resizeStartTop;

                switch (resizeDir) {
                    case 'nw':
                        newWidth = resizeStartWidth - deltaX;
                        newHeight = resizeStartHeight - deltaY;
                        newLeft = resizeStartLeft + deltaX;
                        newTop = resizeStartTop + deltaY;
                        break;
                    case 'ne':
                        newWidth = resizeStartWidth + deltaX;
                        newHeight = resizeStartHeight - deltaY;
                        newTop = resizeStartTop + deltaY;
                        break;
                    case 'sw':
                        newWidth = resizeStartWidth - deltaX;
                        newHeight = resizeStartHeight + deltaY;
                        newLeft = resizeStartLeft + deltaX;
                        break;
                    case 'se':
                        newWidth = resizeStartWidth + deltaX;
                        newHeight = resizeStartHeight + deltaY;
                        break;
                    case 'n':
                        newHeight = resizeStartHeight - deltaY;
                        newTop = resizeStartTop + deltaY;
                        break;
                    case 's':
                        newHeight = resizeStartHeight + deltaY;
                        break;
                    case 'e':
                        newWidth = resizeStartWidth + deltaX;
                        break;
                    case 'w':
                        newWidth = resizeStartWidth - deltaX;
                        newLeft = resizeStartLeft + deltaX;
                        break;
                }

                // 限制最小尺寸
                newWidth = Math.max(20, newWidth);
                newHeight = Math.max(20, newHeight);

                // 限制在容器内
                if (newLeft < 0) newLeft = 0;
                if (newTop < 0) newTop = 0;
                if (newLeft + newWidth > container.offsetWidth) newWidth = container.offsetWidth - newLeft;
                if (newTop + newHeight > container.offsetHeight) newHeight = container.offsetHeight - newTop;

                cropBox.style.width = newWidth + 'px';
                cropBox.style.height = newHeight + 'px';
                cropBox.style.left = newLeft + 'px';
                cropBox.style.top = newTop + 'px';
            }
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
            isResizing = false;
        });

        // 3. 缩放裁剪框
        let isResizing = false;
        let resizeDir = '';
        let resizeStartX, resizeStartY, resizeStartWidth, resizeStartHeight, resizeStartLeft, resizeStartTop;

        document.querySelectorAll('.resize-handle').forEach(handle => {
            handle.addEventListener('mousedown', (e) => {
                e.stopPropagation();
                isResizing = true;
                resizeDir = [...handle.classList].find(cls => cls !== 'resize-handle');
                resizeStartX = e.clientX;
                resizeStartY = e.clientY;
                resizeStartWidth = cropBox.offsetWidth;
                resizeStartHeight = cropBox.offsetHeight;
                resizeStartLeft = parseInt(cropBox.style.left);
                resizeStartTop = parseInt(cropBox.style.top);
                e.preventDefault();
            });
        });

        function cropImage() {
            if (!imgLoaded) return alert('请先上传图片');

            const canvas = document.getElementById('canvas');
            const ctx = canvas.getContext('2d');

            const img = preview;
            const scale = img.naturalWidth / img.width;

            const cropX = parseInt(cropBox.style.left) * scale;
            const cropY = parseInt(cropBox.style.top) * scale;
            const cropW = cropBox.offsetWidth * scale;
            const cropH = cropBox.offsetHeight * scale;

            canvas.width = cropW;
            canvas.height = cropH;

            ctx.drawImage(img, cropX, cropY, cropW, cropH, 0, 0, cropW, cropH);
        }
    </script>
</body>

</html>

 

posted @ 2025-07-14 17:46  unique-yb  阅读(13)  评论(0)    收藏  举报