Files
inMyHeartFrontEnd/src/components/UploadUnit/UploadUnit.js

247 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}
}