圖片添加水印、文件轉圖片、圖片轉文件、html2canvas截屏功能

圖片添加水印、文件轉圖片、圖片轉文件、html2canvas截屏功能

工做中遇到了關於圖片的處理方法,作個記錄分享給小夥伴們。javascript

1、圖片添加水印(包括圖片壓縮)

作法:html

  • 將圖片轉爲 canvas
  • 對圖片進行內容填充
  • 將圖片轉化成 base64 的格式

下面先提供代碼,並對代碼作進一步的講解:html5

picWaterMark = ({ // 1
  url = '',
  textAlign = 'left',
  font = "30px Microsoft Yahei",
  fillStyle = 'rgba(255, 255, 255, 0.8)',
  content = '請勿外傳',
  callback = null,
} = {}) => {
  const img = new Image(); // 2
  img.src = url;
  img.crossOrigin = 'anonymous';  // 3
  img.onload = function () {
    const canvas = document.createElement('canvas');
    canvas.width = img.width;  // 4
    canvas.height = img.height;

    const ctx = canvas.getContext('2d');

    ctx.drawImage(img, 0, 0, imageWidth, imageHeight);
    ctx.textAlign = textAlign;  // 5
    ctx.font = font;
    ctx.fillStyle = fillStyle;
    ctx.fillText(content, 10, 20);

    let base64Url = '';
    base64Url = canvas.toDataURL("image/jpeg", 0.5); // 6
    callback && callback(base64Url); // 7
  }
}
複製代碼

代碼中註釋的數字,是我下文要說明的內容哈。java

一、傳參

傳參我這裏定義成一個對象,小夥伴能夠自行定義哈,對於上面的傳參值,小夥伴們也能夠直接寫死在代碼中。ios

  • url: 做爲圖片的內容,這個應該是必傳項,傳入 base64 格式的圖片或者圖片的 url
  • callBack: 圖片添加完水印後的回調事件,這個也應該是必傳項。

二、建立圖片對象

建立一個圖片對象用來存放咱們要改造的圖片。canvas

三、crossOrigin

解決跨域問題。跨域

當在 canvas 中繪製一張外鏈圖片時,咱們會遇到一個跨域問題。打開瀏覽器調試會發現如下錯誤:瀏覽器

img.html:23 Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.安全

這是受限於 CORS 策略,會存在跨域問題,雖然可使用圖像,可是繪製到畫布上會污染畫布,一旦畫布被污染,就沒法提取畫布上的數據。服務器

好比沒法使用使用畫布 toBlob(),toDataURL(),或 getImageData() 方法;當使用這些方法的時候 會拋出上面的安全錯誤。

加上第三點的代碼就能解決這個問題。

四、設置畫布大小

這裏若是將原圖的長寬縮小,是能夠對圖片進行壓縮處理的。

五、水印內容

打開這裏,能夠查看到比較全的 Canvas 參考手冊

有了這份參考手冊和上面的代碼作參考,對於自定義水印內容應該是沒有難度了。

六、提取畫布數據

將畫布數據提取出來,toDataURL 的第二個參數是對圖片進行壓縮,是個選填值,小夥伴能夠根據須要自行定義。

七、callBack

生成新的圖像數據後進行接下來的操做。

2、文件轉圖片

若是咱們使用的 input, 設置 type='file',進行圖片上傳,須要將文件數據轉化成 base64 的形式。

const Page: FC = () => {

  // 將圖片轉化爲 base64 的格式
  const transformFileToDataUrl = (file) => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = e => {
        const { result } = e.target;
        resolve({
          result,
        });
      };
    });
  }

  const fileChange = files => {
    const file = files.target.files[0];
    transformFileToDataUrl(file).then(data => {
      console.log(data.result);
    })
  };

  return (
    <input type='file' onChange={fileChange} /> ) } 複製代碼

html5 + canvas 進行移動端手機照片上傳時,發現ios手機上傳豎拍照片會逆時針旋轉90度,橫拍照片無此問題;Android手機沒這個問題。

所以解決這個問題的思路是:獲取到照片拍攝的方向角,對非橫拍的ios照片進行角度旋轉修正。

利用 exif.js 讀取照片的拍攝信息。可到官網 查看詳細文檔。

這裏主要用到 Orientation 屬性。

Orientation 屬性說明以下:

旋轉角度 參數
1
順時針90° 6
逆時針90° 8
180° 3

根據旋轉角度進行修正。

修改上述 transformFileToDataUrl() 的方法,以下:

// 將圖片轉化爲 base64 的格式
const transformFileToDataUrl = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = e => {
      const { result } = e.target;

      EXIF.getData(file, function () {
        EXIF.getAllTags(this);
        const orientation = EXIF.getTag(this, "Orientation");
        resolve({
          result,
          orientation
        });
      });
    };
  });
}
複製代碼

3、圖片轉文件

export const dataURLtoFile = (dataurl, filename) => {
  var arr = dataurl.split(","),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, { type: mime });
}
複製代碼

4、壓縮圖片

如下代碼爲網絡上 copy 過來,侵刪。

入參先寫在前頭:

  • data: 這裏的 data 是個對象,裏面放兩個字段,dataUrl: base64 圖片數據格式,orientation: 可能須要用到的旋轉字段,若是不須要設置固定值爲 1 便可。
  • callback: 回掉函數
  • compressionRatio: 壓縮比例,默認值爲20
  • compress: 是否開啓壓縮,默認值爲 true
  • cross: 是否設置開啓跨域,建議設置爲 true
export const compress = (
  data,  // { dataUrl: '', orientation: 默認值爲1 }
  callback,
  compressionRatio = 20,
  compress = true,
  cross = false,
) => {
  /** * 壓縮圖片 * @param data file文件 數據會一直向下傳遞 * @param callback 下一步回調 * @compressionRatio 壓縮比例 * @compress 是否壓縮 */
  // const imgCompassMaxSize = 200 * 1024; // 超過 200k 就壓縮
  // const imgFile = data.file;
  const orientation = data.orientation || 1;
  const img = new window.Image();
  img.src = data.dataUrl;

  if (cross) {
    // img.setAttribute("crossOrigin", 'Anonymous')
    img.crossOrigin = "*";
  }

  img.onload = function () {
    let drawWidth, drawHeight, width, height;

    drawWidth = this.naturalWidth;
    drawHeight = this.naturalHeight;

    // 改變一下圖片大小
    let maxSide = Math.max(drawWidth, drawHeight);

    if (maxSide > 1024) {
      let minSide = Math.min(drawWidth, drawHeight);
      minSide = (minSide / maxSide) * 1024;
      maxSide = 1024;
      if (drawWidth > drawHeight) {
        drawWidth = maxSide;
        drawHeight = minSide;
      } else {
        drawWidth = minSide;
        drawHeight = maxSide;
      }
    }

    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");

    canvas.width = width = drawWidth;
    canvas.height = height = drawHeight;
    // 判斷圖片方向,重置 canvas 大小,肯定旋轉角度,iphone 默認的是 home 鍵在右方的橫屏拍攝方式

    switch (orientation) {
      // 1 不須要旋轉
      case 1: {
        // ctx.drawImage(img, 0, 0, drawWidth, drawHeight);
        ctx.clearRect(0, 0, width, height);
        ctx.drawImage(img, 0, 0, width, height);
        break;
      }
      // iphone 橫屏拍攝,此時 home 鍵在左側 旋轉180度
      case 3: {
        ctx.clearRect(0, 0, width, height);
        ctx.translate(0, 0);
        ctx.rotate(Math.PI);
        ctx.drawImage(img, -width, -height, width, height);
        break;
      }
      // iphone 豎屏拍攝,此時 home 鍵在下方(正常拿手機的方向) 旋轉90度
      case 6: {
        canvas.width = height;
        canvas.height = width;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.translate(0, 0);
        ctx.rotate((90 * Math.PI) / 180);
        ctx.drawImage(img, 0, -height, width, height);
        break;
      }
      // iphone 豎屏拍攝,此時 home 鍵在上方 旋轉270度
      case 8: {
        canvas.width = height;
        canvas.height = width;
        ctx.clearRect(0, 0, width, height);
        ctx.translate(0, 0);
        ctx.rotate((-90 * Math.PI) / 180);
        ctx.drawImage(img, -width, 0, width, height);
        break;
      }
      default: {
        ctx.clearRect(0, 0, width, height);
        ctx.drawImage(img, 0, 0, width, height);
        break;
      }
    }

    let compressedDataUrl;
    if (compress) {
      compressedDataUrl = canvas.toDataURL(
        "image/jpeg",
        compressionRatio / 100
      );
    } else {
      compressedDataUrl = canvas.toDataURL("image/jpeg");
    }
    data.compressedDataUrl = compressedDataUrl;
    delete data.orientation;
    callback(compressedDataUrl, data);
  };
};
複製代碼

5、html2canvas截屏功能,以及遇到的問題

//兩個參數:所須要截圖的元素id,截圖後要執行的函數, canvas爲截圖後返回的最後一個canvas
 html2canvas(document.getElementById('id')).then(function(canvas) {document.body.appendChild(canvas);});
複製代碼

API

html2canvas 能夠設置一些參數,入參請看官網

FAQ

一、截屏失效

若是是在移動端進行截屏嘗試的話,可能會存在大屏手機沒法截屏的狀況。

這是由於 canvas 有面積大小的限制,因此建議減小 canvas 的高度來保證 html2canvas 的正常使用。

二、預覽失效問題

舉個例子,在釘釘微應用的開發中,官方有提供一個圖片預覽的方法。

截屏出來的 base64 數據在 ios 上可以正常的展現,可是在安卓機子上確失效。

建議:base64 的數據先上傳到服務器上,用返回回來的 url 進行圖片預覽來達到目的。

相關文章
相關標籤/搜索