基于canvas的前端图片真实旋转

对于前端图片的旋转,我们一般会利用css来旋转,但是css旋转有一些问题,比如旋转的图片外层有一个容器是通过图片的宽高撑开的,当图片旋转后,就会出现一个非常诡异的现象图片和容器的框分离了,而容器的宽高恰恰是未旋转前图片的宽高。

上述问题的产生是由于css的旋转是一个视图上的旋转,其占位还是没变的,为了解决上述问题,我们需要一张真实旋转的图片,这个时候有两种方案:

1、请后端提供接口,传入特定的旋转角度,得到旋转的图片。

2、前端通过一定的技术实现图片的真实旋转。

这里,通过前端的技术来实现图片的真实旋转,怎么做呢?在对大脑里前端技术搜索一遍之后发现我们可以利用canvas的特性来实现图片的真实旋转。

具体过程是先将图片的宽高对调,并设置为canvas的宽高,然后将旋转的图片绘上去,再导出为blob或者base64就得到旋转图片的地址了。

代码:

const convertRotateImg = (src, orientation) => {
  return new Promise((resolve, reject) => {
    fetchImage(src)
      .then((img) => {
        const { width, height } = img;
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const direction = ((orientation + 360) % 360) / 90;
        switch (direction) {
          case 1: {
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(90*Math.PI/180);
            ctx.drawImage(img, 0, -height);
            break;
          }
          case 2: {
            canvas.width = width;
            canvas.height = height;
            ctx.rotate(180*Math.PI/180);
            ctx.drawImage(img, -width, -height);
            break;
          }
          case 3: {
            canvas.width = height;
            canvas.height = width;
            ctx.rotate(270*Math.PI/180);
            ctx.drawImage(img, -width, 0);
            break;
          }
          case 0:
          case 4:
          default: {
            canvas.width = width;
            canvas.height = height;
            ctx.rotate(0);
            ctx.drawImage(img, 0, 0);
            break;
          }
        }
        toObjectUrl(canvas)
          .then(resolve, reject);
      }, reject);
    });
}

const fetchImage = (url) => {
  return new Promise((resolve, reject) => {

    if (RegExp(location.host + '|base64,').test(url)) {
      const im = new window.Image();
      im.onload = () => {
        resolve(im);
      };
      im.onerror = () => {
        reject();
      };
      im.src = url;
    } else {
      const xhr = createXHR();
      xhr.open('GET', url, true);
      xhr.responseType = 'blob';
      xhr.onload = () => {
        const im = new window.Image();

        im.onload = () => {
          resolve(im);
        };

        im.onerror = () => {
          reject();
        };
        im.src = window.URL.createObjectURL(xhr.response);
      };

      xhr.onerror = () => {
        reject();
      };

      xhr.send();
    }
  });
}

const toObjectUrl = (canvas) => {
  const promise = new Promise((resolve, reject) => {
    if (!canvas) {
      reject();
    } else {
      // chrome等现代浏览器
      if (canvas.toBlob) {
        canvas.toBlob((blob) => {
          resolve(URL.createObjectURL(blob));
        }, "image/png", 1);
      } else if (canvas.msToBlob) {
        // IE.
        const blob = canvas.msToBlob();
        resolve(URL.createObjectURL(blob));
      } else {
        const data = canvas.toDataURL('image/png', 1);
        resolve(data);
      }
    }
  });

  return promise;
};

这里需要注意canvas绘制过程中图片跨域的处理还有canvas转换成blob的兼容性问题。

  • 支付宝二维码 支付宝
  • 微信二维码 微信
相关文章