当前位置: 首页>移动开发>正文

vue3 + element-plus 用 image-conversion 插件以及 canvas 在上传前对图片进行压缩处理

鑷姩涓婁紶

image-conversion NPM 鍦板潃

瀹夎鎻掍欢
npm i image-conversion --save 

鍒╃敤before-upload閽╁瓙鍑芥暟锛屽湪涓婁紶涔嬪墠鐢?code>image-conversion鎻掍欢鐨?compressAccurately 鏂规硶瀵瑰浘鐗囪繘琛屽帇缂╁鐞嗐€?/p>

vue3 + element-plus 用 image-conversion 插件以及 canvas 在上传前对图片进行压缩处理,第1张
<!--鍗曞浘涓婁紶缁勪欢/鎸夐挳-->
<template>
    <el-upload
      :action="uploadUrl"
      name="avatar"
      :multiple="false"
      :show-file-list="false"
      :before-upload="beforeUpload" 
      :on-success="handleUploadSuccess"
      :on-error="handleUploadError"
      with-credentials
    >
    </el-upload>
</template>
<script>
import { compress, compressAccurately } from 'image-conversion'
export default {
  setup(props, { emit }) {
    // ...鐪佺暐鍏朵粬
    const beforeUpload = file => {
      const typeList = ['image/jpeg', 'image/png', 'image/gif']
      const isTypeValid = typeList.includes(file.type)
      const isLt2M = file.size / 1024 / 1024 < 2
      if (!isTypeValid) {
        ElMessage.error('鍥剧墖鏍煎紡鍙兘鏄?JPG/PNG/GIF!')
      }
      if (!isLt2M) {
        ElMessage.error('鍥剧墖澶у皬涓嶈兘瓒呰繃 2MB!')
      }

      return new Promise((resolve, reject) => {
        if (file.size / 1024 > 200) { // 澶т簬 200 kb 灏卞帇缂?
          compressAccurately(file, 200).then(res => {
            // The res in the promise is a compressed Blob type (which can be treated as a File type) file;
            console.log(res)
            resolve(res)
          })
        } else if (isTypeValid && isLt2M) {
          // 鏃犻渶鍘嬬缉鐩存帴杩斿洖鍘熸枃浠?
          resolve(file)
        } else {
          reject()
        }
      })
    }
    return {
      beforeUpload
    }
  }
}
</script>

鎵嬪姩涓婁紶

鎵嬪姩涓婁紶闇€灏?el-upload 缁勪欢鐨?code>auto-upload灞炴€ц缃负 false锛岃繖鏍?code>before-upload閽╁瓙渚垮け鏁堜簡銆傝繖鏃跺彲浠ョ敤on-change閽╁瓙鍑芥暟浠f浛锛?/p>

vue3 + element-plus 用 image-conversion 插件以及 canvas 在上传前对图片进行压缩处理,第2张

鍥犱负鏈夋壒閲忎笂浼犵殑鎯呭喌锛屾敞鎰忚繖涓挬瀛愭槸浼犱簡鍑犱釜鏂囦欢灏辫Е鍙戝嚑娆$殑锛屾垜浠篃鍙互鍦ㄦ彁浜よ〃鍗曟椂鍐嶅幓瀵瑰浘鐗囨枃浠惰繘琛屽帇缂╁鐞嗐€?/p>

鍚屾椂 image-conversion 鏄紓姝ヨ繑鍥炵粨鏋滐紝鏃犳硶鎺ユ敹锛屾垜浠渶瑕佹崲涓€涓柟寮忥紝鍒╃敤 canvas 鐢诲竷瀵瑰浘鐗囪繘琛屽帇缂╋細
鍏舵牳蹇冨師鐞嗗氨鏄妸涓€寮犲ぇ鐨勫浘鐗囷紝鐩存帴鐢诲湪涓€寮犲皬灏忕殑鐢诲竷涓娿€傛鏃跺ぇ鍥剧墖灏卞彉鎴愪簡灏忓浘鐗囷紝鍘嬬缉灏辫繖涔堝疄鐜颁簡銆?/p>

canvas鐨?code>drawImage API锛欳anvasRenderingContext2D.drawImage()

ctx.drawImage(image, dx, dy);
ctx.drawImage(image, dx, dy, dWidth, dHeight);
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
vue3 + element-plus 用 image-conversion 插件以及 canvas 在上传前对图片进行压缩处理,第3张

image锛氬嵆闇€缁樺埗鍒颁笂涓嬫枃鐨勫浘鐗囧厓绱犮€傚厑璁镐换浣曠殑 canvas 鍥惧儚婧?CanvasImageSource)锛屽彲浠ユ槸椤甸潰涓婅幏鍙栫殑 DOM 瀵硅薄锛屼篃鍙互鏄?strong>铏氭嫙 DOM 涓殑鍥剧墖瀵硅薄 (鎴戜滑鐨勪笂浼犳搷浣滃氨鏄櫄鎷?DOM 瀵硅薄锛屽苟娌℃湁鐪熸缁樺埗鍒伴〉闈笂)銆?br> dx, dy, dWidth, dHeight锛?br> 琛ㄧず鍦?canvas 鐢诲竷涓婅鍒掑涓€鐗囧尯鍩熺敤鏉ユ斁缃浘鐗囷紝dx, dy鍒嗗埆涓虹洰鏍?canvas鍏冪礌鐨勫乏涓婅X 銆乊杞村潗鏍囧潗鏍囷紝dWidth, dHeight琛ㄧずcanvas鍏冪礌涓婄敤浜庢樉绀哄浘鐗囩殑鍖哄煙澶у皬锛屽厑璁稿缁樺埗鐨?image 杩涜缂╂斁銆?br> 濡傛灉娌℃湁鎸囧畾sx,sy,sWidth,sHeight杩?涓弬鏁帮紝鍒欏浘鐗囦細琚媺浼告垨缂╂斁鍦ㄨ繖鐗囧尯鍩熷唴銆?/p>

瀹為檯鎿嶄綔寰堢畝鍗曪紝姣斿闇€瑕佹妸涓€寮犲浘鐗囩殑灏哄缂╂斁涓?code>400*200澶у皬锛?/p>

const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 200; 
// 鏍稿績 js
ctx.drawImage(img, 0, 0, 400, 200)

閭e浣曟妸 canvas 鐢诲竷杞崲鎴?img 鍥惧儚鍛紝canvas鎻愪緵浜?涓浆鍥剧墖鐨勬柟娉曪細

  1. canvas.toDataURL(type, quality)
    API 鍦板潃锛欻TMLCanvasElement.toDataURL()
    鎶婂浘鐗囪浆鎹㈡垚 base64 鏍煎紡淇℃伅锛岀函瀛楃鐨勫浘鐗囪〃绀烘硶銆?/li>
  • type锛?鍥剧墖鏍煎紡锛岄粯璁mage/png
  • quality锛氬湪鎸囧畾鍥剧墖鏍煎紡涓?image/jpeg 鎴?image/webp鐨勬儏鍐典笅锛屽彲浠ュ湪 0 鍒?1 鐨勫尯闂撮€夋嫨鍥剧墖鐨勮川閲忋€傚鏋滆秴鍑哄彇鍊艰寖鍥达紝灏嗕細浣跨敤榛樿鍊?0.92銆?/li>
  1. toBlob(callback, type, quality)锛?br> API 鍦板潃锛欻TMLCanvasElement.toBlob()
    鎶?canvas 杞崲鎴?Blob鏂囦欢锛岄€氬父鐢ㄥ湪鏂囦欢涓婁紶涓紝鍥犱负鏄簩杩涘埗鐨勶紝瀵瑰悗绔洿鍔犲弸濂姐€?/li>
  • callback锛氬洖璋冨嚱鏁帮紝杞崲濂界殑 Blob 瀵硅薄銆傚鏋滃浘鍍忔湭琚垚鍔熷垱寤猴紝鍙兘浼氳幏寰?null 鍊笺€?/li>
  • type鍜?code>quality锛氬拰toDataURL()鐨勫弬鏁颁竴鑷?/li>

鍜?code>toDataURL()鏂规硶鐩告瘮锛?code>toBlob()鏂规硶鏄紓姝ョ殑锛屽洜姝ゅ浜嗕釜callback鍙傛暟銆傚紓姝ユ剰鍛崇潃涓嶅彲鎺ф€э紝閭d箞濡傛灉鎴戜滑闇€瑕佺殑涓婁紶鏍煎紡鏄?code>Blob锛屽彲浠ラ€氳繃鍏堣浆鎴?code>dataURL锛屽啀鎶?code>dataURL杞垚Blob鐨勫鐞嗘柟寮忋€?/p>

鐜板湪鏉ョ湅鐪嬪叿浣撲唬鐮佹€庝箞瀹炵幇鈥滃浘鐗団啋canvas鍘嬬缉鈫掑浘鐗団€?/strong>杩欎釜杩囩▼锛?/h5>

绠€鍗曟暣鐞嗕笅鎬濊矾锛?/p>

  1. 棣栧厛鎷垮埌鍥剧墖璧勬簮锛岀敤 FileReader 鍘昏鍙栵紝璇荤殑杩囩▼涓敤readAsDataURL鏂规硶鎶?File 杞垚dataURL锛坆ase64缂栫爜瀛楃涓诧級
  2. 璇诲彇鎴愬姛瑙﹀彂 reader 鐨?load 浜嬩欢锛屾鏃舵妸dataURL璧嬪€肩粰鍒涘缓鐨勭┖new Image()鐨?code>src
  3. image 寮€濮嬪姞杞斤紝瑙﹀彂 image 鐨?onload 浜嬩欢锛屽湪姝ゅ杩涜鍘嬬缉锛屽啀灏嗗帇缂╁畬鐨勫€艰繑鍥炶繃鏉ャ€?/li>
  4. 鍏蜂綋鍘嬬缉杩囩▼鏄敤 canvas 鍦ㄨ櫄鎷?DOM 涓粡杩囩畻娉曟寜姣斾緥缁樺埗鍥剧墖锛岀劧鍚?br> 灏嗙粯鍒跺畬鐨?canvas 杞垚 dataURL锛屽啀鎶?dataURL 杞垚 Blob
  5. 鎺ョ潃鎶? Blob append 鍒?FormData 瀹炰緥瀵硅薄涓婏紙濡傛湁闇€瑕侊紝鍙互鍦ㄦ嬁鍒板埌鍘嬬缉瀹岀殑Blob 缁撴灉鍚庯紝鍐嶅皢鍏惰浆鎹负 File锛?/li>

涓嬮潰鎶婂叿浣撲唬鐮佽创涓€涓嬶紙鐪佺暐鏃犲叧閮ㄥ垎锛夛細
馃憞
灏佽鐨勫帇缂╂柟娉昷s锛?/p>

// `src/utils/compress.js`
export function dataURLToBlob(dataUrl) {
  let arr = dataUrl.split(',')
  let mime = arr[0].match(/:(.*?);/)[1]
  let bytes = window.atob(arr[1]) // 鍘绘帀url鐨勫ご锛屽苟杞崲涓篵yte
  var n = bytes.length
  var u8arr = new Uint8Array(n)
  while (n--) { // 灏嗗浘鍍忔暟鎹€愬瓧鑺傛斁鍏int8Array涓?
    u8arr[n] = bytes.charCodeAt(n)
  }
  return new Blob([u8arr], { type: mime })
}

export const compress = (img, option, Orientation) => {
  const options = option || {}
  const { width, height, quality } = options
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')

  let initSize = img.src.length
  let w = width || img.width
  let h = height || img.height
  // 濡傛灉鍥剧墖澶т簬鍥涚櫨涓囧儚绱狅紝璁$畻鍘嬬缉姣斿苟灏嗗ぇ灏忓帇鑷?00涓囦互涓?
  let ratio
  if ((ratio = (w * h) / 4000000) > 1) {
    ratio = Math.sqrt(ratio)
    w /= ratio
    h /= ratio
  } else {
    ratio = 1
  }

  canvas.width = w
  canvas.height = h
  ctx.fillStyle = '#fff'
  ctx.fillRect(0, 0, w, h)

  // 鐡︾墖canvas
  const tCanvas = document.createElement('canvas')
  const tctx = tCanvas.getContext('2d')

  let count
  if ((count = (w * h) / 1000000) > 1) {
    // 濡傛灉鍥剧墖鍍忕礌澶т簬100涓囧垯浣跨敤鐡︾墖缁樺埗
    console.log('瓒呰繃100W鍍忕礌')
    count = ~~(Math.sqrt(count) + 1) //璁$畻瑕佸垎鎴愬灏戝潡鐡︾墖
    // 璁$畻姣忓潡鐡︾墖鐨勫鍜岄珮
    let nw = ~~(w / count)
    let nh = ~~(h / count)
    tCanvas.width = nw
    tCanvas.height = nh
    for (let i = 0; i < count; i++) {
      for (let j = 0; j < count; j++) {
        tctx.drawImage(
          img,
          i * nw * ratio,
          j * nh * ratio,
          nw * ratio,
          nh * ratio,
          0,
          0,
          nw,
          nh
        )
        ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
      }
    }
  } else {
    ctx.drawImage(img, 0, 0, w, h)
  }
  //淇 ios涓婁紶鍥剧墖鐨勬椂鍊?琚棆杞殑闂
  if (Orientation != '' && Orientation != 1) {
    switch (Orientation) {
      case 6: //闇€瑕侀『鏃堕拡锛堝悜宸︼級90搴︽棆杞?
        rotateImg(img, 'left', canvas)
        break
      case 8: //闇€瑕侀€嗘椂閽堬紙鍚戝彸锛?0搴︽棆杞?
        rotateImg(img, 'right', canvas)
        break
      case 3: //闇€瑕?80搴︽棆杞?
        rotateImg(img, 'right', canvas) //杞袱娆?
        rotateImg(img, 'right', canvas)
    }
  }
  // 杩涜鏈€灏忓帇缂?
  // canvas.toDataURL(type, encoderOptions);
  // type锛?鍥剧墖鏍煎紡 image/png
  // encoderOptions锛氬湪鎸囧畾鍥剧墖鏍煎紡涓?image/jpeg 鎴?image/webp鐨勬儏鍐典笅锛屽彲浠ュ湪 0 鍒?1 鐨勫尯闂撮€夋嫨鍥剧墖鐨勮川閲忋€?
  // 濡傛灉瓒呭嚭鍙栧€艰寖鍥达紝灏嗕細浣跨敤榛樿鍊?0.92銆?
  let newImg = canvas.toDataURL('image/jpeg', quality || 0.3)
  console.log('鍘嬬缉鍓嶏細' + initSize)
  console.log('鍘嬬缉鍚庯細' + newImg.length)
  console.log('ndata:' + newImg)

  console.log(
    '鍘嬬缉鐜囷細' + ~~((100 * (initSize - newImg.length)) / initSize) + '%'
  )
  // return new File([blob], file.name, { type: file.mime }) // 濡傛灉鏄繑鍥?File
  // 榛樿杩斿洖 Blob 鏍煎紡锛屽吋瀹规€ц緝濂?
  // newImg = base64UrlToBlob(newImg)
  newImg = dataURLToBlob(newImg)

  tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
  return newImg
}

/** 鍥剧墖鍘嬬缉锛岄粯璁や负鍚屾瘮渚嬪帇缂?
 *  @param {file} 鎺ュ彈鏂囦欢鏁扮粍鎴栧崟涓枃浠?鍥剧墖瀵硅薄)
 *  @param {option} 鎸囧畾鍘嬬缉鐨勯厤缃弬鏁帮紝 width锝?height锝?quality
 *  鍥炶皟鍑芥暟 callback 鍙傛暟涓?base64 瀛楃涓插浘鐗囨暟鎹暟缁?
 */
export function compressFile(file, option, callback) {
  const reader = new FileReader()
  const image = new Image()

  // 寮傛璇诲彇浼犲叆鐨?Blob 鎴?File 瀵硅薄锛屻€愯鍙栨垚鍔熴€戜細瑙﹀彂 reader 鐨?load 浜嬩欢
  reader.readAsDataURL(file)

  // 璇诲彇鍒板浘鐗囨暟鎹繚瀛樺湪 event.target 鐨?result灞炴€т腑
  reader.onload = function (event) {
    // 鏁版嵁鏍煎紡鏄?data:URL瀛楃涓诧紙base64缂栫爜锛?
    image.src = event.target.result
  }

   return new Promise((resolve, reject) => {
    image.onload = () => {
      resolve(compress(image, option))
    }
  })
}

鎵归噺涓婁紶缁勪欢锛?/p>

<!--鎵归噺涓婁紶缁勪欢-->
<template>
    <el-upload
      :action="`${apiUrl}/admin/upload`"
      name="photoSrc"
      with-credentials
      :show-file-list="true"
      :file-list="photoList"
      :on-change="photoListChange"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :auto-upload="false"
      :drag="drag"
      multiple
      :class="uploadClass"
    >
      <template v-if="!drag" #trigger>
        <el-button size="small" type="primary">閫夊彇鏂囦欢</el-button>
      </template>
      <template v-if="drag">
        <i class="el-icon-upload" />
        <div class="el-upload__text">
          灏嗗浘鐗囨嫋鍒版澶勶紝<br />鎴?lt;em>鐐瑰嚮涓婁紶</em>
        </div>
      </template>
   </el-upload>
</template>

<script lang="ts">
import { compressFile } from '@/utils/compress'

export default defineComponent({
  name: 'UploadMulti',
  setup(props) {
    const photoList = ref([])
    // 褰撴枃浠跺垪琛ㄦ湁鏀瑰彉鏃惰Е鍙戯紝娣诲姞鏂囦欢銆佷笂浼犳垚鍔熷拰涓婁紶澶辫触鏃堕兘浼氳璋冪敤銆?
    // 鎴戜滑閰嶇疆浜嗘墜鍔ㄤ笂浼狅紝鍥犳鍙湁娣诲姞鍥剧墖鏃惰Е鍙?
    const photoListChange = (file: Object) => {
      const typeList = ['image/jpeg', 'image/png', 'image/gif']
      const isTypeValid = typeList.includes(file.raw.type)
      const isLt2M = file.size / 1024 / 1024 < 2
      if (!isTypeValid) return ElMessage.error('鍥剧墖鏍煎紡鍙兘鏄?JPG/PNG/GIF!')
      if (!isLt2M) return ElMessage.error('鍥剧墖澶у皬涓嶈兘瓒呰繃 2MB!')
      // 鍘嬬缉澶勭悊
      compressFile(file.raw).then(res => {
        const newFile = new File([res], file.name, { type: file.raw.type })
        file.blobUrl = res
        file.raw = Object.assign({}, newFile)
        photoList.value.push(file)
      })
      // photoList.value.push(file)
    }
    // 浼犲叆 true 鑾峰彇銆愬凡涓婁紶銆戞枃浠讹紝鍚﹀垯鏄幏鍙栥€愭湭涓婁紶銆戞枃浠?
    // 鐢ㄦ槸鍚︽嫢鏈?raw 灞炴€?鏉ュ垽鏂紝鏈?raw property 灏辨槸寰呬笂浼犵殑
    const filterPhotoList = (isUploaded?: Boolean) => {
      return isUploaded
        photoList.value.filter(
            item => !Object.prototype.hasOwnProperty.call(item, 'raw')
          )
        : photoList.value.filter(item =>
            Object.prototype.hasOwnProperty.call(item, 'raw')
          )
    }
    // 琛ㄥ崟鎻愪氦鏃跺厛瑙﹀彂杩欎釜鏂规硶鐢ㄦ潵鑾峰彇鏂囦欢鍒楄〃锛屽鏈夋湭涓婁紶鐨勫氨鍦ㄨ繖鏃朵笂浼?
    const getUploadedList = async () => {
      // 鑾峰彇寰呬笂浼犵殑鏂囦欢鍒楄〃锛屽鏋滄病鏈夊氨鐩存帴杩斿洖 photoList 鐨勫€?
      const toUploadList = filterPhotoList()
      if (toUploadList.length === 0) return photoList.value

      // 鏈夊垯寮€濮嬫墜鍔ㄤ笂浼?
      const formData = new FormData()
      // 鍘嬬缉澶勭悊涔熷彲浠ヨ€冭檻鏀捐繖閲?
      // for (let file of toUploadList) {
      //   const res = await compressFile(file)
      //  ...
      //   formData.append('photoSrc', file.blobUrl, file.name)
      // }
      toUploadList.forEach(file => {
        // 濡傛灉鏄?Blob
        formData.append('photoSrc', file.blobUrl, file.name)
        // formData.append('photoSrc', file.raw, file.name)
      })
      // 鎵ц涓婁紶锛寀ploadMulti 鏄垜鐨勬壒閲忎笂浼犳帴鍙?
      const { data } = await uploadMulti(formData)
      // 瀵硅繑鍥炵殑鍥剧墖淇℃伅鍋氫簺澶勭悊锛屽啀缁欒〃鍗曞幓缁х画 Post
      return filterPhotoList(true).concat(uploadMultiSuccess(data))
    }
    return {
      photoList,
      photoListChange,
      filterPhotoList,
      getUploadedList,
    }
  }
})
</script>

鍙傝€冿細
鍓嶇JS鍒╃敤canvas鐨刣rawImage()瀵瑰浘鐗囪繘琛屽帇缂?br> vue涓嬪疄鐜癷nput瀹炵幇鍥剧墖涓婁紶锛屽帇缂╋紝鎷兼帴浠ュ強鏃嬭浆


https://www.xamrdz.com/mobile/4fv1994278.html

相关文章: