如何在JavaScript中使用四个坐标点实现图片裁剪?
基于任意四点的JavaScript图片裁剪实现
Canvas的drawImage()确实只能处理矩形区域的裁剪/绘制,但要实现任意四边形(四个坐标点)的图片裁剪,核心思路是通过透视变换将四边形区域映射到一个矩形Canvas上,从而得到裁剪后的结果。
实现步骤
- 计算从原图四边形点到目标矩形的透视变换矩阵
- 在Canvas上应用该变换矩阵,将原图的四边形区域"变形"为矩形
- 提取目标Canvas的内容作为最终裁剪后的图片
具体代码实现
1. 透视变换矩阵计算函数
这个函数负责根据四个源点(原图上的裁剪区域)和四个目标点(输出矩形的四个角),求解出Canvas需要的变换矩阵:
// 计算透视变换矩阵:从四个源点映射到四个目标点 function getPerspectiveTransform(srcPoints, dstPoints) { const [s1, s2, s3, s4] = srcPoints; const [d1, d2, d3, d4] = dstPoints; const matrix = []; // 为每个点构建方程组 addPoint(s1, d1); addPoint(s2, d2); addPoint(s3, d3); addPoint(s4, d4); function addPoint(s, d) { matrix.push([s.x, s.y, 1, 0, 0, 0, -s.x * d.x, -s.y * d.x]); matrix.push([0, 0, 0, s.x, s.y, 1, -s.x * d.y, -s.y * d.y]); } const result = [d1.x, d1.y, d2.x, d2.y, d3.x, d3.y, d4.x, d4.y]; // 高斯消元求解矩阵参数 for (let i = 0; i < 8; i++) { let maxRow = i; for (let j = i + 1; j < 8; j++) { if (Math.abs(matrix[j][i]) > Math.abs(matrix[maxRow][i])) maxRow = j; } [matrix[i], matrix[maxRow]] = [matrix[maxRow], matrix[i]]; [result[i], result[maxRow]] = [result[maxRow], result[i]]; const div = matrix[i][i]; for (let j = i; j < 8; j++) matrix[i][j] /= div; result[i] /= div; for (let j = 0; j < 8; j++) { if (j !== i && matrix[j][i] !== 0) { const factor = matrix[j][i]; for (let k = i; k < 8; k++) matrix[j][k] -= factor * matrix[i][k]; result[j] -= factor * result[i]; } } } // 返回适配Canvas transform的矩阵参数 return [result[0], result[1], result[3], result[4], result[2], result[5], result[6], result[7]]; }
2. 裁剪主函数
封装裁剪逻辑,接收原图、四个源点、输出尺寸,返回裁剪后的图片:
function cropImageByFourPoints(img, srcPoints, outputWidth, outputHeight) { // 目标矩形的四个角点(对应输出Canvas的边界) const dstPoints = [ {x: 0, y: 0}, {x: outputWidth, y: 0}, {x: 0, y: outputHeight}, {x: outputWidth, y: outputHeight} ]; // 创建临时Canvas处理变换 const tempCanvas = document.createElement('canvas'); const ctx = tempCanvas.getContext('2d'); tempCanvas.width = outputWidth; tempCanvas.height = outputHeight; // 应用透视变换并绘制原图 const transformMatrix = getPerspectiveTransform(srcPoints, dstPoints); ctx.transform(...transformMatrix); ctx.drawImage(img, 0, 0); // 生成裁剪后的图片对象 const croppedImg = new Image(); croppedImg.src = tempCanvas.toDataURL(); return croppedImg; }
3. 使用示例
// 加载图片后执行裁剪 const img = new Image(); img.crossOrigin = 'anonymous'; // 跨域图片需设置 img.src = 'your-image-url.jpg'; img.onload = function() { // 定义原图上的四个裁剪点(顺序:左上、右上、左下、右下) const srcPoints = [ {x: 50, y: 50}, {x: 250, y: 60}, {x: 40, y: 250}, {x: 260, y: 240} ]; // 裁剪后输出的尺寸 const outputWidth = 200; const outputHeight = 200; const croppedImg = cropImageByFourPoints(img, srcPoints, outputWidth, outputHeight); // 将裁剪后的图片添加到页面 document.body.appendChild(croppedImg); };
注意事项
- 源点的顺序必须和目标点对应(左上→左上、右上→右上),否则变换结果会错乱
- 处理跨域图片时,需确保图片服务器配置了CORS,同时设置
img.crossOrigin = 'anonymous' - 上述矩阵求解是简化实现,若需处理极端场景,可结合成熟的线性代数库优化
内容的提问来源于stack exchange,提问作者Sanjay Devadiga
相关产品推荐
相关产品推荐

