You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CSS变换图像超出容器部分填白并还原尺寸的实现方案

动态图片变换与裁剪处理问题

我有一个包含图片的DIV容器,图片应用了translate、rotate、scale等CSS变换。用户可在父容器内拖拽、缩放、旋转图片,部分图像区域会超出容器范围。我需要生成一张与原图尺寸完全一致的新图(无旋转、无缩放),但原变换后超出容器的图像部分需填充为白色。

效果示例图:
效果示例图

原图:
原图

我尝试了多种方法但未成功,以下是我借助ChatGPT编写的代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>动态图片变换与裁剪</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        .container {
            width: 300px; /* 可修改为任意尺寸 */
            height: 300px; /* 可修改为任意尺寸 */
            border: 2px solid #000;
            position: relative;
            overflow: hidden;
            margin-bottom: 20px;
        }
        .container img {
            width: 150px; /* 可修改为任意尺寸 */
            height: 150px; /* 可修改为任意尺寸 */
            /* 应用CSS变换 */
            transform: translate(-30px, -30px) rotate(45deg) scale(1.2);
            transform-origin: center center;
            position: absolute;
            top: 0;
            left: 0;
        }
        #buttons {
            margin-bottom: 20px;
        }
        #tempCanvases {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }
        #tempCanvases canvas {
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <h1>动态图片变换与裁剪</h1>
    <div class="container">
        <img id="sourceImage" src="https://i.sstatic.net/M6v4Hvlp.jpg" alt="原图">
    </div>
    <div id="buttons">
        <button id="processButton">处理图片</button>
        <button id="downloadButton" disabled>下载图片</button>
    </div>
    <h2>临时画布(用于调试)</h2>
    <div id="tempCanvases"></div>

    <script>
        // 等待DOM加载完成
        document.addEventListener('DOMContentLoaded', () => {
            const processButton = document.getElementById('processButton');
            const downloadButton = document.getElementById('downloadButton');
            const tempCanvasesDiv = document.getElementById('tempCanvases');
            const sourceImage = document.getElementById('sourceImage');
            let finalCanvas = null; // 存储最终处理后的画布

            processButton.addEventListener('click', () => {
                // 清空之前的临时画布
                tempCanvasesDiv.innerHTML = '';
                finalCanvas = null;
                downloadButton.disabled = true;

                // 步骤1:获取容器和图片尺寸
                const container = sourceImage.parentElement;
                const containerWidth = container.clientWidth;
                const containerHeight = container.clientHeight;

                const imageNaturalWidth = sourceImage.naturalWidth;
                const imageNaturalHeight = sourceImage.naturalHeight;

                const imageRenderedWidth = sourceImage.width;
                const imageRenderedHeight = sourceImage.height;

                console.log('容器尺寸:', containerWidth, containerHeight);
                console.log('图片原始尺寸:', imageNaturalWidth, imageNaturalHeight);
                console.log('图片渲染尺寸:', imageRenderedWidth, imageRenderedHeight);

                // 步骤2:获取图片的计算样式
                const style = window.getComputedStyle(sourceImage);
                const transform = style.transform;

                // 如果没有应用变换,设置为单位矩阵
                const matrix = transform === 'none' ? new DOMMatrix() : new DOMMatrix(transform);

                // 提取变换参数
                const scaleX = matrix.a;
                const scaleY = matrix.d;
                const rotateRadians = Math.atan2(matrix.b, matrix.a);
                const rotateDegrees = rotateRadians * (180 / Math.PI);
                const translateX = matrix.e;
                const translateY = matrix.f;

                console.log('提取的变换参数:');
                console.log('X轴缩放:', scaleX);
                console.log('Y轴缩放:', scaleY);
                console.log('旋转角度(度):', rotateDegrees);
                console.log('X轴平移:', translateX);
                console.log('Y轴平移:', translateY);

                // 步骤3:创建第一个临时画布(容器尺寸)并应用变换
                const tempCanvas1 = document.createElement('canvas');
                tempCanvas1.width = containerWidth;
                tempCanvas1.height = containerHeight;
                const ctx1 = tempCanvas1.getContext('2d');

                // 填充白色背景
                ctx1.fillStyle = '#FFFFFF';
                ctx1.fillRect(0, 0, tempCanvas1.width, tempCanvas1.height);

                // 计算图片中心位置
                const centerX = imageRenderedWidth / 2;
                const centerY = imageRenderedHeight / 2;

                // 应用变换:围绕中心平移、旋转、缩放
                ctx1.translate(translateX + centerX, translateY + centerY); // 移动到中心
                ctx1.rotate(rotateRadians); // 应用旋转
                ctx1.scale(scaleX, scaleY); // 应用缩放
                ctx1.translate(-centerX, -centerY); // 移回原位置

                // 绘制图片
                ctx1.drawImage(sourceImage, 0, 0, imageRenderedWidth, imageRenderedHeight);

                // 添加第一个临时画布用于调试
                appendCanvas(tempCanvas1, '变换后图片');

                // 步骤4:创建第二个临时画布以还原变换并裁剪
                const tempCanvas2 = document.createElement('canvas');
                tempCanvas2.width = containerWidth;
                tempCanvas2.height = containerHeight;
                const ctx2 = tempCanvas2.getContext('2d');

                // 填充白色背景
                ctx2.fillStyle = '#FFFFFF';
                ctx2.fillRect(0, 0, tempCanvas2.width, tempCanvas2.height);

                // 还原变换:应用逆变换
                // 逆缩放
                const invScaleX = 1 / scaleX;
                const invScaleY = 1 / scaleY;
                // 逆旋转
                const invRotateRadians = -rotateRadians;

                ctx2.translate(-translateX - centerX, -translateY - centerY); // 反向平移
                ctx2.translate(centerX, centerY); // 移动到中心
                ctx2.rotate(invRotateRadians); // 应用逆旋转
                ctx2.scale(invScaleX, invScaleY); // 应用逆缩放
                ctx2.translate(-centerX, -centerY); // 移回原位置

                // 绘制图片
                ctx2.drawImage(sourceImage, 0, 0, imageRenderedWidth, imageRenderedHeight);

                // 添加第二个临时画布用于调试
                appendCanvas(tempCanvas2, '还原变换后图片');

                // 步骤5:裁剪回图片原始尺寸
                // 创建与图片原始尺寸一致的最终画布
                finalCanvas = document.createElement('canvas');
                finalCanvas.width = imageNaturalWidth;
                finalCanvas.height = imageNaturalHeight;
                const ctxFinal = finalCanvas.getContext('2d');

                // 填充白色背景
                ctxFinal.fillStyle = '#FFFFFF';
                ctxFinal.fillRect(0, 0, finalCanvas.width, finalCanvas.height);

                // 计算渲染尺寸与原始尺寸的缩放比例
                const scaleFactorX = imageNaturalWidth / imageRenderedWidth;
                const scaleFactorY = imageNaturalHeight / imageRenderedHeight;

                // 将还原后的图片绘制到最终画布
                ctxFinal.drawImage(
                    tempCanvas2,
                    0, 0, containerWidth, containerHeight, // 源区域
                    0, 0, finalCanvas.width, finalCanvas.height // 目标区域
                );

                // 添加最终画布用于调试
                appendCanvas(finalCanvas, '最终裁剪图片');

                // 启用下载按钮
                downloadButton.disabled = false;
            });

            downloadButton.addEventListener('click', () => {
                if (!finalCanvas) return;

                // 将最终画布转换为Data URL
                const dataURL = finalCanvas.toDataURL('image/png');

                // 创建临时链接触发下载
                const link = document.createElement('a');
                link.href = dataURL;
                link.download = 'processed_image.png';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            });

            /**
             * 工具函数:添加带标签的画布用于调试
             * @param {HTMLCanvasElement} canvas 
             * @param {string} label 
             */
            function appendCanvas(canvas, label) {
                const wrapper = document.createElement('div');
                const caption = document.createElement('p');
                caption.textContent = label;
                wrapper.appendChild(caption);
                wrapper.appendChild(canvas);
                tempCanvasesDiv.appendChild(wrapper);
            }
        });
    </script>
</body>
</html>

内容的提问来源于stack exchange,提问作者BlasterGod

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 22:19:52