照片裁剪✂✂✂-基础裁剪、裁剪器裁剪、固定规格裁剪、不规则裁剪

零 JavaScript教程评论76字数 6303阅读21分0秒阅读模式

照片裁剪✂✂✂-基础裁剪、裁剪器裁剪、固定规格裁剪、不规则裁剪

基础裁剪

照片裁剪一个很常见的功能了,今天咱们来手撸一个耍耍看。?

当前,照片裁剪在很大程度上已经转向基于 Canvas 来实现,这样做有几个好处:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

  • 性能:Canvas 能通过GPU加速进行图像操作,这通常比CPU处理更快。
  • 灵活性:Canvas API 提供了丰富的绘图和图像处理能力,可以轻松实现复杂的裁剪、旋转、缩放等操作。
  • 易于集成、无需依赖:Canvas 可以轻易无缝集成到各种库/框架(如 React/Vue 等)中,不需要安装额外的插件。
  • ...

咱们来看一个最简单的裁剪案例:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

html文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

代码解读
复制代码
<!DOCTYPE html>
<html>
<head>
  <style>
    canvas { border: 1px solid #ccc;  margin-top: 10px; }
  </style>
</head>
<body>
  <input id="file" type="file">
  <canvas id="canvas"></canvas>
  <canvas id="canvasCropping"></canvas>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const file = document.getElementById('file');
      const canvas = document.getElementById('canvas');
      const canvasCtx = canvas.getContext('2d');
      const canvasCropping = document.getElementById('canvasCropping');
      const canvasCroppingCtx = canvasCropping.getContext('2d');
      
      const image = new Image();

      file.addEventListener('change', (e) => {
        const file = e.target.files[0];
        image.src = URL.createObjectURL(file);
        image.onload = () => {
          // 将画布大小调整成照片大小
          const { width, height } = image;
          canvas.width = width;
          canvas.height = height;
          // 将图片绘制到画布中
          canvasCtx.drawImage(image, 0, 0, width, height);
          
          // 裁剪出坐标为(0, 0),width与height都为100的照片
          const croppingImageData = canvasCtx.getImageData(0, 0, 100, 100);
          // 将裁剪到的照片绘制到新的画布中预览
          canvasCroppingCtx.putImageData(croppingImageData, 0, 0)
        };
      })
    });
  </script>
</body>
</html>

很简单,咱们上传了一个照片,将它完整的绘制在左边的 Canvas 中,然后进行裁剪,将裁剪得到的照片绘制到了右边的 Canvas 中。 ?文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

效果如下:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

2024712-1.gif你可以直接在右边的 Canvas 上鼠标右键将照片下载下来,也可以通过代码,去调用 Canvas 的 toDataURL 方法,将裁剪的照片下载下来了,这样就能得到你想要的裁剪照片了。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

从上面代码中,可以看到咱们是调用了两个 Canvas 的相关API来完成裁剪的,如下:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

ctx.getImageData(x, y, width, height):用于获取画布中的某个区域内容,能得到一个 ImageData 对象。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

image.pngctx.putImageData(imagedata, x, y):能将 ImageData 对象绘制到画布中。文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

裁剪器裁剪

平常的照片裁剪功能一般都是有一个裁剪器,它能帮助我们裁剪需要的部分,正好,咱们在上一篇文章 ???图片裁剪器✂✂✂ 已经完成了裁剪器这个功能。如下:文章源自灵鲨社区-https://www.0s52.com/bcjc/javascriptjc/17147.html

2024711-2.gif这里咱们就直接拿过来用用,裁剪器源码可以看这里。传送门 ???

来看看这个裁剪器要如何与具体裁剪功能结合起来:

html

代码解读
复制代码
<button id="btn">裁剪</button>
<canvas id="canvasCropping"></canvas>

js

代码解读
复制代码
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
  // currentDimention: top/right/bottom/left
  if (currentDimention.length > 0) {
    // 图片的宽度与高度
    const { width: imageWidth, height: imageHeight } = bgImage.getBoundingClientRect();
    // 创建一个canvas用于裁剪操作,其实也可以将bgImage本身变成一个canvas,直接在上面操作就行
    const canvas = document.createElement('canvas');
    const canvasCtx = canvas.getContext('2d');
    canvas.width = imageWidth;
    canvas.height = imageHeight;
    // 绘制照片
    canvasCtx.drawImage(bgImage, 0, 0, imageWidth, imageHeight);
    // 裁剪照片
    const x = currentDimention[LEFT];
    const y = currentDimention[TOP];
    const width = imageWidth - currentDimention[LEFT] - currentDimention[RIGHT];
    const height = imageHeight - currentDimention[TOP] - currentDimention[BOTTOM];
    const croppingImageData = canvasCtx.getImageData(x, y, width, height);
    // 通过canvas展示裁剪的照片
    const canvasCropping = document.getElementById('canvasCropping');
    const canvasCroppingCtx = canvasCropping.getContext('2d');
    canvasCropping.width = croppingImageData.width;
    canvasCropping.height = croppingImageData.height;
    // 清空一下画布
    canvasCroppingCtx.clearRect(0, 0, croppingImageData.width, croppingImageData.height);
    //将裁剪到的照片绘制到新的画布中预览
    canvasCroppingCtx.putImageData(croppingImageData, 0, 0);
  }
})

上面仅是裁剪功能的代码,关于 currentDimention/bgImage/LEFT... 等变量是什么,最好还是要先去看看上一篇文章 ???图片裁剪器✂✂✂ 的内容。

其实,咱们主要目的就是为了得到 ctx.getImageData(x, y, width, height) API所需的参数信息,裁剪器的目的也是如此。

还有,由于是不断往 Canvas 上绘制新裁剪照片,每次绘制之前记得清空一下画布哦?:ctx.clearRect(x, y, width, height)。

效果如下:

2024712-2.gif

固定规格裁剪

固定规格裁剪,这应该算是裁剪器那边的小功能,等于就是提前固定了裁剪器的一些规格尺寸。

效果如下:

2024712-3.gif实现也很简单:

html

代码解读
复制代码
<button id="btn-small"></button>
<button id="btn-middel"></button>
<button id="btn-big"></button>

javascript

代码解读
复制代码
function createCropper() {
  mask.style.display = "block";
  cropper.style.display = "block";
  setDimention(initDimention);
  // 增加这行
  currentDimention = initDimention;
  // ...
}
btnSmall.addEventListener("click", () => {
  currentDimention = [150, 150, 150, 150];
  setDimention(currentDimention);
})
btnMiddel.addEventListener("click", () => {
  currentDimention = [100, 100, 100, 100];
  setDimention(currentDimention);
})
btnBig.addEventListener("click", () => {
  currentDimention = [10, 10, 10, 10];
  setDimention(currentDimention);
})

这部分其实是属于一个铺垫?抛砖引玉?? (反正就是为了继续引出下文内容吧。)

不规则裁剪

是不是有点意犹未尽?咋固定规格全是矩形?能否搞个圆形?三角形?或者其他形状?

08B6B4AB.png当然是可以的,接下来就要来聊聊不规则的裁剪。

要实现不规则裁剪,会用到很多 Canvas 相关的API的,其中最关键的就是 ctx.clip() ,它是裁剪的关键。

先来看看实现效果:

2024712-4.gif具体实现:

html

代码解读
复制代码
<!DOCTYPE html>
<html>
<body>
  <input id="file" type="file" />
  <canvas id="canvas"></canvas>
  <script>
    document.addEventListener('DOMContentLoaded', () => {
      const file = document.getElementById("file");
      const canvas = document.getElementById("canvas");
      const ctx = canvas.getContext("2d");
      const width = 600;
      const height = 400;
      const image = new Image();
      // 照片上传后绘制到canvas中展示
      file.addEventListener("change", (e) => {
        const target = e.target.files[0];
        const imgURL = URL.createObjectURL(target);
        image.src = imgURL;
        image.onload = () => {
          canvas.width = width;
          canvas.height = height;
          ctx.drawImage(image, 0, 0, width, height);
        }
      });

      // 裁剪路径集合
      const pathMap = [];
      // 裁剪中
      let cropping = false;
      
      // 监听canvas的鼠标按下事件
      canvas.addEventListener('mousedown', e => {
        cropping = true;
        pathMap.push({
          // 点和线的坐标不能一样
          offsetXPoint: e.offsetX,
          offsetYPoint: e.offsetY,
          offsetY: e.offsetY,
          offsetY: e.offsetY,
        });
        draw();
      });
      // 监听canvas的鼠标移动事件
      canvas.addEventListener('mousemove', (e) => {
        if (cropping) {
          // 拿最后一个点,根据鼠标移动不断改变偏移量
          pathMap[pathMap.length - 1].offsetX = e.offsetX;
          pathMap[pathMap.length - 1].offsetY = e.offsetY;
          // 标记是有效的点
          pathMap[pathMap.length - 1].effectived = true;
        }
      });
      // 监听canvas的鼠标双击事件
      canvas.addEventListener('dblclick', (e) => {
        cropping = false;
        if (!pathMap[pathMap.length - 1].effectived) {
          // 删除双击带来的多余点
          pathMap.pop();
        }
        // 裁剪照片
        setTimeout(async () => {
          // 清空原画布
          ctx.clearRect(0, 0, width, height);
          // 开始绘制
          ctx.beginPath();
          pathMap.forEach((item, index) => {
            if (index === 0) {
              ctx.moveTo(item.offsetXPoint, item.offsetYPoint);
            }
            ctx.lineTo(item.offsetX, item.offsetY);
          });
          // 裁剪目标区域
          ctx.clip();
          // 在裁剪的区域内绘制图片
          ctx.drawImage(image, 0, 0, width, height);
        }, 200);
      });

      /** @name 绘制 **/
      function draw() {
        // 先清空画布
        ctx.clearRect(0, 0, width, height);
        // 绘制照片
        ctx.drawImage(image, 0, 0, width, height);
        // 开始绘制
        ctx.beginPath();
        // 设置画笔的颜色
        ctx.strokeStyle = 'yellow';
        pathMap.forEach((item, index) => {
          if (index === 0) {
            // 端点位置
            ctx.moveTo(item.offsetXPoint, item.offsetYPoint);
          }
          ctx.lineTo(item.offsetX, item.offsetY);
        })
        // 将路径绘制到画布上
        ctx.stroke();
        // 通过requestAnimationFrame去不断执行绘制
        cropping && requestAnimationFrame(draw);
      }
    });
  </script>
</body>
</html>

案例代码不多,但需要你足够了解 Canvas 的知识才行,特别得多关注一下 ctx.moveTo 与 ctx.lineTo

这两个API可以瞅一瞅这个图:

image.png应该足够说明两者的作用了。

当然,这还没完?,不是说要裁剪个圆形吗?

其实当你理解上面的案例后,这个问题就是手到擒来了,比如:

javascript

代码解读
复制代码
canvas.addEventListener('mousedown', e => {
  // 圆形
  ctx.clearRect(0, 0, width, height);
  ctx.beginPath();
  ctx.arc(width/2, height/2, 100, 0, Math.PI * 2);
  ctx.clip();
  ctx.drawImage(image, 0, 0, width, height)
});

咱们稍微修改一下 mousedown 事件的逻辑,效果如下:

2024712-5.gif

作者:橙某人
链接:https://juejin.cn/post/7391160093565976616
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

零
  • 转载请务必保留本文链接:https://www.0s52.com/bcjc/javascriptjc/17147.html
    本社区资源仅供用于学习和交流,请勿用于商业用途
    未经允许不得进行转载/复制/分享

发表评论