You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

React TypeScript画布图片缩放组件为何模糊?如何裁剪为180x180?

解决图片裁剪为固定正方形及模糊问题

嘿,我来帮你搞定这两个问题——把图片裁剪成180x180的正方形,同时解决缩放后模糊的问题。咱们先拆解问题,再一步步修改你的代码:

一、裁剪为180x180正方形

你的现有代码是按原图比例缩放,所以如果原图不是1:1的正方形,缩放后自然不会得到180x180的结果。要生成固定尺寸的正方形,需要先从原图中裁剪出一个正方形区域(通常取中心位置),再把这个裁剪后的正方形缩放到目标尺寸。

修改resizeImage函数里的尺寸计算逻辑:

image.onload = async (imageEvent) => {
  const canvas = document.createElement('canvas');
  const targetSize = props.resizeTo; // 180
  // 1. 计算原图中要裁剪的正方形区域
  const cropSize = Math.min(image.width, image.height);
  const cropX = (image.width - cropSize) / 2;
  const cropY = (image.height - cropSize) / 2;

  // 2. 设置canvas为目标正方形尺寸
  canvas.width = targetSize;
  canvas.height = targetSize;

  const ctx: CanvasRenderingContext2D = canvas.getContext('2d')!;
  // --- 这里先保留,后面解决模糊问题再调整 ---
  // 3. 绘制:把裁剪后的区域缩放到canvas上
  ctx.drawImage(
    image,
    cropX, cropY, cropSize, cropSize, // 原图裁剪区域
    0, 0, targetSize, targetSize // 目标canvas区域
  );
  // ... 后续代码不变
};

这样不管原图是横版还是竖版,都会取中心的正方形部分,再缩放到180x180,和其他网站的结果就可以公平对比了。

二、解决图片模糊问题

你的代码里设置了ctx.imageSmoothingEnabled = false,这是导致模糊/像素化的主要原因之一!关闭平滑后,缩小图片时会丢失细节,反而变得模糊。另外,还要考虑高清屏幕的设备像素比(DPR),不然在Retina屏幕上渲染的canvas会被拉伸,显得模糊。

修改点:

  1. 启用图像平滑并设置高质量插值
  2. 适配设备像素比(DPR)

更新后的canvas绘制逻辑:

image.onload = async (imageEvent) => {
  const canvas = document.createElement('canvas');
  const targetSize = props.resizeTo; // 180
  const cropSize = Math.min(image.width, image.height);
  const cropX = (image.width - cropSize) / 2;
  const cropY = (image.height - cropSize) / 2;

  // 适配设备像素比,避免高清屏幕模糊
  const dpr = window.devicePixelRatio || 1;
  const canvasSize = targetSize * dpr;
  canvas.width = canvasSize;
  canvas.height = canvasSize;
  // 设置canvas元素的显示尺寸为目标大小
  canvas.style.width = `${targetSize}px`;
  canvas.style.height = `${targetSize}px`;

  const ctx: CanvasRenderingContext2D = canvas.getContext('2d')!;
  // 启用平滑,并设置高质量插值
  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = 'high'; // 可选值:low/medium/high
  // 缩放坐标系,适配DPR
  ctx.scale(dpr, dpr);

  // 绘制裁剪并缩放后的图片
  ctx.drawImage(
    image,
    cropX, cropY, cropSize, cropSize,
    0, 0, targetSize, targetSize
  );

  // 后续生成dataURL和Blob的代码不变
  const dataUrl = canvas.toDataURL('image/jpeg', 0.9); // 可以调整质量参数,0-1
  const resizedImage = dataURLToBlob(dataUrl);
  setState((prev) => ({ ...prev, imageURL: dataUrl }));
  if (props.blob) props.blob(resizedImage);
  if (props.base64) props.base64(dataUrl);
};

额外优化点:

  • 调用canvas.toDataURL时可以添加质量参数(第二个参数,0-1),比如toDataURL('image/jpeg', 0.9),平衡文件大小和画质。
  • 移除原来的reader.readAsDataURL(file),因为你已经用了URL.createObjectURL(file),不需要重复读取,直接用后者效率更高,记得最后调用URL.revokeObjectURL(image.src)释放内存。

完整修改后的组件代码(关键部分)

const resizeImage = (event: React.ChangeEvent<HTMLInputElement>) => {
  if (event.currentTarget.files) {
    const file = event.currentTarget.files[0];
    const image = new Image();
    image.onload = async (imageEvent) => {
      const canvas = document.createElement('canvas');
      const targetSize = props.resizeTo;
      const cropSize = Math.min(image.width, image.height);
      const cropX = (image.width - cropSize) / 2;
      const cropY = (image.height - cropSize) / 2;

      const dpr = window.devicePixelRatio || 1;
      const canvasSize = targetSize * dpr;
      canvas.width = canvasSize;
      canvas.height = canvasSize;
      canvas.style.width = `${targetSize}px`;
      canvas.style.height = `${targetSize}px`;

      const ctx: CanvasRenderingContext2D = canvas.getContext('2d')!;
      ctx.imageSmoothingEnabled = true;
      ctx.imageSmoothingQuality = 'high';
      ctx.scale(dpr, dpr);

      ctx.drawImage(
        image,
        cropX, cropY, cropSize, cropSize,
        0, 0, targetSize, targetSize
      );

      const dataUrl = canvas.toDataURL('image/jpeg', 0.9);
      const resizedImage = dataURLToBlob(dataUrl);
      setState((prev) => ({ ...prev, imageURL: dataUrl }));
      if (props.blob) props.blob(resizedImage);
      if (props.base64) props.base64(dataUrl);
      // 释放URL对象,避免内存泄漏
      URL.revokeObjectURL(image.src);
    };
    image.src = URL.createObjectURL(file);
  }
};

这样修改后,你就能得到清晰的180x180正方形图片了,和其他网站的结果对比也更公平。

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

火山引擎 最新活动