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

如何用jQuery实现Canvas裁剪图片为正方形而非拉伸缩放

问题说明
  • 正在开发双图片叠加功能:一张图片由用户上传,另一张为系统默认预置图片
  • 现存异常:当用户上传矩形(非正方形)图片时,现有Canvas绘制逻辑会对图片做拉伸缩放处理。预期效果为Canvas直接将图片居中裁剪为正方形尺寸,无拉伸变形,裁剪完成后再应用滤镜、完成叠加。
  • 现有实现代码:

原有JS逻辑

$('.file1, .file2').on('change', function() {
  var reader = new FileReader(),
    imageSelector = $(this).data('image-selector');

  if (this.files && this.files[0]) {
    reader.onload = function(e) {
      imageIsLoaded(e, imageSelector)
    };
    reader.readAsDataURL(this.files[0]);
  }
});

$('.btn-merge').on('click', merge);

function imageIsLoaded(e, imageSelector) {
  $(imageSelector).attr('src', e.target.result);
  $(imageSelector).removeClass('hidden');
};

function merge() {
  var canvas = document.getElementById('canvas'),

    ctx = canvas.getContext('2d'),
    imageObj1 = new Image(),
    imageObj2 = new Image();
  canvas.width = 300;
  canvas.height = 300;
  imageObj1.src = $('.image1').attr('src');
  imageObj1.onload = function() {
    ctx.globalAlpha = 1;
    ctx.drawImage(imageObj1, 0, 0, 300, 300);
    imageObj2.src = $('.image2').attr('src');
    imageObj2.onload = function() {
      ctx.globalAlpha = 1;
      ctx.drawImage(imageObj2, 0, 0, 300, 300);
      var img = canvas.toDataURL('img/png');
      $('.merged-image').attr('src', img);
      var mergedimage = document.getElementById('mergedimage');
      $('.merged-image').removeClass('hidden');
      $("#downloadfinal").attr("href", mergedimage.src);
    }

  };
}

原有HTML结构

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<div class="container-fluid shadow">

  <div class="container d-flex flex-column justify-content-center">
    <div class="row mb-1 ">
      <h2>Votre photo ici : </h2>
      <label class="custom ml-3 hover-underline-animation"> <input class="ml-2 file1" type="file" data-image-selector=".image1" />Selectionner un fichier</label>

    </div>
    <img class="image1 hidden mb-4" alt="abs" width="200px" height="auto" />
    <div class="row">

      <h2 class="mr-2 ">Filtre par défaut : </h2>


      <img alt="abs image3" width="200px" height="auto" src="img/filtre.png" />
    </div>
    <div class="otherfilter">

      <div class="row mt-5 mb-5">
        <h2 class="">Mettez un autre filtre ici : </h2>
        <label class="custom ml-3 hover-underline-animation"> <input class="ml-2 file2" type="file" data-image-selector=".image2" />Selectionner un fichier</label>
      </div>

      <img class="hidden image2" alt="ab" width="200px" height="auto" src="img/filtre.png">
    </div>

  </div>



  <br />
  <div class="text-center mb-5">
    <input class="btn-merge mb-3" type="button" value="Appliquer le filtre" />
    <br />
    <img class="merged-image hidden mb-3" id="mergedimage" alt="merged image" />
    <canvas id="canvas" class="hidden"></canvas>
    <br>
    <a class="btn-dl" id="downloadfinal" role="button" href="#" download="photo_profil_modifie">
      <i class="mr-2 bi bi-download"></i>Télécharger
    </a>

  </div>
修复方案

拉伸问题的根源是原有drawImage调用直接将整张原图强制映射到300*300的画布区域,没有做等比裁剪。修复时新增通用的正方形居中裁剪绘制函数,逻辑如下:

  1. 读取图片原始宽高,取短边长度作为正方形裁剪区的边长
  2. 计算裁剪起点坐标,保证裁剪区域在原图居中位置
  3. 将裁剪得到的正方形区域等比绘制到300*300的画布上,完全避免拉伸

修复后核心JS代码

// 居中裁剪绘制正方形的通用方法
function drawSquareImage(ctx, img, targetSize) {
  const imgWidth = img.width;
  const imgHeight = img.height;
  // 计算短边长度,作为裁剪边长
  const cropEdge = Math.min(imgWidth, imgHeight);
  // 计算原图上的裁剪起点,居中裁剪
  const sx = (imgWidth - cropEdge) / 2;
  const sy = (imgHeight - cropEdge) / 2;
  // 从原图(sx,sy)位置裁剪cropEdge*cropEdge的正方形区域,缩放到targetSize*targetSize
  ctx.drawImage(img, sx, sy, cropEdge, cropEdge, 0, 0, targetSize, targetSize);
}

function merge() {
  var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    imageObj1 = new Image(),
    imageObj2 = new Image();
  const canvasSize = 300;
  canvas.width = canvasSize;
  canvas.height = canvasSize;
  imageObj1.src = $('.image1').attr('src');
  imageObj1.onload = function() {
    ctx.globalAlpha = 1;
    // 替换原有直接drawImage逻辑,调用裁剪绘制方法
    drawSquareImage(ctx, imageObj1, canvasSize);
    imageObj2.src = $('.image2').attr('src');
    imageObj2.onload = function() {
      ctx.globalAlpha = 1;
      // 滤镜图同理走裁剪逻辑,预置滤镜图本身为正方形时不会有显示异常
      drawSquareImage(ctx, imageObj2, canvasSize);
      // 修复原有toDataURL的MIME类型错误,原写法img/png为无效值
      var img = canvas.toDataURL('image/png');
      $('.merged-image').attr('src', img);
      var mergedimage = document.getElementById('mergedimage');
      $('.merged-image').removeClass('hidden');
      $("#downloadfinal").attr("href", mergedimage.src);
    }
  };
}

额外修复原有代码隐性bug:toDataURL传入的img/png是无效MIME类型,正确值为image/png,否则部分浏览器导出图片会异常。

替换原有merge函数、新增drawSquareImage工具函数后,所有矩形图片都会自动居中裁剪为正方形,不会出现拉伸变形,裁剪完成后会正常叠加预置滤镜图。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:54:21