js手写一个前端压缩图片的方法,并将代码同步到github且发布到npm

24 篇文章 0 订阅
2 篇文章 0 订阅

本文档主要实现两个目标:

  • 用js写一个将上传的图片压缩的方法
  • 将自己写的代码同步到github且发布到npm

 

现在从0零开始操作,一步一步实现这两个目标;

 

1. Prepare: 先注册NPM和github账号

过程省略,进入各自的官网,创建账号

npm: https://www.npmjs.com/

github: https://github.com/

 

2. Coding: 结合两个目标,编码

默认你已经安装了node环境,node环境也包括了npm环境

新建工程文件夹,然后执行下面的命令,初始化一个package.json文件

package.json: 

package.json就是管理你本地安装的npm包,用于定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。一个package.json文件可以做如下事情: 

展示项目所依赖的npm包
允许你指定一个包的版本[范围] 
让你建立起稳定,意味着你可以更好的与其他开发者共享

注意:npm init -y 执行之后,node会自动在当前目录生成一个基本的package.json文件。

npm init -y // 初始化一个package.json文件

默认生产的package.json文件: 

{
  "name": "image-compress-tool",
  "version": "1.0.0",
  "description": "A package for compressing a large image to a small size image",
  "main": "index.js",
  "repository": "https://github.com/yorcent/image-compress-tool.git",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["js", "compress", "image"],
  "author": "Yorcent Luo",
  "license": "ISC"
}

"main"这一项 是默认的js入口文件, 可以修改。

"repository"这一项暂时不填,一会你创建了对应的github仓库再填进去对应的仓库地址

新建index.js文件,编写代码,如:

/*
 * @LastEditTime: 2021-05-19 16:54:13
 * @LastEditors: Please set LastEditors
 * @Description: 图片处理相关方法
 * @FilePath: \light_monitor_web\src\utils\imageCompress.js
 */
/**
     * @description: 图片压缩函数
     * @param {*} img: Image 格式的image源文件
     * @param {*} orientation:需要旋转的参数,一般不需要,除非遇到图片旋转问题才需要
     * @param {*} compressType:压缩图片的类型:按照size压缩或者按照分辨率(宽度)压缩, 如果0: 按size压缩,如果是按分辨率压缩,则传需要压缩的目标宽度,如500
     * @param {*} quality:压缩图片质量:数值越小,代表压缩后的图片质量越低,大小越小
     * @param {*} imageFomate:图片格式,如:image/png
     * @param {*} returnType: 压缩后返回图片的类型,可选项为:base64  /blob
     * @param {*} cb: 回调
     * @return {*}
     */
function compress(img, orientation, compressType, quality, imageFomate, returnType, cb) {
    let canvas = document.createElement("canvas")
    let ctx = canvas.getContext('2d')
    //瓦片canvas
    let tCanvas = document.createElement("canvas")
    let tctx = tCanvas.getContext("2d")
    let width = compressType ? compressType : img.width
    let height = compressType ? compressType * (img.height / img.width).toFixed(3) : img.height
    // 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
    let ratio
    if ((ratio = width * height / 4000000) > 1) {
      console.log("大于400W像素")
      ratio = Math.sqrt(ratio)
      width /= ratio
      height /= ratio
    } else {
      ratio = 1
    }
    canvas.width = width
    canvas.height = height
    //        铺底色
    ctx.fillStyle = "#fff"
    ctx.fillRect(0, 0, width, height)
    // 如果图片像素大于100万则使用瓦片绘制
    let count
    if ((count = width * height / 1000000) > 1) {
      console.log("超过100W像素")
      count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
      //            计算每块瓦片的宽和高
      let nw = ~~(width / count)
      let nh = ~~(height / 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, width, height)
    }
    //修复有的时候上传图片的时候 被旋转的问题
    if(orientation !== '' && orientation !== 1){
      switch(orientation){
        case 6:// 需要顺时针(向左)90度旋转
          this.rotateImg(img,'left',canvas)
          break
        case 8: // 需要逆时针(向右)90度旋转
          this.rotateImg(img,'right',canvas)
          break
        case 3:// 需要180度旋转
          this.rotateImg(img,'right',canvas) //转两次
          this.rotateImg(img,'right',canvas)
          break
      }
    }
    // 进行压缩
    if (returnType === 'base64Url') {
      let ndata = canvas.toDataURL(imageFomate, quality) // 第一个参数是图片格式,如:image/png; 第二个参数代表压缩后的图片质量,数值越小,代表压缩后的图片质量越低,大小越小
      console.log('压缩前:' + img.src.length)
      console.log('压缩后:' + ndata.length)
      console.log('压缩率:' + ~~(100 * (img.src.length - ndata.length) / img.src.length) + "%")
      tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
      cb(ndata, ndata.length)
    } else if (returnType === 'blob') {
      canvas.toBlob((blob) => {
        console.log('压缩前:' + img.raw.size) // 这个时候的压缩率不太准确,因为initSize算得是原图的base64的长度
        console.log('压缩后:' + blob.size)
        console.log('压缩率:' + ~~(100 * (img.raw.size - blob.size) / img.raw.size) + "%")
        tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
        cb(blob, blob.size)
      }, imageFomate, quality)
    }
}

为了测试代码,我直接写了一个简单的页面,作为图片压缩的工具使用,同时验证代码;

新建test.html文件,编码如下:

<!--
 * @Author: yorcent
 * @Date: 2021-05-08 14:30:16
 * @LastEditTime: 2021-05-19 17:38:59
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \imageCompressTool\test.html
-->
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>Image Compress Tool</title>
    <style>
        .originImage {
            width: 300px;
            height: 300px;
        }
        .compressImage {
            width: auto;
            display: block;
        }
        .fileTip {
            color: red;
        }
        .infoLabel {
            color: red;
            height: 40px;
            width: auto;
        }
    </style>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app">
      <input id="inputFile" type="file" name="file" onchange="handleFileChange()">
      <div class="fileTip"></div>
      <img class="originImage">
      <div class="infoLabel"></div>
      <img class="compressImage">
    </div>
    <script type="text/javascript" src="./index.js"></script>
    <script>
        var fileTip = document.getElementsByClassName('fileTip')
        var infoLabel = document.getElementsByClassName('infoLabel')
        var originImage = document.getElementsByClassName('originImage')
        var compressImage = document.getElementsByClassName('compressImage')
        var inputFile = document.getElementById('inputFile')
        function handleFileChange ()  {
            const files = inputFile.files
            if(files && files[0]) {
                const file = files[0]
                var formatArr = ['jpg', 'png', 'jpeg']
                if (!file.type.split('image/')[1] || !formatArr.includes(file.type.split('image/')[1].toLowerCase())) {
                    fileTip[0].innerHTML = '上传图片只能是jpg/jpeg/png格式!'
                    return false
                }
                blob2Base64(file, function (base64Data) { // blob 转base64
                    let img = new Image()
                    img.src = base64Data
                    img.raw = file
                    originImage[0].src = base64Data
                    const timout = setTimeout(() => {
                        if (file.size > (500 * 1024) || img.width > 600) { // // 图片大小大于500K或宽度大于600px就压缩
                            fileTip[0].innerHTML = ''
                            var imageDataType = 'base64Url' // blob 或者 base64Url
                            compress(img, 1, 600, 1, file.type, imageDataType, function (data, size) {
                                if (imageDataType === 'base64Url') {
                                    data = base642Blob(data) // base64转blob
                                }
                                // let newFile = new File( // 也可以手动new一个File格式的文件,第一个参数为文件blob数据,格式是数组;第二个参数是文件名
                                //   [imgBlob],
                                //   file.name
                                // )
                                infoLabel[0].innerHTML = `压缩前:${imageDataType === 'blob' ? file.size : base64Data.length} B; 压缩后:${size} B; 压缩率:${imageDataType === 'blob' ? ~~(100 * (file.size - size)/file.size) : ~~(100 * (base64Data.length - size)/base64Data.length)}%` // 大小统计需和文件转换之后的类型保持一致;blob和base64格式统计出来的数据大小是不一样的
                                file.raw = data // 后端接口需要将文件blob格式的数据放到file.raw中
                                file.url = URL.createObjectURL(data)
                                compressImage[0].src = file.url
                                clearTimeout(timout)
                            })
                        } else {
                            file.base64 = base64Data
                            infoLabel[0].innerHTML = '图片未达到压缩条件'
                            compressImage[0].src = base64Data
                            file.url = URL.createObjectURL(file.raw)
                        }
                    }, 500) // 延时添加用来给压缩过程留一些时间,否则会出现压缩失效的情况
                })
            }
        }
    </script>
  </body>
</html>

测试文件test.html 用到了另外两个比较常用的方法,一般上传文件和文件转换时都用得上,所以这里也加入到index.js文件中:

// base64字符串转blob
function base642Blob (code) {
    let parts = code.split(';base64,')
    if (parts.length < 2) {
      return null
    }
    let contentType = parts[0].split(':')[1]
    let raw = window.atob(parts[1])
    let rawLength = raw.length
    let uInt8Array = new Uint8Array(rawLength)
    for (let i = 0; i < rawLength; ++i) {
      uInt8Array[i] = raw.charCodeAt(i)
    }
    return new Blob([uInt8Array], { type: contentType })
}
  // blob转base64字符串
function blob2Base64 (blob, cb) {
    let reader = new FileReader()
    reader.readAsDataURL(blob)
    reader.onloadend = function(e) {
      const base64data = e.target.result
      cb(base64data)
    }
}

然后在浏览器中打开一下test.html, 上传图片,压缩,结果如下:

node test

检查执行结果,初步测试通过。

注意: 一般如果你的npm包有比较方便的测试代码或者用例,更能吸引开发者使用你发布的npm包。所以,一个优秀的npm包还需要更完善的测试环境。这里暂时不深入展示。

新建README.md文件

# image-compress-tool

A package for compressing a large image to a small size image

# install

npm install -g image-compress-tool

# usage

引入index.js文件:
<script type="text/javascript" src="./index.js"></script>
模拟tesh.html中的样例,上传文件调用方法:
compress(img, 1, 600, 1, file.type, imageDataType, function (data, size) {
}
params description:
/**
     * @description: 图片压缩函数
     * @param {*} img(Image): Image 格式的image源文件
     * @param {*} orientation(Number):需要旋转的参数,一般不需要,除非遇到图片旋转问题才需要
     * @param {*} compressType(Number):压缩图片的类型:按照size压缩或者按照分辨率(宽度)压缩, 如果值为0: 按size压缩,如果是按分辨率压缩,则传需要压缩的目标宽度,如500
     * @param {*} quality(Number):压缩图片质量:数值越小,代表压缩后的图片质量越低,大小越小
     * @param {*} imageFomate(String):图片格式,如:image/png
     * @param {*} returnType((String)): 压缩后返回图片的类型,可选项为:base64  /blob
     * @param {*} cb(fuction(param1, param2)): 回调方法:返回压缩后的文件数据和文件大小
     * @return {*}
**/

README.md文件 是方便开发者快速的了解和学习如何使用你这个npm包,非常重要。

3. 将代码上传至github仓库

这个方法有三种,这里使用其中一种。

1.1 新建git仓库

1.2 将新建好的git仓库地址克隆到本地

1.3 将之前创建的npm包的代码文件夹丢到git仓库克隆后的文件夹内,和.git文件在同一级目录

1.4 执行以下命令

git add .

git commit -m "init commit"

git push -u origin master

4.将github仓库地址复制到npm包的package.json里的“repository”中

"repository": "https://github.com/yorcent/image-compress-tool.git",

5. 执行以下命令将完整和正确的代码发布到npm

npm login
// 输入npm账号名

// 输入npm密码

// 输入npm邮箱

npm publish

6. 验证发布是否成功

一般你登录成功之后,常见的发布失败的原因有两个:

1.  包名已经被占用,失败结果如下,或者一些其他的错误提示,其实基本都是包名重复了,查重的方式很简单,直接去npm搜你的包名就好了,如果收到了,那你就得修改包名,直接publish成功为止

2. 包名或者版本号不符合规则,这个字需要根据规则重新修改包名和版本号就行了(发布包的名字不能跟npm网上已有的包重名,不能有大写字母,空格,和下划线)

npm publish成功 之后 会返回给你发布包的名称和版本:

如果没有返回则发布失败,需要重新发布;或者如果你又修改了代码,重新发布的话你需要将包修改版本号之后重新发布

然后你试着npm install -s packageName 检查是否安装到了node-modules目录。最后你可以开始调用你的写的方法。

7. 踩坑

如果你将你的npm镜像改成了淘宝镜像,那么需要将镜像改回城 npm官方镜像,然后再重新npm login, 否则会发布失败。

npm 设置镜像命令:

npm config set registry http://registry.npmjs.org

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值