前端图片的预览和上传、加载及下载


对于前端开发而言,和图片打交道自然是家常便饭。曾经的项目开发之中,对图片的使用也经历过不少问题,cc最近准备延伸一下,全方位的探讨在前端的多种应用场景下的图片操作,从预览上传、加载到下载到本地,同时也会分析图片的处理方式并share一些和图片素材的自用网站。

一、图片的预览和上传

1. 图片的预览
图片的预览不只是根据后台接口返回的图片地址来显示图片,更多情况下,我们需要及时预览,就是在我们选择完图片之后就及时地显示在前台页面,而不是从api中向后台再次请求来的图片,这样的预览可以减少前后台的频繁交互,减少服务端无用的图片资源,简化我们的开发过程。
方案: 使用FileReader实现前端的及时显示

1)FileReader的简介:

FileReader是H5中新增的对象, 负责读取文件内容

实例化: var reader = new FileReader();

FileReader通过一系列异步接口,在主线程中访问本地文件,方法有:

readAsText() :按字符读取文本文件,返回文本字符串。默认UTF-8
readAsArrayBuffer(file): 按字节读取文件的内容,结果用ArrayBuffer对象表示
readAsBinaryString(file): 按字节读取任意类型的文件,返回二进制字符串,这个方法不是为了读文件,而是为了存储文件
readAsDataURL(file): 读取文件内容,结果用data:url的字符串形式表示,这段字符串的本质就是DataURLDataURL是一种将文件(这个文件一般就是指图像或者能够嵌入到文档的文件格式) 嵌入到文档的方案。DataURL是将资源转换为base64编码的字符串形式,并且将这些内容直接存储在url中 → 优化网站的加载速度和执行效率。
abort(): 终止文件读取操作

FileReader提供了一个完整的事件模型,用来识别捕获文件时的状态,事件有:

事件 调用描述
onabort 读取操作被中断时
onerror 读取操作发生错误时
onload 读取操作成功完成时
onloadend 读取操作完成时(不论成功/失败)
onloadatart 读取操作将要开始之前
onprogress 读取数据过程中周期性调用

2)图片的本地预览

①通过FileReader的readAsDataURL()方法,我们可以FileReader是H5中新增的对象, 负责读取文件内容,跳过后台,直接 将本地图片显示在页面上。
<input type="file" id="imgFile" />上传文件
<img id="file_img" height="200">预览图片

var inputFile = document.getElementById("imgFile")
inputFile.onchange = function(){
  var file = this.files[0]
  if(file){
    var reader = new FileReader();
    reader.readAsDataURL(file); // 将图片转化为base64格式
    reader.onload = function(){ // 成功读取文件
      document.getElementById("file_img").src = this.result;
    }
  }else{
    alert('图片预览失败')
  }
}

②通过window.URL.createObjectURL(blob/file),参数既可以是Blob对象,也可是File对象,这个方法可以根据传入的参数创建一个指向该擦书对象的URL,这个URL的生命仅存在于它被创建的这个文档中

function previewFile(){
  var inputFile = document.getElementById("imgFile")
  var file = inputFile.files[0]
  if(file.size > 1024*1024*2){ // 限制图像的大小
    alert('图片的大小不超过2MB!')
    return false
  }
  // 创建图片的url
  var imgUrl = window.URL.createObjectURL(file)
  var preview = document.getElementById("file_img")
  preview.setAttribute("src", imgUrl)
  // document.getElementById("file_img").src = imgUrl
  // 更改图片的url之后释放url
  preview.onload = function(){
    console.log('图片加载成功')
    URL.revokeObjectURL(imgUrl)
  }
}

注: 每次调用createObjectURL()方法的时候,都会创建新的URL对象,所以在我们不需要的时候,必须调用URL.revokeObjectURL()进行安全释放,为了保证更好的性能。

3)图片上传至服务器

主要有以下几种方式:

  • <input>标签中的file类型
    file-input具备多个属性:accept、capture、multiple、files
    示例:<input type="file" multiple id="imgFile" accept="image/*" />
let fileInput = document.getElementById('imgFile')
fileInput.onchange = function(e){
  let fileList = this.files // 列出每个所选的文件对象
  console.log(fileList)
}
  • FileReader对象读取文件并上传
    上面已经介绍过FileReader就不再过多赘述
  • 利用FormData对象上传文件
    FromData对象的使用:
    1)用一些键值来模拟一系列表单控件 name+value->queryString
    2)异步上传二进制文件
    主要使用append()方法:fromData.append(name,value,filename) , 其中Blob对象的默认filename是"blob",File对象的默认文件名就是文件名
let file = document.querySelector('#imgFile').files[0]
const formData = new formData()
formData.append('usePicture', file, 'img.jpg')
  • axios上传文件,并异步显示
    axios不仅可以做数据请求,也可以实现图片上传的功能,主要是设置header中的Content-Type
let config = {
  headers: {
    'Content-Type': 'nultipart/form-data'
  }
}
this.$axios
       .post('serverUrl', formData, config)
       .then(res => {
         console.log(res)
       })
       .catch(err => {
         console.log(err)
       })

axios 也可以使用onUploadProgress监听上传进度

  • 基于form表单和iframe自己实现Ajax请求
    form的target属性设置为iframe的name值,表示提交到url后返回的数据显示到iframe区域中
    设置form和iframe

    function loadIframe() {
      // 获取iframe内部的内容,并解析
      var str_json = $('#iframe_1').contents().find('body').text()
      var obj = JSON.parse(str_json)
      if(obj.status){
        var img = document.createElement('img')
        img.src = "/" + obj.path;
        $('#imgs').append(img);
      }
    }
    

二、图片的加载

除了正常的图片预览之外,还有不得不提的两种技术,预加载和懒加载

预加载: 提前加载图片,当用户需要查看时直接从本地缓存中渲染
懒加载: 一种按需加载的技术,主要目的是作为服务器前端的优化,减少请求数或者延迟请求数

技术本质:两者行为是相反的,一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力的作用,而预加载则会增加服务器端的压力。因此呢,我们主要使用的还是懒加载,它可以提升前端的性能,要隆重介绍下了。

(一) About Lazy Load
Lazy Load的实现思路

  1. 计算每张图片相对于屏幕顶端的距离offsetTop,如果图片还未出现就不需要加载,也就是如上图所示:图片到顶端距离 > 滚动条滚动的高度 + 浏览器可视高度, 只有当offsetTop <= document.body.scrollTop + window.innerHeight,图片才会加载
    2)运用API IntersectionObserver,去动态检测元素是否出现在屏幕内,jquery_lazyLoad也支持API

主要介绍懒加载的几种实现方式:

  1. 原生js实现LazyLoad
    根据上面1)中的原理:html中 <img class="pic" alt="加载中" data-src="./images/1.png">

在IE9及以上的浏览器,可以使用window.innerHeight获取;但是在低版本的IE的标准模式中,可以使用document.documentElement.clientHeight获取;因此关于viewHeight我们写代码时要考虑浏览器兼容的情况

const imgs = document.getElementByTagName('img') // 获取所有图片的标签
const viewHeight = window.innerHeight || document.documentElement.clientHeight // 获取可视区域的高度
const scrollTop = document.body.scrollTop || document.documentElement.scrollTop // 获取滚动条滑过的高度
let cnt = 0 // 记录当前统计到哪一张图片

function lazyLoad() {
  // 遍历每一张图片,计算该图片是否加载,是否赋值真实src
  imgs.map((imgItem)=>{
    if(imgItem < scrollTop + viewHeight){
      imgItem.src = imgItem.getAttribute('data-src') // 把真实的图片src赋值
    }
  })
}
// 写一个节流函数
function throttle(method, delay){
  let timer = null
  return function(){
    let args = arguments
    clearTimeout(timer)
    timer = setTimeout(function(){
      console.log(this)
      method.apply(this.args)
    },delay)
  }
}
lazyLoad()
window.onscroll = throttle(lazyload, 200)
  1. 运用IntersectionObserver动态检测元素
    检测是否会出现在屏幕内
// 获取观察器示例,changes是被观察的对象数组
var observer = new IntersectionObserver(fuction(changes){
  console.log(changes)
  changes.forEach(function(index, item){
    if(item.intersectionRatio > 0 && item.intersectionRatio < 1){
      item.target.src = item.target.dataset.src // target是被观察的目标元素,是一个DOM节点对象
    }
  })
})
function addObserver(){
  var listItems = document.querySelectorAll('.img-item')
  listItems.forEach(function(item){
    // 实例的observer方法可以指定观察哪个DOM节点
    observer.observe(item) // 开始观察,obsrver的参数是一个DOM节点对象
  })
}
addObserver()
  1. Vue-lazyload使用

mint-UI组件库中的LazyLoad组件就是完全引用的‘vue-lazyload’
使用示例:
html部分: <li v-for="imgItem in imgList"><img v-lazy="imgItem"></li>

Vue.use(Lazyload)
new Vue({
  el: #app,
  data: {
    imgList: ['imgurl1','imgurl2','imgurl3']
  }
})

更多关于Vue-lazyload源码和原理的解析点击👉Pick me up!~o( =∩ω∩= )m

4. jQuery-lazyload的使用
<img class="lazyload" data-original="ig/example.jpg" width="765" height="574">
<img class="lazyload" src="img/example.jpg" data-original="ig/example.jpg" width="765" height="574">
$('img.lazyload').lazyload();

更多详见源码:

(二) About PreLoad

虽然预先加载图片会增加服务器的压力,但必须承认的是预加载对于提升用户的体验非常有效,在访问页面时,可以自由自在体会到流畅的加载速度。尤其是对于一些主力军是图片的网站来说,使用预加载可以保证图片快速、无缝隙地发布,使用户有非凡的体验,增强网站的性能和可用性。

实现预加载的三种技术: 参考文章

  1. 使用CSS + JS 实现
    实际上,单纯使用css就可以实现预加载,非常的简单、高效

    #preload-01 { 
    background: url(./image-01.png) no-repeat -9999px -9999px; 
    }
    #preload-02 { 
    background: url(./image-02.png) no-repeat -9999px -9999px; 
    }
    #preload-03 { 
    background: url(./image-03.png) no-repeat -9999px -9999px; 
    }
    

    但是该方法下的加载时间却很长会和页面上的其他内容一起加载,增添整体页面的加载时间,仍有改进的余地

    结合JS改进来推迟预加载的时间,直至页面加载完毕,所以添加JS代码:

    var preLoader = function(){
      if(document.getElementById){
        // 获取类选择器的元素,并设置background属性
        document.getElementById("preload-01").style.background = "url(./image-01.png) no-repeat -9999px -9999px";
        document.getElementById("preload-02").style.background = "url(./image-02.png) no-repeat -9999px -9999px";
        document.getElementById("preload-03").style.background = "url(./image-03.png) no-repeat -9999px -9999px";
      }
    }
    function addLoadEvent(func){
      var old_onload = window.onload;
      if(typeof(window.onload) != 'function'){
        window.onload = func;
      }else{
        window.onload = function(){
          if(old_onload){
            old_onload()
          }
          // 延迟preLoader()的加载时间,直到页面加载完毕
          func()
        }
      }
    }
    addLoadEvent(preLoader)
    
  2. 使用JS实现

    即便是使用JS对CSS加载进行优化,但涉及CSS加载,在实际过程中即便看起来高效但还是会花费很多时间。而最好的选择怕不是使用纯JS代码了,并且不用考虑浏览器的兼容性。
    方案一: 适合大批量图片加载情况

    var images = new Array()
    function preload() {
      for(let i = 0; i < preload.arguments.length; i++){
        images[i] = new Image()
        images[i].src = preload.arguments[i]
      }
    }
    // 把需要加载的图片的url放在其中
    preload(
      "http://www.baidu.com/gallery/image1.jpg",
      "http://www.baidu.com/gallery/image2.jpg",
      "http://www.baidu.com/gallery/image3.jpg"
    )
    
    

    方案二: 适合少量图片加载情况

    function preLoader(){
      if(documents.images) {
        var img1 = new Image()
        var img2 = new Image()
        var img3 = new Image()
        img1.src = "path/to/image1.jpg"
        img2.src = "path/to/image1.jpg"
        img3.src = "path/to/image1.jpg"
      }
    }
    function addLoadEvent(func){
      // ...同上面的延迟加载功能函数
    }
    addLoadEvent(preLoader)
    
  3. 使用Ajax实现

    还有一种更秀的操作——使用Ajax实现预加载🆒。这种方法可以利用DOM,不仅可以预加载图片,还会预加载CSS、Javascript等相关的东西。比起直接用JS,它的闪光点在于JS和CSS的加载不会影响当前页面,更加简洁高效。

    window.onload = function(){
      setTimeout(function(){
        // XHR去请求js和css对象
        var xhr = new XMLHttpRequest()
        xhr.open('GET', 'http://domain.tld/preload.js')
        xhr.send('')
        xhr = new XMLHttpRequest()
        xhr.open('GET', 'http://domain.tld/preload.js')
        xhr.send('')
            new Image().src = "http://domain.tld/preload.png" // 预加载图片
      }, 1000); // 1s超时,防止脚本挂起
    }
    

三、图片的下载

除了后台直接给url点开就可以下载的方式,前端实现图片的下载主要有三种方案:

  1. 通过<a>download属性

    html中的a标签中自带下载属性,因此我们可以这样实现下载:

    <a href = "javascript:void()" download="pic.jpg" id="download">下载本图片</a>

    在上面1.2.1使用FileReader接口预览代码的🌰上添加下载功能:

    var inputFile = document.getElementById("imgFile")
    inputFile.onchange = function(){
      var file = this.files[0]
      if(file){
        var reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function(e){
          // 预览图片,this.result/e.target.result都可以
          document.getElementById("file_img").src = e.target.result;
          // 点击下载图片
          var downloadImg = document.getElementById("download");
          downloadImg.setAttribute("href", e.target.result); //给a标签设置href
        }
      }else{
        alert('图片加载失败')
      }
    }
    

    ❗️ 注:除了考虑浏览器的兼容问题之外,还有同源策略的限制,也就是说href指向路径必须与当前网站同源,否则就会出现下载失效或者只能下载无法改名之类的问题

    MDN a标签说明文档 在很多实际情况下,这种方案一并不是可行的,因为我们需要下载的图片不一定是放在后台,跨域限制使得我们无法正常下载。

  2. 通过Javascript动态创建<a>并设置download属性

    原理和限制同上,代码如下(不支持IE):

    function download(url, name){
      const aLink = document.createElement('a')
      aLink.download = name
      aLink.href = url
      // 触发a的单击事件
      aLink.dispatchEvent(new MouseEvent('click', {}))
    }  
    

    以导出canvas图像为例:
    <canvas id = "canvas" > </canvas>

    const canvas = document.getElementById('canvas')
    download(canvas.toDataURL('image/png'),'name.png')
    

    进化版: 只要浏览器可以正常打开这个图片,就可以下载
    原理: 点击下载之后,新建一个隐藏的canvas ,大小和图片本身的大小一致,然后保存这个数据流,就启动了浏览器的下载。(这时候图片是以文字流的形式下载的)

    function downloadImage(url, name){
      let image = new Image()
      // 解决跨域Canvas污染问题
      image.setAttribute("crossOrigin", anonymous)
      image.onload = function(){
        let canvas = document.getElementById('canvas')
        canvas.width = image.width
        canvas.height = image.height
        let context = canvas.getContext("2d")
        context.drawImage(image, 0, 0, image.width, image.height)
        let imgUrl = canvas.toDatatURL("image/png") // 得到图片的base64编码数据
        let a = document.createElement("a")
        let event = new MouseEvent("click") // 创建一个单击事件
        a.download = name || "photo" // 设置图片名称
        a.href = url // 将生成的URL设置为a.href属性
        a.dispatchEvent(event) // 触发单击事件 
      }
      image.src = url
    }
    
  3. 通过Javascript创建<frame>(不推荐❌)

    <iframe height="0" width = "0" src="pic.jpg" name="saveImage" id="saveImage"></iframe>
    <a href="#" onclick="saveImage.document.execCommand('saveAs')">Download</a>

  • document.execCommand('SaveAs') 中 SaveAs 是个非标准值,主要用来兼容 ie 不支持 <a> 标签 download 属性的场景
  • window.frames["iframeName"].document 受到同源策略的影响,如果图片地址跨域,是无法访问的 <frame> 的属性和方法

此外,还可以设置response, header, content-type

轮播图的实现

在电商平台中,轮播图的应用较为广泛:

  1. JS中轮播图的实现原理
    图片轮播的原理就是图片排成一行,然后准备一个只有一张图片大小的容器,对这个容器设置超出部分隐藏,来控制定时器让这些图片整体左移或者右移,这样呈现出来的效果就是图片在轮播

  2. 多个轮播的实现: 封装一个轮播的组件,供多处使用

  3. Vue中的轮播组件应用实例
    之前在复现去哪儿项目的时候,涉及到了广告轮播图的使用,主要使用的是功能强大的swiper组件:

    具体的实现:
    1)Swiper.vue

    markdown编辑器暂时不支持直接放vue代码和html代码,所以分开放
    Swiper.vue中对轮播图的定义

    export default{
      name: 'HomeSwiper',
      props: {
        list: Array
      },
      data(){
        return {
          swiperOption: {  // 自定义轮播图的属性
            pagination: '.swiper-pagination',
            loop: true
          }
        }
      }
    }
    
    .wrapper >>> .swiper-pagination-bullet-activate{
      background: red !important
    }
    .wrapper{
      overflow: hidden
      width: 100%
      height: 0
      padding-bottom: 31.25%
      /*或者直接定义height: 31.25vm,保持宽高比例*/
      background-color: darkgrey
      .swiper-img{
        width: 100%
      }
    }
    

    2)Home.vue中
    使用自定义的Swiper组件,<home-swiper :list="swiperList"></home-swiper>

    import HomeSwiper from './components/Swiper'
    export default{
      name: 'Home',
      components: {
        HomeSwiper
      },
      data(){
        swiperList: [] // 轮播的内容,图片信息之类,数据示例如下:
         // swiperList: [{      
         //   id: '0001',      
         //   imgUrl: 'https://img1.qunarzz.com/sight/p0/1606/be/be7426985c756fc7e2.img.jpg_600x330_4dc81148.jpg'      
         // }, {      
         //   id: '0002',      
         //   imgUrl: 'https://img1.qunarzz.com/sight/p0/1412/ad/32a5e0cd8baa07f908eab28e03d5a89f.water.jpg_600x330_353965dc.jpg'      
         // }, {      
         //   id: '0003',      
         //   imgUrl: 'https://img1.qunarzz.com/sight/p0/1603/b5/b55923ced74910b990.water.jpg_600x330_05ff4419.jpg'      
         // }]
      }
    }
    

图片的压缩

通常在移动端项目的开发中,图片的大小非常影响加载性能,图片过大十分耗费用户流量,延缓加载时间,会使得用户的体验大打折扣。对图片进行压缩是前端优化的重中之重,在很多地方我们需要对其进行压缩,比如在上传之前对手机里选择的高达好几M的图片进行压缩。图片压缩的常见方式:插件压缩、Canvas压缩等

  1. tinypng插件压缩
    可以使用tinypng插件实现压缩项目图片,毕竟一一官网上传太麻烦了。
    官方给出了node包,gulp-tinypng,但是需要官方注册邮件,拿到key之后每月限免500张。

使用gulp-tinypng-compress插件的具体实现:

    var gulp = require('gulp');
    var tinypng = require('gulp-tinypng-compress');
    
    gulp.task("tinypng", function(){
        gulp.src('img/**/*.{png,jpg,jpeg}')
            .pipe(tinypng({
                key: '-HERE IS YOUR API KEY-',
                sigFile: 'images/.tinypng-sigs',
                log: true
            })).on('error', function(err) {
                console.error(err.message);
            })
            .pipe(gulp.dest('dis/img'));
    });
  1. 利用Canvas压缩图片
    也就是使用js实现图片压缩的效果,原理:
  • 利用canvasdrawImage将目标图片画到画布上
  • 利用画布调整绘制尺寸,以及导出的 quality ,确定压缩的程度
  • 利用canvastoDataURL或者toBlob可以将画布上的内容导出成 base64 格式的数据

Q&A:

  1. ios下图片翻转
    👉 使用import EXIF from 'exif-js'来获取手机的方向,然后对canvas的宽高进行处理
  2. 压缩到特定大小
    👉 let imgDataLength = dataUrl.length;
    👉 获取到数据后,判断压缩后的图片大小是否满足需求,否则就降低尺寸以及质量,再次压缩
  3. quality对png无效
    👉 因此导出的格式统一为jpeg, 透明背景填充为白色
// 填充白色背景
ctx.fillStyle = fillBgColor;
ctx.fillRect(0, 0, size.w, size.h);

具体代码实现:

/**
 * 文件读取并通过canvas压缩转成base64
 * @param files
 * @param callback
 */

//EXIF js 可以读取图片的元信息  https://github.com/exif-js/exif-js
import EXIF from 'exif-js';

// 压缩图片时 质量减少的值
const COMPRESS_QUALITY_STEP = 0.03;
const COMPRESS_QUALITY_STEP_BIG = 0.06;
// 压缩图片时,图片尺寸缩放的比例,eg:0.9, 等比例缩放为0.9
const COMPRESS_SIZE_RATE = 0.9;

let defaultOptions = {
    removeBase64Header: true,
    // 压缩后允许的最大值,默认:300kb
    maxSize: 200 * 1024,
    fillBgColor: '#ffffff'
};

/**
 *  将待上传文件列表压缩并转换base64
 *  !!!! 注意 : 图片会默认被转为 jpeg , 透明底会加白色背景
 *  files : 文件列表 ,必须是数组
 *  callback : 回调,每个文件压缩成功后都会回调,
 *  options :配置
 *  options.removeBase64Header : 是否需要删除 'data:image/jpeg;base64,'这段前缀,默认true
 *  @return { base64Data: '',fileType: '' }, //fileType强制改为jpeg
 */
export function imageListConvert(files, callback, options = {}) {
    if (!files.length) {
        console.warn('files is null');
        return;
    }

    options = { ...defaultOptions, ...options };
    // 获取图片方向--iOS拍照下有值
    EXIF.getData(files[0], function() {
        let orientation = EXIF.getTag(this, 'Orientation');
        for (let i = 0, len = files.length; i < len; i++) {
            let file = files[i];
            let fileType = getFileType(file.name);

            //强制改为jpeg
            fileType = 'jpeg';

            let reader = new FileReader();
            reader.onload = (function() {
                return function(e) {
                    let image = new Image();
                    image.onload = function() {
                        let data = convertImage(
                            image,
                            orientation,
                            fileType,
                            options.maxSize,
                            options.fillBgColor
                        );
                        if (options.removeBase64Header) {
                            data = removeBase64Header(data);
                        }
                        callback({
                            base64Data: data,
                            fileType: fileType
                        });
                    };
                    image.src = e.target.result;
                };
            })(file);
            reader.readAsDataURL(file);
        }
    });
}

/**
 * 将 image 对象 画入画布并导出base64数据
 */
export function convertImage(
    image,
    orientation,
    fileType = 'jpeg',
    maxSize = 200 * 1024,
    fillBgColor = '#ffffff'
) {
    let maxWidth = 1280,
        maxHeight = 1280,
        cvs = document.createElement('canvas'),
        w = image.width,
        h = image.height,
        quality = 0.9;

    /**
     * 这里用于计算画布的宽高
     */
    if (w > 0 && h > 0) {
        if (w / h >= maxWidth / maxHeight) {
            if (w > maxWidth) {
                h = (h * maxWidth) / w;
                w = maxWidth;
            }
        } else {
            if (h > maxHeight) {
                w = (w * maxHeight) / h;
                h = maxHeight;
            }
        }
    }

    let ctx = cvs.getContext('2d');
    let size = prepareCanvas(cvs, ctx, w, h, orientation);

    // 填充白色背景
    ctx.fillStyle = fillBgColor;
    ctx.fillRect(0, 0, size.w, size.h);

    //将图片绘制到Canvas上,从原点0,0绘制到w,h
    ctx.drawImage(image, 0, 0, size.w, size.h);

    let dataUrl = cvs.toDataURL(`image/${fileType}`, quality);

    //当图片大小 > maxSize 时,循环压缩,并且循环不超过5次
    let count = 0;
    while (dataUrl.length > maxSize && count < 10) {
        let imgDataLength = dataUrl.length;
        let isDoubleSize = imgDataLength / maxSize > 2;

        // 质量一次下降
        quality -= isDoubleSize
            ? COMPRESS_QUALITY_STEP_BIG
            : COMPRESS_QUALITY_STEP;
        quality = parseFloat(quality.toFixed(2));

        // 图片还太大的情况下,继续压缩 。 按比例缩放尺寸
        let scaleStrength = COMPRESS_SIZE_RATE;
        w = w * scaleStrength;
        h = h * scaleStrength;

        size = prepareCanvas(cvs, ctx, w, h, orientation);

        //将图片绘制到Canvas上,从原点0,0绘制到w,h
        ctx.drawImage(image, 0, 0, size.w, size.h);

        console.log(`imgDataLength:${imgDataLength} , maxSize --> ${maxSize}`);
        console.log(`size.w:${size.w}, size.h:${size.h}, quality:${quality}`);
        dataUrl = cvs.toDataURL(`image/jpeg`, quality);
        count++;
    }

    console.log(`imgDataLength:${dataUrl.length} , maxSize --> ${maxSize}`);
    console.log(`size.w:${size.w}, size.h:${size.h}, quality:${quality}`);

    cvs = ctx = null;
    return dataUrl;
}

/**
 * 准备画布
 * cvs 画布
 * ctx 上下文
 * w : 想要画的宽度
 * h : 想要画的高度
 * orientation : 屏幕方向
 */
function prepareCanvas(cvs, ctx, w, h, orientation) {
    cvs.width = w;
    cvs.height = h;
    //判断图片方向,重置canvas大小,确定旋转角度,iphone默认的是home键在右方的横屏拍摄方式
    let degree = 0;
    switch (orientation) {
        case 3:
            //iphone横屏拍摄,此时home键在左侧
            degree = 180;
            w = -w;
            h = -h;
            break;

        case 6:
            //iphone竖屏拍摄,此时home键在下方(正常拿手机的方向)
            cvs.width = h;
            cvs.height = w;
            degree = 90;
            // w = w;
            h = -h;
            break;
        case 8:
            //iphone竖屏拍摄,此时home键在上方
            cvs.width = h;
            cvs.height = w;
            degree = 270;
            w = -w;
            // h = h;
            break;
    }

    // console.log(`orientation --> ${orientation} , degree --> ${degree}`);
    // console.log(`w --> ${w} , h --> ${h}`);
    //使用canvas旋转校正
    ctx.rotate((degree * Math.PI) / 180);
    return { w, h };
}

/**
 * 截取 ‘data:image/jpeg;base64,’,
 * 截取到第一个逗号
 */
export function removeBase64Header(content) {
    if (content.substr(0, 10) === 'data:image') {
        let splitIndex = content.indexOf(',');
        return content.substring(splitIndex + 1);
    }
    return content;
}

export function getFileType(fileName = '') {
    return fileName.substring(fileName.lastIndexOf('.') + 1);
}

export function checkAccept(
    file,
    accept = 'image/jpeg,image/jpg,image/png,image/gif'
) {
    return accept.toLowerCase().indexOf(file.type.toLowerCase()) !== -1;
}

网站收藏

推荐几个比较常用的网站,值得收藏⭐️

  1. 阿里巴巴矢量图标库Iconfont
  2. 图片转Base64在线网站
  3. 图片的格式转换
  4. Tiny_png图片压缩不失真
  5. 创可贴平面设计素材网

好用的切图工具

  • Photoshop CC
  • XD
  • PxCook
  • FastStone Capture截图工具

如果你看到这里了,那么你将会收获一件小干货
💥——HOT面试题分享:如何选择图片的格式❓(例如png,webp(B_Cornelius))
图片格式的特点汇总表

关于图片的总结大概就是这些了,忽然发现这篇文章像极了cc最心爱❤️的螺蛳粉(又臭又长)😝 hhhhhhhhhh但整理总结的过程十分迷人的


Author: Casey Lu
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Casey Lu !
评论
 Previous
Javascript笔试刷题小结(四) Javascript笔试刷题小结(四)
最近在准备美团的笔试,本来就想着继续刷心爱的leetcode,但是看了下是在赛码网OJ,,发现并不是直接写函数体那样,输入输出要有所改变,#%¥……@%#&@#*&^%$ 输入(按行): read_line() 输出:pri
2020-03-28
Next 
Javascript笔试刷题小结(三) Javascript笔试刷题小结(三)
生命不息,刷题不止,hhhhhhhhhh … 可爱的我又来了 1. Leetcode 1013. 将数组分成和相等的三个部分 **思路一:**最开始看到题目的时候cc是这么想的,既然能分成三个部分,不妨先计算每个部分的和,然后只要能成功地
2020-03-19
  TOC