如何用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的画布区域,没有做等比裁剪。修复时新增通用的正方形居中裁剪绘制函数,逻辑如下:
- 读取图片原始宽高,取短边长度作为正方形裁剪区的边长
- 计算裁剪起点坐标,保证裁剪区域在原图居中位置
- 将裁剪得到的正方形区域等比绘制到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
相关产品推荐
相关产品推荐

