tk挠痒痒网址发布页-秘密武器
历史搜索

从图片裁剪来聊聊前端二进制

游客2025-05-16 12:16:02
目录文章目录
  1. 紧接着一个不那么简单的需求
  2. FileReader

最开始的一个小需求

前两天项目中有个小需求:前端下载后台小哥返回的二进制流文件。

起初接到这个需求时,我感觉这很简单啊(虽然我不会,但可以百度啊,,,,)

从图片裁剪来聊聊前端二进制 1

整个需求分以下四步:

  1. 获取文件并读取文件。
  2. 获取裁剪坐标。
  3. 裁剪图片。
  4. 读取裁剪后的图片预览并上传。

获取文件并读取文件

首先来看下上面第一步提到的获取文件。对应就是给input绑定的handleChange事件:

handleChange = (event) => {
  let file = event.target.files[0];
  let fileReader = new FileReader();
  fileReader.onload = (event) => {
    this.setState({
      file,
      dataURL: event.target.result,
    });
    this.imageRef.current.onload = () => this.drawImage();
  };
  fileReader.readAsDataURL(file);
};

HTML5 支持从 input[type=file] 元素中直接获取文件信息,也可以读取文件内容。

这里就需要用到了 FileReader ,这个类是专门用来读取本地文件的。纯文本或者二进制都可以读取,但是本地文件必须是经过用户允许才能读取,也就是说用户要在input[type=file]中选择了这个文件,你才能读取到它。

通过 FileReader 我们可以将图片文件转化成 DataURL,就是以 data:image/png;base64开头的一种URL,然后可以直接放在 image.src 里,这样本地图片就显示出来了。

获取裁剪坐标

这里主要是mousedownmousemovemouseup事件的结合使用。

mousedown

鼠标按下事件。这里要记录下鼠标按下时的开始坐标,即startXstartY,同时要将标志位startDrag设为true,标识鼠标开始移动。

handleMouseDown = (event) => {
  this.setState({
    startX: event.clientX,
    startY: event.clientY,
    startDrag: true,
  });
};

mousemove

鼠标移动事件。判断startDragtrue(即鼠标开始移动),然后记录对应移动的距离。

handleMouseMove = (event) => {
  if (this.state.startDrag) {
    this.drawImage(
      event.clientX - this.state.startX + this.state.lastX,
      event.clientY - this.state.startY + this.state.lastY
    );
  }
};

mouseup

鼠标弹起事件。这里要记录下最终鼠标的落点坐标,对应就是lastXlastY

handleMouseUp = (event) => {
  this.setState({
    lastX: event.clientX - this.state.startX + this.state.lastX,
    lastY: event.clientY - this.state.startY + this.state.lastY,
    startDrag: false,
  });
};

裁剪图片

这个时候我们就需要用到canvas了,canvas和图片一样,所以新建canvas时就要确定其高宽。

将图片放置入canvas时需要调用drawImage

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

具体API使用参考MDN上的drawImage

drawImage = (left = this.state.lastX, top = this.state.lastY) => {
  let image = this.imageRef.current;
  let canvas = this.canvasRef.current;
  let ctx = canvas.getContext("2d");
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  let imageWidth = image.width;
  let imageHeight = image.height;
  if (imageWidth > imageHeight) {
    let scale = canvas.width / canvas.height;
    imageWidth = canvas.width * this.state.times;
    imageHeight = imageHeight * scale * this.state.times;
  } else {
    let scale = canvas.height / canvas.width;
    imageHeight = canvas.height * this.state.times;
    imageWidth = imageWidth * scale * this.state.times;
  }
  ctx.drawImage(
    image,
    (canvas.width - imageWidth) / 2 + left,
    (canvas.height - imageHeight) / 2 + top,
    imageWidth,
    imageHeight
  );
};

其中这里面我们还加入了scale,这个变量是用来实现图片放大缩小效果的。

而且会判断图片的宽、高的大小关系,从而实现图片在canvas中对应的适配。

读取裁剪后的图片并上传

这时我们要获取canvas中图片的信息,用toDataURL就可以转换成上面用到的DataURL

confirm = () => {
  let canvas = this.canvasRef.current;
  let ctx = canvas.getContext("2d");
  const imageData = ctx.getImageData(100, 100, 100, 100);
  let avatarCanvas = document.createElement("canvas");
  avatarCanvas.width = 100;
  avatarCanvas.height = 100;
  let avatarCtx = avatarCanvas.getContext("2d");
  avatarCtx.putImageData(imageData, 0, 0);
  let avatarDataUrl = avatarCanvas.toDataURL();
  this.setState({ avatarDataUrl });
  this.avatarRef.current.src = avatarDataUrl;
};

然后取出其中base64信息,再用window.atob转换成由二进制字符串。但window.atob转换后的结果仍然是字符串,直接给Blob还是会出错。所以又要用Uint8Array转换一下。

这时候裁剪后的文件就储存在blob里了,我们可以把它当作是普通文件一样,加入到FormData里,并上传至服务器了。

upload = (event) => {
  // console.log("文件 url", this.state.avatarDataUrl);
  let bytes = atob(this.state.avatarDataUrl.split(",")[1]);
  console.log("bytes", bytes);
  let arrayBuffer = new ArrayBuffer(bytes.length);
  let uInt8Array = new Uint8Array();
  for (let i = 0; i < bytes.length; i++) {
    uInt8Array[i] = bytes.charCodeAt[i];
  }
  let blob = new Blob([arrayBuffer], { type: "image/png" });
  let xhr = new XMLHttpRequest();
  let formData = new FormData();
  formData.append("avatar", blob);
  xhr.open("POST", "/upload", true);
  xhr.send(formData);
};

今天装逼到此结束,谢谢观看!

文章来源:CSDN

标签:ArrayBuffer

本文是由用户"游客"发布,所有内容的版权归原作者所有。没有经过书面许可,任何单位或个人不得以任何形式复制、转载、引用本网站的内容。否则将追究法律责任。

相关专题