react图片裁剪工具 – react-image-crop

react-image-crop

react-image-crop 是react响应式图片裁剪的一个工具包,能做到裁剪区域,裁剪宽高比限制等等。
npm地址:https://www.npmjs.com/package/react-image-crop

阅读前须知

版本的不同,在使用时有不同的表现,也可能造成使用失败。
本文中基于版本:8.6.2

安装

npm

1
npm install react-image-crop --save

CDN

1
<script src="//i2.wp.com/unpkg.com/react-image-crop/dist/ReactCrop.min.js"></script>

注意,当使用

组件使用

1
2
3
4
5
6
7
8
  <ReactCrop
            src={imageurl}
            crop={crop}
            ruleOfThirds
            onImageLoaded={this.onImageLoaded}
            onComplete={this.onCropComplete}
            onChange={this.onCropChange}
          />

属性解释:

src:图片路径
crop:裁剪相关配置 参考地址:https://www.npmjs.com/package/react-image-crop#crop-required
ruleOfThirds:裁剪区域三分规则默认false;

ruleOfThirds为false时:
在这里插入图片描述
ruleOfThirds为true时(注意图中裁剪区域的九宫格):
在这里插入图片描述
onImageLoaded: 图片加载时的回调函数,返回image对象;
onComplete:裁剪区域调整大小、拖动、微调之后的回调,返回当前裁剪对象;
onChange:裁剪区域调整大小、拖动、微调时的回调,返回当前裁剪对象;官网中的提示:请注意,您必须实现此回调并更新您的裁剪状态,否则什么都不会改变!
更多属性参考

获取到的裁剪区域保存为图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  // 组件的onComplete事件
  onCropComplete = crop => {
    this.makeClientCrop(crop);
  };
  // 调用getCroppedImg函数获取裁剪图片信息
   async makeClientCrop(crop) {
    if (this.imageRef && crop.width && crop.height) {
      const croppedImageUrl = await this.getCroppedImg(
        this.imageRef,
        crop,
        'newFile.jpeg'
      );
      /*
      * croppedImageUrl: {url,blob,crop}
      *  @url:图片文件对象的DOMString url
      *  @blob: 图片文件的类文件对象
      *  @crop: 裁剪参数
      */
  }
// 裁剪区域转图片的处理
 getCroppedImg(image, crop, fileName) {
    const canvas = document.createElement('canvas');
    const scaleX = image.naturalWidth / image.width;
    const scaleY = image.naturalHeight / image.height;
    canvas.width = crop.width;
    canvas.height = crop.height;
    const ctx = canvas.getContext('2d');

    ctx.drawImage(
      image,
      crop.x * scaleX,
      crop.y * scaleY,
      crop.width * scaleX,
      crop.height * scaleY,
      0,
      0,
      crop.width,
      crop.height
    );

    return new Promise((resolve, reject) => {
      canvas.toBlob(blob => {
        if (!blob) {
          //reject(new Error('Canvas is empty'));
          console.error('Canvas is empty');
          return;
        }
        blob.name = fileName;
        window.URL.revokeObjectURL(this.fileUrl);
        this.fileUrl = window.URL.createObjectURL(blob);
        resolve({url:this.fileUrl,blob,crop});
      }, 'image/jpeg');
    });
  }

官方完整示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import ReactDOM from "react-dom";
import React, { PureComponent } from "react";
import ReactCrop from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";

import "./App.css";

class App extends PureComponent {
  state = {
    src: null,
    crop: {
      unit: "%",
      width: 30,
      aspect: 16 / 9
    }
  };

  onSelectFile = e => {
    if (e.target.files && e.target.files.length > 0) {
      const reader = new FileReader();
      reader.addEventListener("load", () =>
        this.setState({ src: reader.result })
      );
      reader.readAsDataURL(e.target.files[0]);
    }
  };

  // If you setState the crop in here you should return false.
  onImageLoaded = image => {
    this.imageRef = image;
  };

  onCropComplete = crop => {
    this.makeClientCrop(crop);
  };

  onCropChange = (crop, percentCrop) => {
    // You could also use percentCrop:
    // this.setState({ crop: percentCrop });
    this.setState({ crop });
  };

  async makeClientCrop(crop) {
    if (this.imageRef && crop.width && crop.height) {
      const croppedImageUrl = await this.getCroppedImg(
        this.imageRef,
        crop,
        "newFile.jpeg"
      );
      this.setState({ croppedImageUrl });
    }
  }

  getCroppedImg(image, crop, fileName) {
    const canvas = document.createElement("canvas");
    const scaleX = image.naturalWidth / image.width;
    const scaleY = image.naturalHeight / image.height;
    canvas.width = crop.width;
    canvas.height = crop.height;
    const ctx = canvas.getContext("2d");

    ctx.drawImage(
      image,
      crop.x * scaleX,
      crop.y * scaleY,
      crop.width * scaleX,
      crop.height * scaleY,
      0,
      0,
      crop.width,
      crop.height
    );

    return new Promise((resolve, reject) => {
      canvas.toBlob(blob => {
        if (!blob) {
          //reject(new Error('Canvas is empty'));
          console.error("Canvas is empty");
          return;
        }
        blob.name = fileName;
        window.URL.revokeObjectURL(this.fileUrl);
        this.fileUrl = window.URL.createObjectURL(blob);
        resolve(this.fileUrl);
      }, "image/jpeg");
    });
  }

  render() {
    const { crop, croppedImageUrl, src } = this.state;

    return (
      <div className="App">
        <div>
          <input type="file" accept="image/*" onChange={this.onSelectFile} />
        </div>
        {src && (
          <ReactCrop
            src={src}
            crop={crop}
            ruleOfThirds
            onImageLoaded={this.onImageLoaded}
            onComplete={this.onCropComplete}
            onChange={this.onCropChange}
          />
        )}
        {croppedImageUrl && (
          <img alt="Crop" style={{ maxWidth: "100%" }} src={croppedImageUrl} />
        )}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

在线编辑地址:https://codesandbox.io/s/react-image-crop-demo-with-classes-7jxc0?file=/src/index.js:0-2838