图片压缩的功能,基于以下一些知识点:
- input的file类型
- react的传值机制
- 子盒子如何在父盒子中居中
- ...的用处
- file与canvas等类型之间的相互转换
- js如何保留小数点后两位
- js中标签的显示与否的几种神仙写法
- 关于_this=this的便利性
- 箭头函数
- 当对象不存在时,使用其length属性会报错,这个时候该怎么办
- 循环渲染:for,forEach,map等
- a标签的神仙用法
- input原先的样式太难看了,如何更改input的样式
- 一种比较好的代码书写结构
- 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);