247 行
11 KiB
JavaScript
247 行
11 KiB
JavaScript
import './upload.css';
|
|
|
|
import { Component, createRef, Fragment } from "react";
|
|
import { apis } from '../../helper/apis';
|
|
import Spinner from '../Spinner/Spinner';
|
|
|
|
export default class UploadUnit extends Component {
|
|
imgRef = createRef();
|
|
imgWidth;
|
|
imgHeight;
|
|
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
// 0: loading, 1: compressing, 2: compressed, 3: uploading, 4: uploaded
|
|
status: -1,
|
|
src: null,
|
|
file: null,
|
|
progress: 0,
|
|
width: 0,
|
|
isScaled: false,
|
|
transform: ''
|
|
};
|
|
this.upload = this.upload.bind(this);
|
|
this.handleCancel = this.handleCancel.bind(this);
|
|
this.scale = this.scale.bind(this);
|
|
this.unscale = this.unscale.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
setTimeout(() => {
|
|
this.readFile(this.props.file);
|
|
}, 100);
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
if (this.props.upload && this.state.status === 2)
|
|
this.upload();
|
|
}
|
|
|
|
handleCancel() {
|
|
this.setState({ status: -1 });
|
|
setTimeout(() => {
|
|
this.props.onCancel();
|
|
}, 300);
|
|
}
|
|
|
|
readFile(file) {
|
|
if (!["image/jpeg", "image/png", "image/gif"].includes(file.type)) {
|
|
alert('请不要上传jpg、png、gif格式以外的文件!');
|
|
this.handleCancel();
|
|
return;
|
|
}
|
|
let reader = new FileReader();
|
|
this.setState({ status: -2 });
|
|
setTimeout(() => {
|
|
this.setState({ status: 0 });
|
|
reader.onload = () => {
|
|
var image = new Image();
|
|
// 被注释掉的是用来应对ios巨大图片没来得及加载的问题
|
|
// image.onload = () => setTimeout(() => this.convert(image), 1000);
|
|
image.onload = () => this.convert(image);
|
|
image.src = reader.result;
|
|
this.setState({ status: 1 });
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}, 300);
|
|
}
|
|
|
|
convert(img) {
|
|
if (this.converted) return;
|
|
var canvas = document.createElement('canvas');
|
|
var { width, height } = img;
|
|
if (width > 1600) {
|
|
height *= 1600 / width;
|
|
width = 1600;
|
|
}
|
|
if (height > 1200) {
|
|
width *= 1200 / height;
|
|
height = 1200;
|
|
}
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
this.imgWidth = width;
|
|
this.imgHeight = height;
|
|
canvas.getContext('2d').drawImage(img, 0, 0, width, height);
|
|
canvas.toBlob(blob => {
|
|
this.setState({ src: canvas.toDataURL('image/jpeg', 1), status: 2, file: blob, width: width / height * 200 });
|
|
}, 'image/jpeg', 1);
|
|
}
|
|
|
|
async upload() {
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.onreadystatechange = () => {
|
|
if (xhr.readyState === 4) {
|
|
if (xhr.status === 200) {
|
|
var data = JSON.parse(xhr.responseText);
|
|
if (data.code === 200) {
|
|
this.setState({ status: 4 });
|
|
this.props.onUpload(data.data.url);
|
|
}
|
|
else {
|
|
this.setState({ status: 2 });
|
|
this.props.onUploadError(data.msg);
|
|
}
|
|
}
|
|
else {
|
|
this.setState({ status: 2 });
|
|
this.props.onUploadError('上传失败:服务器出错');
|
|
}
|
|
}
|
|
};
|
|
xhr.onerror = () => {
|
|
this.setState({ status: 2 });
|
|
this.props.onUploadError('请求失败,请检查网络');
|
|
}
|
|
xhr.onprogress = e => {
|
|
if (e.lengthComputable) {
|
|
var percent = Math.round(e.loaded * 100 / e.total);
|
|
this.setState({ progress: percent });
|
|
}
|
|
};
|
|
xhr.open("POST", apis.uploadImage, true);
|
|
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
|
|
|
var fd = new FormData();
|
|
fd.append("image", this.state.file, 'image.jpg');
|
|
xhr.send(fd);
|
|
this.setState({ status: 3, progress: 0 });
|
|
}
|
|
|
|
scale() {
|
|
if (this.state.isScaled) return;
|
|
let el = this.imgRef.current;
|
|
let { x, y } = el.getBoundingClientRect();
|
|
let { innerWidth, innerHeight } = window;
|
|
console.log(x, y, innerWidth, innerHeight);
|
|
let initialScale = 200 / this.imgHeight,
|
|
terminalScale = Math.min(1, Math.min(innerWidth / this.imgWidth, innerHeight / this.imgHeight));
|
|
this.setState({ transform: `translate(${x - (innerWidth - this.imgWidth * terminalScale) / 2}px, ${y - (innerHeight - this.imgHeight * terminalScale) / 2}px) scale(${initialScale})`, isScaled: true });
|
|
setTimeout(() => {
|
|
this.setState({ transform: `translate(0, 0) scale(${terminalScale})` });
|
|
}, 0);
|
|
}
|
|
|
|
unscale() {
|
|
if (!this.state.isScaled) return;
|
|
let el = this.imgRef.current;
|
|
let { x, y } = el.getBoundingClientRect();
|
|
let { innerWidth, innerHeight } = window;
|
|
let initialScale = 200 / this.imgHeight,
|
|
terminalScale = Math.min(1, Math.min(innerWidth / this.imgWidth, innerHeight / this.imgHeight));
|
|
this.setState({ transform: `translate(${x - (innerWidth - this.imgWidth * terminalScale) / 2}px, ${y - (innerHeight - this.imgHeight * terminalScale) / 2}px) scale(${initialScale})`, isScaled: true });
|
|
setTimeout(() => {
|
|
this.setState({ isScaled: false });
|
|
}, 300);
|
|
}
|
|
|
|
render() {
|
|
const angle = this.state.progress / 100 * Math.PI * 2;
|
|
console.log(this.state.transform);
|
|
return (
|
|
<div className="upload-wrap">
|
|
<div className={this.state.status === -1 ? 'upload-unit' : 'upload-unit open'} style={{ width: this.state.status <= 1 ? '' : `${this.state.width}px` }}>
|
|
{
|
|
this.state.src
|
|
? <img src={this.state.src} alt="压缩后的图片" style={{ filter: this.state.status === -1 ? 'opacity(0)' : '' }} ref={this.imgRef} />
|
|
: null
|
|
}
|
|
{
|
|
this.state.status >= 0
|
|
? (
|
|
<Fragment>
|
|
<div className={this.state.status === 2 ? 'upload-unit-mask hide' : 'upload-unit-mask'}>
|
|
{
|
|
this.state.status === 3
|
|
? (
|
|
<div className="upload-unit-progress">
|
|
<div className="progress-circle">
|
|
<svg viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
|
<path d={
|
|
`M 50,3 A 47,47 0 ${this.state.progress < 50 ? 0 : 1},1 ${50 + Math.sin(angle) * 47},${50 - Math.cos(angle) * 47}`
|
|
} fill="transparent" stroke="#FFF" strokeWidth="6" strokeLinecap="round"></path>
|
|
</svg>
|
|
</div>
|
|
<div className="progress-detail">
|
|
上传中
|
|
</div>
|
|
</div>
|
|
)
|
|
: null
|
|
}
|
|
{
|
|
this.state.status <= 1
|
|
? (
|
|
<div className="upload-unit-progress">
|
|
<Spinner />
|
|
<div className="progress-detail">
|
|
{this.state.status === 0 ? '加载中' : '压缩中'}
|
|
</div>
|
|
</div>
|
|
)
|
|
: null
|
|
}
|
|
{
|
|
this.state.status === 4
|
|
? (
|
|
<div style={{ color: '#FFF', fontSize: '16px' }}>上传成功</div>
|
|
)
|
|
: null
|
|
}
|
|
</div>
|
|
<div className="upload-btns">
|
|
{
|
|
this.state.status === 2
|
|
? (
|
|
<button className="btn btn-circle btn-ok" onClick={this.upload}>
|
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M939.717 106.665l-304.049 324.529-78.769 85.071-225.28 240.246-123.668-115.003-129.182-121.305-78.769 84.283 129.969 121.305 33.083 31.508 129.969 121.305 44.111 40.96 304.049-324.529 78.769-85.071 304.049-324.529-84.283-78.769z" fill="#ffffff"></path></svg>
|
|
</button>
|
|
)
|
|
: null
|
|
}
|
|
{
|
|
this.state.status === 2 || this.state.status === 4
|
|
? (
|
|
<button className="btn btn-circle btn-primary" onClick={this.scale}>
|
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M959.488 920.32l-39.168 39.168a27.676444 27.676444 0 0 1-39.139556 0l-162.133333-162.133333a27.648 27.648 0 0 1 0-39.168l39.139556-39.139556a27.648 27.648 0 0 1 39.168 0l162.133333 162.104889c10.808889 10.837333 10.808889 28.359111 0 39.168zM169.358222 712.419556c-149.959111-149.959111-149.959111-393.102222 0-543.061334 149.959111-149.959111 393.073778-149.959111 543.061334 0 149.959111 149.959111 149.959111 393.102222 0 543.061334-149.987556 149.959111-393.102222 149.959111-543.061334 0zM631.324444 249.742222c-105.358222-105.386667-276.195556-105.386667-381.582222 0a269.824 269.824 0 0 0 0 381.610667A269.824 269.824 0 1 0 631.324444 249.742222z" fill="#ffffff"></path><path d="M959.488 920.32l-39.168 39.168a27.676444 27.676444 0 0 1-39.139556 0l-162.133333-162.133333a27.648 27.648 0 0 1 0-39.168l39.139556-39.139556a27.648 27.648 0 0 1 39.168 0l162.133333 162.104889c10.808889 10.837333 10.808889 28.359111 0 39.168zM169.358222 712.419556c-149.959111-149.959111-149.959111-393.102222 0-543.061334 149.959111-149.959111 393.073778-149.959111 543.061334 0 149.959111 149.959111 149.959111 393.102222 0 543.061334-149.987556 149.959111-393.102222 149.959111-543.061334 0zM631.324444 249.742222c-105.358222-105.386667-276.195556-105.386667-381.582222 0a269.824 269.824 0 0 0 0 381.610667A269.824 269.824 0 1 0 631.324444 249.742222z" fill="#ffffff"></path></svg>
|
|
</button>
|
|
)
|
|
: null
|
|
}
|
|
<button className="btn btn-circle btn-gray" onClick={this.handleCancel}>
|
|
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M597.333333 512l284.444445 284.444444c2.929778 3.328 2.929778 22.357333 0 28.444445l-56.888889 56.888889c-6.087111 2.929778-25.116444 2.929778-28.444445 0L512 597.333333 227.555556 881.777778c-3.328 2.929778-22.328889 2.929778-28.444445 0l-56.888889-56.888889c-2.929778-6.087111-2.929778-25.116444 0-28.444445l284.444445-284.444444L142.222222 227.555556c-2.929778-3.328-2.929778-22.328889 0-28.444445l56.888889-56.888889c6.115556-2.929778 25.116444-2.929778 28.444445 0l284.444444 284.444445L796.444444 142.222222c3.328-2.929778 22.357333-2.929778 28.444445 0l56.888889 56.888889c2.929778 6.115556 2.929778 25.116444 0 28.444445L597.333333 512z" fill="#4a4a4a"></path></svg>
|
|
</button>
|
|
</div>
|
|
</Fragment>
|
|
)
|
|
: null
|
|
}
|
|
</div>
|
|
<div className={this.state.isScaled ? 'scaled-wrap open' : 'scaled-wrap'} onClick={this.unscale}>
|
|
<img className="scaled-image" src={this.state.src} alt="压缩后的图片" style={{ transform: this.state.transform }} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
} |