關於前端上傳文件全面基礎掃盲貼(六) ----- 圖片上傳,旋轉,重繪,預覽等實戰(附DEMO)

系列文章

關於前端上傳文件全面基礎掃盲貼(零)
關於前端上傳文件全面基礎掃盲貼(一) ----- XMLHttpRequest
關於前端上傳文件全面基礎掃盲貼(二) ----- File
關於前端上傳文件全面基礎掃盲貼(三) ----- FormData
關於前端上傳文件全面基礎掃盲貼(四) ----- FileReader
關於前端上傳文件全面基礎掃盲貼(五) ----- H5拖拽事件
關於前端上傳文件全面基礎掃盲貼(六) ----- 圖片上傳,旋轉,重繪,預覽等實戰(附DEMO)javascript

圖片上傳,旋轉,重繪,預覽等實戰

距離我上次寫的系列文章好像都過了半年了,廢話很少說,先來展現一下技術思路html

1、獲取用戶照片數據前端

  1. 獲取用戶攝像頭圖片,不是全部手機支持,而且部分手機會有旋轉角度的問題;
  2. Input控件獲取照片文件。

2、編輯合成照片java

  1. 使用canvas編輯壓縮,重設尺寸比例;
  2. 轉成base64輸出預覽。

3、保存並上傳照片
提交base64數據到服務器須要服務器支持,我跳過了。node

基本結構

由於通常手機像素都很高,因此把原始圖片尺寸限制在300裏;git

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="format-detection" content="telphone=no, email=no">
    <meta name="renderer" content="webkit">
    <meta name="HandheldFriendly" content="true">
    <meta name="screen-orientation" content="portrait">
    <meta name="x5-orientation" content="portrait">
    <title></title>
    <style media="screen">
      * {
        margin: 0;
        padding: 0;
      }
      img {
        display: block;
      }
    </style>
  </head>

  <body>
    <div class='upload'>
      <input type="file" id='file' capture="camera" accept="image/*">
      <label id="upload1"></label>

      <p>原始圖片</p>
      <img class="old" width="300">

      <p>修改圖片</p>
      <img class="new">
    </div>

    <script type="text/javascript">
      window.onload = function () {
        var dom = document.getElementById('file');
        dom.addEventListener('change', function (e) {});
      }
    </script>
  </body>

</html>

想要上傳圖片,就要用type="file"得到文件信息,其中下面幾個重要屬性,(在pc瀏覽器打開文件要好久,因此能夠選擇性屏蔽)github

屬性 描述
accept 表示能夠選擇的文件MIME類型,多個MIME類型用英文逗號分開
multiple 是否能夠選擇多個文件,多個文件時其value值爲第一個文件的虛擬路徑
capture 表示,能夠捕獲到系統默認的設備,好比:camera--照相機;camcorder--攝像機;microphone--錄音
<input type="file" id='file' capture="camera" accept="image/*">

經過監聽input變化觸發方法web

window.onload = function () {
  var dom = document.getElementById('file');
  dom.addEventListener('change', function (e) {});
}

Exif.js

Exif.js 提供了 JavaScript 讀取圖像的原始數據的功能擴展,例如:拍照方向、相機設備型號、拍攝時間、ISO 感光度、GPS 地理位置等數據。
EXIF 數據主要來自拍攝的照片,多用於移動端開發,PC 端也會用到,此插件兼容主流瀏覽器,IE10 如下不支持。npm

<script src="https://cdn.jsdelivr.net/npm/exif-js"></script>

爲了避免讓用戶等待過久,咱們能夠在一系列操做以前先展現獲取的圖片,能夠直接從本地先讀取出原始數據,而後在頁面上展現出來,下面用到的知識點以前已經寫過就再也不重複了,能夠前往瀏覽文章
關於前端上傳文件全面基礎掃盲貼(二) ----- File
關於前端上傳文件全面基礎掃盲貼(四) ----- FileReadercanvas

var file = e.target.files[0],
  //旋轉角度
  orientation = null,
  fReader = new FileReader();

//限制大小格式
if (!filtration(file)) {
  return false;
}

上面的filtration(file)是我抽離出去的過濾函數

filtration: function(file, extend) {
  var extend = extend || {},
    settings = {
      size: extend.size || 5 * 1024 * 1024,
      reg: extend.reg || /image\/\w+/
    };

  if (file.size > settings.size || !settings.reg.test(file.type)) {
    alert(extend.msg || ('上傳格式非圖片類型或上傳圖片超過' + settings.size / 1024 / 1024 + 'M'))
    return false;
  }
  return true;
},

接下來要獲取圖片信息了,解決部分手機會有旋轉角度的問題的前提(可能連接格式問題,生成文章以後沒帶連接,只能本身複製粘貼訪問地址了)
中文文檔: http://code.ciaoca.com/javasc...
Demo: http://code.ciaoca.com/javasc...

EXIF.getData(file, function () {
  orientation = EXIF.getTag(this, 'Orientation');
  console.log('旋轉角度: ', orientation);
});

咱們已經拿到原始數據,result屬性中將包含一個data: URL格式的字符串以表示所讀取文件的內容.而後新建img對象加載資源

//轉碼完成
fReader.onload = function (e) {
  //加載圖片
  var img = new Image();
  img.src = e.target.result;
  img.onload = function () { }
}

//執行
fReader.readAsDataURL(file);

接着咱們使用canvas畫布從新設定尺寸重繪達到壓縮的功能,我只用到基礎使用方法,詳情(Canvas API)

var canvas = document.createElement('canvas'),
  ctx = canvas.getContext('2d'),
  nSize = resetSize(this, 300, 300, 1, 600);

canvas.width = nSize.width;
canvas.height = nSize.height;
ctx.drawImage(this, 0, 0, nSize.width, nSize.height);

上面的resetSize是我自定義的一個方法,能夠設置高寬比例最大範圍值,可能有點繁瑣,由於我想控制的功能比較多,中間容易發生衝突,我暫時是後面設置能覆蓋前面設置(注意:這裏的this是上文的data: URL格式)

resetSize: function(img, width, height, ratio, max) {
  var w = img.naturalWidth,
    h = img.naturalHeight,
    width = width || w,
    height = height || h;

  console.log('圖片寬高: ', w, h, '入參: ', width, height, ratio, max);

  if (w != width) w = width;
  if (h != height) h = height;
  if (ratio && w / h != ratio) w = h * ratio;

  //限制最大值範圍
  if (max) {
    if (w > max && w >= h) {
      h = Math.ceil(h * max / w);
      w = max;
    } else if (h > max && h >= w) {
      w = Math.ceil(w * max / h);
      h = max;
    }
  }
  console.log('重設寬高: ', w, h);
  return { width: w, height: h }
},

接下來就是比較複雜的圖形操做了,只要掌握了中心點位置就還好理解,詳情(HTML 5 Canvas 參考手冊)
上面拿到的orientation不是真的旋轉角度,只是給一個值表明拍攝方向,
最後返回新圖片展現的 data URI,這裏水比較深,我測試了jpg和png是沒問題,可是gif什麼的就輸出一個靜態圖,詳情(toDataURL)

var canvas = document.createElement('canvas'),
  ctx = canvas.getContext('2d'),
  nSize = picProcess.resetSize(this, 600, 400);

switch (+ orientation) {
  case 3:
    canvas.width = nSize.width;
    canvas.height = nSize.height;
    ctx.rotate(180 * Math.PI / 180);
    ctx.drawImage(this, -nSize.width, -nSize.height, nSize.width, nSize.height);
    break;
  case 6:
    canvas.width = nSize.height;
    canvas.height = nSize.width;
    ctx.rotate(90 * Math.PI / 180);
    ctx.drawImage(this, 0, -nSize.height, nSize.width, nSize.height);
    break;
  case 8:
    canvas.width = nSize.height;
    canvas.height = nSize.width;
    ctx.rotate(270 * Math.PI / 180);
    ctx.drawImage(this, -nSize.width, 0, nSize.width, nSize.height);
    break;
  default:
    canvas.width = nSize.width;
    canvas.height = nSize.height;
    ctx.drawImage(this, 0, 0, nSize.width, nSize.height);
    break;
}

return canvas.toDataURL(file.type, 0.8);

如今回看上面,咱們已經實現了獲取圖片,重繪圖片,讀取本地圖片,剩下就是預覽了,我簡單實現一個支持id和class的方法,也能判斷出img和div智能設置展現方式

preview: function(selector, url) {
  var isId = /^#\w+/ig.test(selector),
    isClass = /^\.\w+/ig.test(selector),
    name = selector.slice(1),
    dom = null;

  //選擇器類型
  if (isId) {
    dom = document.getElementById(name);
  } else if (isClass) {
    dom = document.getElementsByClassName(name)[0];
  } else {
    alert('選擇器傳參不支持!');
    return false;
  }

  //判斷類型
  if (dom.nodeName == 'IMG') {
    dom.src = url;
  } else {
    dom.style.backgroundImage = 'url(' + url + ')';
  };
}

而後咱們在監聽方法裏調用就行了.爲了更加直觀的看到代碼效果,我簡單寫了一個demo版本,能夠直接下載本地運行,不依賴任何環境實戰Demo

你們且看且珍惜吧,我可能後續還會研究手勢旋轉處理之類更加複雜的東西,但也說很差,畢竟仍是太懶散了.循例仍是得加上一句,由於都沒有很深刻測試過,因此確定會有些隱藏的問題,歡迎你們指出最好還有答案咯

相關文章
相關標籤/搜索