React(一):基于input的图片上传结合canvas实现图片压缩

图片压缩的功能,基于以下一些知识点:

  1. input的file类型
  2. react的传值机制
  3. 子盒子如何在父盒子中居中
  4. ...的用处
  5. file与canvas等类型之间的相互转换
  6. js如何保留小数点后两位
  7. js中标签的显示与否的几种神仙写法
  8. 关于_this=this的便利性
  9. 箭头函数
  10. 当对象不存在时,使用其length属性会报错,这个时候该怎么办
  11. 循环渲染:for,forEach,map等
  12. a标签的神仙用法
  13. input原先的样式太难看了,如何更改input的样式
  14. 一种比较好的代码书写结构
  15. display: flex;布局,可以看看我之前写的一篇文章:display: flex;弹性布局

话不多说,直接上代码

代码结构:

// 文件名: compreFile.js

// 引入,有一些没用着
import React from 'react';
import { Upload, message, Button, Progress } from 'antd';
import styled from 'styled-components';
import { UploadOutlined, InboxOutlined } from '@ant-design/icons';
import { size } from 'lodash';
import image from 'antd/lib/image';
import styles from '../index.module.css';

export default class CompreFile extends React.Component {
  // 数据的结构如果更改一下的话,可能会更好,比如:preInformation,newInformation
  state = {
    files: [],
    imgNames: [],
    preSizes: [],
    newSizes: [],
    preSrcs: [],
    newSrcs: [],
    percent: Number
  }
  // 获得新的input文件输入
  fileSelectedHandler = (e) => {
    var files = e.target.files;
    console.log(files);
    this.setState({ files: [...this.state.files, ...e.target.files] })
    return files;
  }
  // 点击事件函数
  fileChange = (e) => {
    var _this = this; // this的保留赋值,省去很多麻烦,避免后续this指向不清问题
    var files = this.fileSelectedHandler(e);
    console.log(files);
    [...files].map(file => {
        var imgName = file.name;
        console.log(imgName);
        // this.setState({ imgNames: [...this.state.imgNames, imgName] })
        var preSize = Math.round((file.size / 1024) * 100) / 100;
        console.log(preSize);
        setTimeout(() => {
            this.setState({ 
                preSizes: [...this.state.preSizes, preSize],
                imgNames: [...this.state.imgNames, imgName] 
            })
        }, 0);

        // 选择的文件是图片
        if (file.type.indexOf("image") === 0) {
          // 压缩图片需要的一些元素和对象
          var reader = new FileReader(),
            //创建一个img对象
            img = new Image();

          reader.readAsDataURL(file);
          // 文件base64化,以便获知图片原始尺寸
          reader.onload = function (e) {
            img.src = e.target.result;
            console.log(img.src);
            _this.setState({ preSrcs: [..._this.state.preSrcs, img.src] })
          };

          // base64地址图片加载完毕后执行
          img.onload = function () {
            // 缩放图片需要的canvas(也可以在DOM中直接定义canvas标签,这样就能把压缩完的图片不转base64也能直接显示出来)
            var canvas = document.createElement('canvas');
            var context = canvas.getContext('2d');

            // 图片原始尺寸
            var originWidth = this.width;
            var originHeight = this.height;

            // 最大尺寸限制,可通过设置宽高来实现图片压缩程度
            var maxWidth = 300,
              maxHeight = 300;
            // 目标尺寸
            var targetWidth = originWidth,
              targetHeight = originHeight;
            // 图片尺寸超过300x300的限制
            if (originWidth > maxWidth || originHeight > maxHeight) {
              if (originWidth / originHeight > maxWidth / maxHeight) {
                // 更宽,按照宽度限定尺寸
                targetWidth = maxWidth;
                targetHeight = Math.round(maxWidth * (originHeight / originWidth));
              } else {
                targetHeight = maxHeight;
                targetWidth = Math.round(maxHeight * (originWidth / originHeight));
              }
            }
            // canvas对图片进行缩放
            canvas.width = targetWidth;
            canvas.height = targetHeight;
            // 清除画布
            context.clearRect(0, 0, targetWidth, targetHeight);
            // 图片压缩
            context.drawImage(img, 0, 0, targetWidth, targetHeight);
            /*第一个参数是创建的img对象;第二三个参数是左上角坐标,后面两个是画布区域宽高*/

            //压缩后的图片转base64 url
            /*canvas.toDataURL(mimeType, qualityArgument),mimeType 默认值是'image/png';
             * qualityArgument表示导出的图片质量,只有导出为jpeg和webp格式的时候此参数才有效,默认值是0.92*/

            var newSrc = canvas.toDataURL('image/jpeg', 0.92);//base64 格式
            console.log(newSrc);
            // var newUrl = canvas.toBlob((blob) => {
            //   console.log(blob)
            //   //把blob作为参数传给后端
            // }, 'image/jpeg', 0.92)
            var isShow = size(newSrc) > 0 ? true : false;
            var percent = 100;
            console.log(isShow)

            _this.setState({
              newSrcs: [..._this.state.newSrcs, newSrc],
              isShow: isShow,
              percent: percent
              // newSize: newSize
            })

            // 也可以把压缩后的图片转blob格式用于上传
            canvas.toBlob((blob) => {
              console.log(blob)
              //把blob作为参数传给后端
              console.log(blob.size);
              var newSize = Math.round((blob.size / 1024) * 100) / 100;
              console.log(newSize)
              _this.setState({
                newSizes: [..._this.state.newSizes, newSize]
              })
            }, 'image/jpeg', 0.92)
          };
        } else {
          alert('请上传图片格式');
        }
    });
  }

  render() {
    let {
      files,
      imgNames,
      preSizes,
      newSizes,
      preSrcs,
      newSrcs,
      isShow,
      percent
    } = this.state;
    const imgCount = files.length;
    console.log(imgCount);
    // for (var i = 0; i < files.length; i++) {
    //   console.log(imgNames[i], preSizes[i], newSizes[i], newSrcs[i]);
    // }
    return (
      <>
        <div className={styles.stCompreWrapper} >
          <div className={styles.CompreFileStyle}>
            点击上传文件
            < input className={styles.inputStyle} id="file" multiple type="file" onChange={this.fileChange} />
          </div>
        </div>
        {isShow
          ? <div className={styles.imgListSt}>
            {files.map((item, index) => (
              <div className={styles.showUrlDownload} key={index}>
                <div>
                  <span>{imgNames[index]}</span>
                  <span>{preSizes[index]}KB</span>
                </div>
                <div>
                  <Progress percent={percent} size="middle" />
                </div>
                <div className={styles.after}>
                  <span>{newSizes[index]}KB</span>
                  <a href={newSrcs[index]} download={imgNames[index]}>点击下载</a>
                  <span>{Math.round(((preSizes[index] - newSizes[index]) / preSizes[index]) * 10000) / 100}%</span>
                </div>
              </div>
            ))}</div>
          : null
        }
      </>
    )
  }
}
/* 文件名:index.module.css */

.stCompreWrapper {
  position: relative;
  width: 38rem;
  height: 14rem;
  margin: 1rem auto;
  padding: 0 0 1.6rem;
  color: #40444f;
  border: 0.2rem dashed #616778;
  border-radius: 1.5rem;
  cursor: pointer;
  -webkit-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  -moz-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  -o-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  -ms-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  transition: color 0.2s ease-out, border-color 0.2s ease-out;
}
.CompreFileStyle {
  display: inline-block;
  background: #D0EEFF;
  border: 1px solid #99D3F5;
  border-radius: 4px;
  padding: 4px 12px;
  overflow: hidden;
  color: #1E88C7;
  text-decoration: none;
  text-indent: 0;
  line-height: 20px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.CompreFileStyle:hover {
  background: #AADFFD;
  border-color: #78C3F3;
  color: #004974;
  text-decoration: none;
}
.inputStyle {
  position: absolute;
  right: 0;
  top: 0;
  opacity: 0; 
}
.showUrlDownload {
  display: block;
  margin: 0.4rem;
  padding: 0 1.28rem;
  background: #f0f0f0;
  border: 0.1rem solid #cfdbde;
  border-radius: 0.2rem;
  font-weight: bold;
  line-height: 2.8rem;
  color: #363a43;
  display: flex;
  justify-content: space-between;
}
.showUrlDownload>* {
  display: flex;
  flex: 1;
  justify-content: space-evenly;
}
.showUrlDownload>*>* {
  flex: 1;
  align-self:center;
}
.after>* {
  text-align: right;
}
.imgListSt {
  position: relative;
  width: 56rem;
  margin: 3.2rem auto 20rem;
  padding: 1.0rem 0 1.0rem;
  color: #40444f;
  border: 0.1rem solid #bbcbd0;
  border-radius: 0.2rem;
  background-color: #fff;
  max-width: 96rem;
  cursor: pointer;
  -webkit-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  -moz-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  -o-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  -ms-transition: color 0.2s ease-out, border-color 0.2s ease-out;
  transition: color 0.2s ease-out, border-color 0.2s ease-out;
}
// 文件名: index.ts
import CompreFile from "./compreFile";

export default CompreFile;
// 文件名: index.tsx

import React from 'react';
import { BrowserRouterProps } from 'react-router-dom';
import CompreFile from './components';

function PhotoCompression(props: BrowserRouterProps) {

  return (
    <>
      <CompreFile></CompreFile>
    </>
  );
};

export default React.memo(PhotoCompression);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值