├── .gitignore
├── .babelrc
├── docs
├── index.html
└── index.js
├── webpack.config.js
├── webpack.dist.config.js
├── package.json
├── cropper.css
├── lib
├── cropper.css
├── index.src.js
└── draggable-resizable-box.js
├── README.md
├── index.js
├── dist
├── react-crop.min.js
├── react-crop.js
└── react-crop.js.map
└── draggable-resizable-box.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 | npm_debug.log
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react", "stage-1"],
3 | "plugins": ["transform-object-assign"]
4 | }
5 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | react-crop example
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack')
2 |
3 | module.exports = {
4 | entry: ['./docs/index.js', 'babel-polyfill'],
5 | output: {
6 | path: './docs',
7 | filename: 'bundle.js'
8 | },
9 | module: {
10 | loaders: [
11 | {
12 | test: /\.js?$/,
13 | exclude: /node_modules/,
14 | loader: 'babel'
15 | },
16 | {
17 | test: /\.css$/,
18 | loader: 'style-loader!css-loader'
19 | }
20 | ]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/webpack.dist.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
3 |
4 | module.exports = {
5 |
6 | entry: {
7 | 'react-crop': './lib/index.src.js',
8 | 'react-crop.min': './lib/index.src.js'
9 | },
10 |
11 | externals: [
12 | 'react',
13 | 'react-dom'
14 | ],
15 |
16 | output: {
17 | filename: '[name].js',
18 | chunkFilename: '[id].chunk.js',
19 | path: 'dist',
20 | publicPath: '/',
21 | libraryTarget: 'umd',
22 | library: 'ReactCrop'
23 | },
24 |
25 | devtool: 'source-map',
26 |
27 | plugins: [
28 | new webpack.DefinePlugin({
29 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
30 | }),
31 | new UglifyJsPlugin({
32 | include: /\.min\.js$/,
33 | minimize: true,
34 | compress: {
35 | warnings: false
36 | }
37 | })
38 | ],
39 |
40 | module: {
41 | loaders: [
42 | { test: /\.js?$/, exclude: /node_modules/, loader: 'babel'},
43 | {
44 | test: /\.css$/,
45 | loader: 'style-loader!css-loader'
46 | }
47 | ]
48 | }
49 |
50 | };
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-crop",
3 | "version": "4.0.2",
4 | "description": "An image cropper that moves the cropper rather than the image",
5 | "repository": "https://github.com/instructure-react/react-crop",
6 | "main": "index.js",
7 | "scripts": {
8 | "build": "NODE_ENV=production webpack --config webpack.dist.config.js",
9 | "compile-index": "cp lib/cropper.css . && babel lib/index.src.js --out-file index.js",
10 | "compile-box": "babel lib/draggable-resizable-box.js --out-file draggable-resizable-box.js",
11 | "compile": "npm run compile-index && npm run compile-box && webpack",
12 | "docs": "webpack-dev-server --content-base docs/",
13 | "prepublish": "npm run compile && npm run build"
14 | },
15 | "keywords": [
16 | "react",
17 | "image",
18 | "crop",
19 | "react-component"
20 | ],
21 | "author": "Matthew Sessions (http://www.matthewsessions.com/)",
22 | "license": "MIT",
23 | "devDependencies": {
24 | "babel-cli": "^6.9.0",
25 | "babel-core": "^6.9.0",
26 | "babel-loader": "^6.2.4",
27 | "babel-plugin-transform-object-assign": "^6.8.0",
28 | "babel-polyfill": "^6.20.0",
29 | "babel-preset-es2015": "^6.9.0",
30 | "babel-preset-react": "^6.5.0",
31 | "babel-preset-stage-1": "^6.5.0",
32 | "css-loader": "^0.26.1",
33 | "node-libs-browser": "0.5.2",
34 | "react": "^15.4.1",
35 | "react-dom": "15.4.1",
36 | "style-loader": "^0.13.1",
37 | "webpack": "1.12.0",
38 | "webpack-dev-server": "1.10.1"
39 | },
40 | "dependencies": {
41 | "autoprefixer": "5.2.0",
42 | "data-uri-to-blob": "0.0.4",
43 | "postcss-cli": "2.0.0"
44 | },
45 | "peerDependencies": {
46 | "react": ">=0.14.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docs/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import Cropper from '../lib/index.src.js'
4 |
5 | const WIDTH = 262;
6 | const HEIGHT = 147;
7 |
8 | let Wrapper = React.createClass({
9 | displayName: 'Wrapper',
10 |
11 | getInitialState () {
12 | return {
13 | image: null,
14 | previewUrl: null
15 | }
16 | },
17 |
18 | onChange (evt) {
19 | this.setState({
20 | image: evt.target.files[0]
21 | })
22 | },
23 |
24 | crop () {
25 | this.refs.crop.cropImage().then((image) => {
26 | this.setState({
27 | previewUrl: window.URL.createObjectURL(image)
28 | })
29 | })
30 | },
31 |
32 | clear () {
33 | this.refs.file.value = null
34 | this.setState({
35 | previewUrl: null,
36 | image: null
37 | })
38 | },
39 |
40 | imageLoaded (img) {
41 | if (img.naturalWidth && img.naturalWidth < WIDTH &&
42 | img.naturalHeight && img.naturalHeight < HEIGHT) {
43 | this.crop()
44 | }
45 | },
46 |
47 | render () {
48 | return (
49 |
50 |
51 |
52 | {this.state.image &&
53 |
54 |
60 |
}
61 |
62 |
63 |
64 |
65 |
66 | {this.state.previewUrl &&
67 |

}
68 |
69 | )
70 | }
71 | })
72 |
73 | ReactDOM.render(, document.querySelector('#view'))
74 |
--------------------------------------------------------------------------------
/cropper.css:
--------------------------------------------------------------------------------
1 | .Cropper {
2 | position: relative;
3 | display: inline-block;
4 | max-width:100%;
5 | max-height:100%;
6 | }
7 |
8 | .box {
9 | position: absolute;
10 | top: 0;
11 | left: 0;
12 | bottom: 0;
13 | right: 0;
14 | }
15 |
16 | .Cropper-box {
17 | position: absolute;
18 | top: 0;
19 | left: 0;
20 | bottom: 0;
21 | right: 0;
22 | cursor: move;
23 | border: #fff solid 1px;
24 | }
25 |
26 | .Cropper-canvas {
27 | visibility: hidden;
28 | position: absolute;
29 | }
30 |
31 | .Cropper-image {
32 | vertical-align: middle;
33 | max-width: 100%;
34 | position: relative;
35 | transform: translate(-50%, -50%);
36 | left: 50%;
37 | }
38 |
39 | .resize-handle {
40 | position: absolute;
41 | background-color: #ECEEEF;
42 | border: #8295AB solid 1px;
43 | width: 13px;
44 | height: 13px;
45 | z-index: 1;
46 | }
47 |
48 | .resize-handle-se {
49 | bottom: 0;
50 | right: 0;
51 | cursor: nwse-resize;
52 | transform: translate(50%, 50%);
53 | }
54 | .resize-handle-ne {
55 | right: 0;
56 | top: 0;
57 | cursor: nesw-resize;
58 | transform: translate(50%, -50%);
59 | }
60 | .resize-handle-sw {
61 | bottom: 0;
62 | left: 0;
63 | cursor: nesw-resize;
64 | transform: translate(-50%, 50%);
65 | }
66 | .resize-handle-nw {
67 | top: 0;
68 | bottom: 0;
69 | cursor: nwse-resize;
70 | transform: translate(-50%, -50%);
71 | }
72 |
73 | .DraggableResizable {
74 | position: relative;
75 | width: 100%;
76 | height: 100%;
77 | }
78 |
79 | .DraggableResizable-controls {
80 | border: 0;
81 | clip: rect(0 0 0 0);
82 | height: 1px;
83 | margin: -1px;
84 | overflow: hidden;
85 | padding: 0;
86 | position: absolute;
87 | width: 1px;
88 | }
89 |
90 | .DraggableResizable-top,
91 | .DraggableResizable-left,
92 | .DraggableResizable-bottom,
93 | .DraggableResizable-right {
94 | position: absolute;
95 | background-color: rgba(0,0,0,.7);
96 | }
97 |
98 | .DraggableResizable-top {
99 | top: 0;
100 | left: 0;
101 | right: 0;
102 | }
103 | .DraggableResizable-bottom {
104 | bottom: 0;
105 | left: 0;
106 | right: 0;
107 | }
108 | .DraggableResizable-left {
109 | left: 0;
110 | }
111 | .DraggableResizable-right {
112 | right: 0;
113 | }
114 |
--------------------------------------------------------------------------------
/lib/cropper.css:
--------------------------------------------------------------------------------
1 | .Cropper {
2 | position: relative;
3 | display: inline-block;
4 | max-width:100%;
5 | max-height:100%;
6 | }
7 |
8 | .box {
9 | position: absolute;
10 | top: 0;
11 | left: 0;
12 | bottom: 0;
13 | right: 0;
14 | }
15 |
16 | .Cropper-box {
17 | position: absolute;
18 | top: 0;
19 | left: 0;
20 | bottom: 0;
21 | right: 0;
22 | cursor: move;
23 | border: #fff solid 1px;
24 | }
25 |
26 | .Cropper-canvas {
27 | visibility: hidden;
28 | position: absolute;
29 | }
30 |
31 | .Cropper-image {
32 | vertical-align: middle;
33 | max-width: 100%;
34 | position: relative;
35 | transform: translate(-50%, -50%);
36 | left: 50%;
37 | }
38 |
39 | .resize-handle {
40 | position: absolute;
41 | background-color: #ECEEEF;
42 | border: #8295AB solid 1px;
43 | width: 13px;
44 | height: 13px;
45 | z-index: 1;
46 | }
47 |
48 | .resize-handle-se {
49 | bottom: 0;
50 | right: 0;
51 | cursor: nwse-resize;
52 | transform: translate(50%, 50%);
53 | }
54 | .resize-handle-ne {
55 | right: 0;
56 | top: 0;
57 | cursor: nesw-resize;
58 | transform: translate(50%, -50%);
59 | }
60 | .resize-handle-sw {
61 | bottom: 0;
62 | left: 0;
63 | cursor: nesw-resize;
64 | transform: translate(-50%, 50%);
65 | }
66 | .resize-handle-nw {
67 | top: 0;
68 | bottom: 0;
69 | cursor: nwse-resize;
70 | transform: translate(-50%, -50%);
71 | }
72 |
73 | .DraggableResizable {
74 | position: relative;
75 | width: 100%;
76 | height: 100%;
77 | }
78 |
79 | .DraggableResizable-controls {
80 | border: 0;
81 | clip: rect(0 0 0 0);
82 | height: 1px;
83 | margin: -1px;
84 | overflow: hidden;
85 | padding: 0;
86 | position: absolute;
87 | width: 1px;
88 | }
89 |
90 | .DraggableResizable-top,
91 | .DraggableResizable-left,
92 | .DraggableResizable-bottom,
93 | .DraggableResizable-right {
94 | position: absolute;
95 | background-color: rgba(0,0,0,.7);
96 | }
97 |
98 | .DraggableResizable-top {
99 | top: 0;
100 | left: 0;
101 | right: 0;
102 | }
103 | .DraggableResizable-bottom {
104 | bottom: 0;
105 | left: 0;
106 | right: 0;
107 | }
108 | .DraggableResizable-left {
109 | left: 0;
110 | }
111 | .DraggableResizable-right {
112 | right: 0;
113 | }
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #react-crop#
2 | An accessible image cropper where the image is stationary and a resizable, draggable box represents the cropped image
3 |
4 | For example usage check out the docs folder. Demo: http://instructure-react.github.io/react-crop/
5 |
6 | ###Basic usage###
7 |
8 | ``` javascript
9 | import React, { Component } from 'react';
10 |
11 | import Cropper from 'react-crop';
12 | import 'react-crop/css';
13 |
14 | // You'll need to use async functions
15 | import "babel-core/register";
16 | import "babel-polyfill";
17 |
18 | export default class MyComponent extends Component {
19 | constructor() {
20 | super();
21 |
22 | this.state = {
23 | image: null,
24 | previewImage: null
25 | };
26 | }
27 |
28 | onChange(evt) {
29 | this.setState({
30 | image: evt.target.files[0]
31 | })
32 | }
33 |
34 | async crop() {
35 | let image = await this.refs.crop.cropImage()
36 | this.setState({
37 | previewUrl: window.URL.createObjectURL(image)
38 | })
39 | }
40 |
41 | clear() {
42 | this.refs.file.value = null
43 | this.setState({
44 | previewUrl: null,
45 | image: null
46 | })
47 | }
48 |
49 | imageLoaded(img) {
50 | if (img.naturalWidth && img.naturalWidth < 262 &&
51 | img.naturalHeight && img.naturalHeight < 147) {
52 | this.crop()
53 | }
54 | }
55 |
56 | render() {
57 | return (
58 |
59 |
60 |
61 | {
62 |
63 | this.state.image &&
64 |
65 |
66 |
73 |
74 |
75 |
76 |
77 |
78 | }
79 |
80 | {
81 | this.state.previewUrl &&
82 |
83 |

84 | }
85 |
86 |
87 | );
88 | }
89 | }
90 | ```
91 |
92 | ###Props###
93 |
94 | ####`width`####
95 | This is the desired width that you would like the image to be cropped to. The width of the cropper will be scaled to fit this size. This prop also helps determine the minimum width that the cropper can be.
96 |
97 | ####`height`####
98 | This is the desired height that you would like the image to be cropped to. The height of the cropper will be scaled to fit this size. This prop also helps determine the minimum height that the cropper can be. The width and height aspect ratio will be preserved while resizing the cropper.
99 |
100 | ####`image`####
101 | A `blob` of the original image that you wish to crop.
102 |
103 | ####`widthLabel`####
104 | The label to use next to the width input used for keyboard users. This is especially useful if you need to localize the text. The default is "Width".
105 |
106 | ####`heightLabel`####
107 | The label to use next to the height input used for keyboard users. This is especially useful if you need to localize the text. The default is "Height".
108 |
109 | ####`offsetXLabel`####
110 | The label to use next to the offset X input used for keyboard users. This is especially useful if you need to localize the text. The default is "Offset X".
111 |
112 | ####`offsetYLabel`####
113 | The label to use next to the offset Y input used for keyboard users. This is especially useful if you need to localize the text. The default is "Offset Y".
114 |
115 | ###Running the Example###
116 | - Clone the repo
117 | - `npm i`
118 | - `npm run docs`
119 | - Visit `localhost:8080`
120 |
--------------------------------------------------------------------------------
/lib/index.src.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import DraggableResizableBox from './draggable-resizable-box'
3 | import toBlob from 'data-uri-to-blob'
4 | import './cropper.css'
5 |
6 | export default React.createClass({
7 | displayName: 'Cropper',
8 |
9 | propTypes: {
10 | width: React.PropTypes.number.isRequired,
11 | height: React.PropTypes.number.isRequired,
12 | center: React.PropTypes.bool,
13 | image: React.PropTypes.any,
14 | widthLabel: React.PropTypes.string,
15 | heightLabel: React.PropTypes.string,
16 | offsetXLabel: React.PropTypes.string,
17 | offsetYLabel: React.PropTypes.string,
18 | onImageLoaded: React.PropTypes.func,
19 | minConstraints: React.PropTypes.arrayOf(React.PropTypes.number)
20 | },
21 |
22 | getDefaultProps () {
23 | return {
24 | center: false,
25 | width: 'Width',
26 | height: 'Height',
27 | offsetXLabel: 'Offset X',
28 | offsetYLabel: 'Offset Y'
29 | }
30 | },
31 |
32 | getInitialState () {
33 | return {
34 | imageLoaded: false,
35 | width: this.props.width,
36 | height: this.props.height,
37 | url: window.URL.createObjectURL(this.props.image)
38 | }
39 | },
40 |
41 | componentWillReceiveProps (nextProps) {
42 | if (this.props.image !== nextProps.image) {
43 | this.setState({
44 | url: window.URL.createObjectURL(nextProps.image),
45 | imageLoaded: false
46 | })
47 | }
48 | },
49 |
50 | shouldComponentUpdate (nextProps, nextState) {
51 | let {image} = this.props
52 | return nextProps.image.size !== image.size ||
53 | nextProps.image.name !== image.name ||
54 | nextProps.image.type !== image.type ||
55 | nextState.imageLoaded !== this.state.imageLoaded
56 | },
57 |
58 | onLoad (evt) {
59 | let box = this.refs.box.getBoundingClientRect()
60 | this.setState({
61 | imageLoaded: true,
62 | width: box.width,
63 | height: box.height
64 | }, () => {
65 | let img = this.refs.image
66 | this.props.onImageLoaded && this.props.onImageLoaded(img)
67 | })
68 | },
69 |
70 | cropImage () {
71 | return new Promise((resolve, reject) => {
72 | let img = new Image()
73 | img.onload = () => {
74 | let canvas = this.refs.canvas
75 | let img = this.refs.image
76 | let ctx = canvas.getContext('2d')
77 | let [xScale, yScale] = [img.naturalWidth / this.state.width,
78 | img.naturalHeight / this.state.height]
79 |
80 | let imageOffsetX = xScale < 1 ? 0 : this.state.offset.left * xScale
81 | let imageOffsetY = yScale < 1 ? 0 : this.state.offset.top * yScale
82 | let imageWidth = xScale < 1 ? img.naturalWidth : this.state.dimensions.width * xScale
83 | let imageHeight = yScale < 1 ? img.naturalHeight : this.state.dimensions.height * yScale
84 |
85 | let canvasOffsetX = xScale < 1 ? Math.floor((this.state.dimensions.width - img.naturalWidth) / 2) : 0
86 | let canvasOffsetY = yScale < 1 ? Math.floor((this.state.dimensions.height - img.naturalHeight) / 2) : 0
87 | let canvasWidth = xScale < 1 ? img.naturalWidth : this.props.width
88 | let canvasHeight = yScale < 1 ? img.naturalHeight : this.props.height
89 |
90 | ctx.clearRect(0, 0, this.props.width, this.props.height)
91 | ctx.drawImage(img, imageOffsetX, imageOffsetY, imageWidth, imageHeight, canvasOffsetX, canvasOffsetY, canvasWidth, canvasHeight)
92 | resolve(toBlob(canvas.toDataURL()))
93 | }
94 | img.src = window.URL.createObjectURL(this.props.image)
95 | })
96 | },
97 |
98 | onChange (offset, dimensions) {
99 | this.setState({offset, dimensions})
100 | },
101 |
102 | render () {
103 | return (
104 |
111 |
117 |

123 | {this.state.imageLoaded &&
124 |
}
138 |
139 | )
140 | }
141 | })
142 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _draggableResizableBox = require('./draggable-resizable-box');
12 |
13 | var _draggableResizableBox2 = _interopRequireDefault(_draggableResizableBox);
14 |
15 | var _dataUriToBlob = require('data-uri-to-blob');
16 |
17 | var _dataUriToBlob2 = _interopRequireDefault(_dataUriToBlob);
18 |
19 | require('./cropper.css');
20 |
21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22 |
23 | exports.default = _react2.default.createClass({
24 | displayName: 'Cropper',
25 |
26 | propTypes: {
27 | width: _react2.default.PropTypes.number.isRequired,
28 | height: _react2.default.PropTypes.number.isRequired,
29 | center: _react2.default.PropTypes.bool,
30 | image: _react2.default.PropTypes.any,
31 | widthLabel: _react2.default.PropTypes.string,
32 | heightLabel: _react2.default.PropTypes.string,
33 | offsetXLabel: _react2.default.PropTypes.string,
34 | offsetYLabel: _react2.default.PropTypes.string,
35 | onImageLoaded: _react2.default.PropTypes.func,
36 | minConstraints: _react2.default.PropTypes.arrayOf(_react2.default.PropTypes.number)
37 | },
38 |
39 | getDefaultProps: function getDefaultProps() {
40 | return {
41 | center: false,
42 | width: 'Width',
43 | height: 'Height',
44 | offsetXLabel: 'Offset X',
45 | offsetYLabel: 'Offset Y'
46 | };
47 | },
48 | getInitialState: function getInitialState() {
49 | return {
50 | imageLoaded: false,
51 | width: this.props.width,
52 | height: this.props.height,
53 | url: window.URL.createObjectURL(this.props.image)
54 | };
55 | },
56 | componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
57 | if (this.props.image !== nextProps.image) {
58 | this.setState({
59 | url: window.URL.createObjectURL(nextProps.image),
60 | imageLoaded: false
61 | });
62 | }
63 | },
64 | shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) {
65 | var image = this.props.image;
66 |
67 | return nextProps.image.size !== image.size || nextProps.image.name !== image.name || nextProps.image.type !== image.type || nextState.imageLoaded !== this.state.imageLoaded;
68 | },
69 | onLoad: function onLoad(evt) {
70 | var _this = this;
71 |
72 | var box = this.refs.box.getBoundingClientRect();
73 | this.setState({
74 | imageLoaded: true,
75 | width: box.width,
76 | height: box.height
77 | }, function () {
78 | var img = _this.refs.image;
79 | _this.props.onImageLoaded && _this.props.onImageLoaded(img);
80 | });
81 | },
82 | cropImage: function cropImage() {
83 | var _this2 = this;
84 |
85 | return new Promise(function (resolve, reject) {
86 | var img = new Image();
87 | img.onload = function () {
88 | var canvas = _this2.refs.canvas;
89 | var img = _this2.refs.image;
90 | var ctx = canvas.getContext('2d');
91 | var xScale = img.naturalWidth / _this2.state.width,
92 | yScale = img.naturalHeight / _this2.state.height;
93 |
94 |
95 | var imageOffsetX = xScale < 1 ? 0 : _this2.state.offset.left * xScale;
96 | var imageOffsetY = yScale < 1 ? 0 : _this2.state.offset.top * yScale;
97 | var imageWidth = xScale < 1 ? img.naturalWidth : _this2.state.dimensions.width * xScale;
98 | var imageHeight = yScale < 1 ? img.naturalHeight : _this2.state.dimensions.height * yScale;
99 |
100 | var canvasOffsetX = xScale < 1 ? Math.floor((_this2.state.dimensions.width - img.naturalWidth) / 2) : 0;
101 | var canvasOffsetY = yScale < 1 ? Math.floor((_this2.state.dimensions.height - img.naturalHeight) / 2) : 0;
102 | var canvasWidth = xScale < 1 ? img.naturalWidth : _this2.props.width;
103 | var canvasHeight = yScale < 1 ? img.naturalHeight : _this2.props.height;
104 |
105 | ctx.clearRect(0, 0, _this2.props.width, _this2.props.height);
106 | ctx.drawImage(img, imageOffsetX, imageOffsetY, imageWidth, imageHeight, canvasOffsetX, canvasOffsetY, canvasWidth, canvasHeight);
107 | resolve((0, _dataUriToBlob2.default)(canvas.toDataURL()));
108 | };
109 | img.src = window.URL.createObjectURL(_this2.props.image);
110 | });
111 | },
112 | onChange: function onChange(offset, dimensions) {
113 | this.setState({ offset: offset, dimensions: dimensions });
114 | },
115 | render: function render() {
116 | return _react2.default.createElement(
117 | 'div',
118 | {
119 | ref: 'box',
120 | className: 'Cropper',
121 | style: {
122 | minWidth: this.props.width,
123 | minHeight: this.props.height
124 | } },
125 | _react2.default.createElement('canvas', {
126 | className: 'Cropper-canvas',
127 | ref: 'canvas',
128 | width: this.props.width,
129 | height: this.props.height }),
130 | _react2.default.createElement('img', {
131 | ref: 'image',
132 | src: this.state.url,
133 | className: 'Cropper-image',
134 | onLoad: this.onLoad,
135 | style: { top: this.state.height / 2 } }),
136 | this.state.imageLoaded && _react2.default.createElement(
137 | 'div',
138 | { className: 'box' },
139 | _react2.default.createElement(
140 | _draggableResizableBox2.default,
141 | {
142 | aspectRatio: this.props.width / this.props.height,
143 | width: this.state.width,
144 | height: this.state.height,
145 | minConstraints: this.props.minConstraints,
146 | onChange: this.onChange,
147 | widthLabel: this.props.widthLabel,
148 | heightLabel: this.props.heightLabel,
149 | offsetXLabel: this.props.offsetXLabel,
150 | offsetYLabel: this.props.offsetYLabel },
151 | _react2.default.createElement('div', { className: 'Cropper-box' })
152 | )
153 | )
154 | );
155 | }
156 | });
157 |
--------------------------------------------------------------------------------
/lib/draggable-resizable-box.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default React.createClass({
4 | displayName: 'DraggableResizableBox',
5 |
6 | propTypes: {
7 | aspectRatio: React.PropTypes.number.isRequired,
8 | width: React.PropTypes.number.isRequired,
9 | height: React.PropTypes.number.isRequired,
10 | onChange: React.PropTypes.func,
11 | offset: React.PropTypes.array,
12 | minConstraints: React.PropTypes.array,
13 | children: React.PropTypes.node,
14 | widthLabel: React.PropTypes.string,
15 | heightLabel: React.PropTypes.string,
16 | offsetXLabel: React.PropTypes.string,
17 | offsetYLabel: React.PropTypes.string
18 | },
19 |
20 | getDefaultProps () {
21 | return {
22 | widthLabel: 'Width',
23 | heightLabel: 'Height',
24 | offsetXLabel: 'Offset X',
25 | offsetYLabel: 'Offset Y'
26 | }
27 | },
28 |
29 | getInitialState () {
30 | let [width, height] = this.preserveAspectRatio(this.props.width, this.props.height)
31 | let centerYOffset = (this.props.height - height) / 2
32 | let centerXOffset = (this.props.width - width) / 2
33 | return {
34 | top: centerYOffset,
35 | left: centerXOffset,
36 | bottom: centerYOffset,
37 | right: centerXOffset,
38 | width: width,
39 | height: height
40 | }
41 | },
42 |
43 | componentDidMount () {
44 | document.addEventListener('mousemove', this.eventMove)
45 | document.addEventListener('mouseup', this.eventEnd)
46 | document.addEventListener('touchmove', this.eventMove)
47 | document.addEventListener('touchend', this.eventEnd)
48 | document.addEventListener('keydown', this.handleKey)
49 | this.props.onChange({
50 | top: this.state.top,
51 | left: this.state.left
52 | }, {
53 | width: this.state.width,
54 | height: this.state.height
55 | })
56 | },
57 |
58 | componentWillUnmount () {
59 | document.removeEventListener('mousemove', this.eventMove)
60 | document.removeEventListener('mouseup', this.eventEnd)
61 | document.removeEventListener('touchmove', this.eventMove)
62 | document.removeEventListener('touchend', this.eventEnd)
63 | document.removeEventListener('keydown', this.handleKey)
64 | },
65 |
66 | calculateDimensions ({top, left, bottom, right}) {
67 | return {width: this.props.width - left - right, height: this.props.height - top - bottom}
68 | },
69 |
70 | // If you do this, be careful of constraints
71 | preserveAspectRatio (width, height) {
72 | if(this.props.minConstraints) {
73 | width = Math.max(width, this.props.minConstraints[0])
74 | height = Math.max(height, this.props.minConstraints[1])
75 | }
76 | const currentAspectRatio = width / height
77 |
78 | if (currentAspectRatio < this.props.aspectRatio) {
79 | return [width, width / this.props.aspectRatio]
80 | } else if (currentAspectRatio > this.props.aspectRatio) {
81 | return [height * this.props.aspectRatio, height]
82 | } else {
83 | return [width, height]
84 | }
85 | },
86 |
87 | constrainBoundary (side) {
88 | return side < 0 ? 0 : side
89 | },
90 |
91 | getClientCoordinates (evt) {
92 | return evt.touches ? {
93 | clientX: evt.touches[0].clientX,
94 | clientY: evt.touches[0].clientY
95 | } :
96 | {
97 | clientX: evt.clientX,
98 | clientY: evt.clientY
99 | }
100 | },
101 |
102 | eventMove (evt) {
103 | if (this.state.resizing) {
104 | this.onResize(evt)
105 | } else if (this.state.moving) {
106 | this.eventMoveBox(evt)
107 | }
108 | },
109 |
110 | eventEnd (evt) {
111 | if (this.state.resizing) {
112 | this.stopResize(evt)
113 | } else if (this.state.moving) {
114 | this.stopMove(evt)
115 | }
116 | },
117 |
118 | // Resize methods
119 | startResize (corner, event) {
120 | event.stopPropagation()
121 | event.preventDefault()
122 | this.setState({
123 | resizing: true,
124 | corner
125 | })
126 | },
127 |
128 | stopResize () {
129 | this.setState({resizing: false})
130 | },
131 |
132 | // resize strategies
133 | nw (mousePos, boxPos) {
134 | let pos = Object.assign({}, this.state, {
135 | top: this.constrainBoundary(mousePos.clientY - boxPos.top),
136 | left: this.constrainBoundary(mousePos.clientX - boxPos.left)
137 | })
138 | let dimensions = this.calculateDimensions(pos)
139 | let [width, height] = this.preserveAspectRatio(dimensions.width, dimensions.height)
140 | pos.top = this.props.height - pos.bottom - height
141 | pos.left = this.props.width - pos.right - width
142 | return pos
143 | },
144 | ne (mousePos, boxPos) {
145 | let pos = Object.assign({}, this.state, {
146 | top: this.constrainBoundary(mousePos.clientY - boxPos.top),
147 | right: this.constrainBoundary(boxPos.right - mousePos.clientX)
148 | })
149 | let dimensions = this.calculateDimensions(pos)
150 | let [width, height] = this.preserveAspectRatio(dimensions.width, dimensions.height)
151 | pos.top = this.props.height - pos.bottom - height
152 | pos.right = this.props.width - pos.left - width
153 | return pos
154 | },
155 | se (mousePos, boxPos) {
156 | let pos = Object.assign({}, this.state, {
157 | bottom: this.constrainBoundary(boxPos.bottom - mousePos.clientY),
158 | right: this.constrainBoundary(boxPos.right - mousePos.clientX)
159 | })
160 | let dimensions = this.calculateDimensions(pos)
161 | let [width, height] = this.preserveAspectRatio(dimensions.width, dimensions.height)
162 | pos.bottom = this.props.height - pos.top - height
163 | pos.right = this.props.width - pos.left - width
164 | return pos
165 | },
166 | sw (mousePos, boxPos) {
167 | let pos = Object.assign({}, this.state, {
168 | bottom: this.constrainBoundary(boxPos.bottom - mousePos.clientY),
169 | left: this.constrainBoundary(mousePos.clientX - boxPos.left)
170 | })
171 | let dimensions = this.calculateDimensions(pos)
172 | let [width, height] = this.preserveAspectRatio(dimensions.width, dimensions.height)
173 | pos.bottom = this.props.height - pos.top - height
174 | pos.left = this.props.width - pos.right - width
175 | return pos
176 | },
177 |
178 | onResize (event) {
179 | let box = this.refs.box.parentElement.parentElement.getBoundingClientRect()
180 | let coordinates = this.getClientCoordinates(event)
181 | let position = this[this.state.corner](coordinates, box)
182 | this.resize(position, coordinates)
183 | },
184 |
185 | controlsResize (event) {
186 | let box = this.refs.box.parentElement.parentElement.getBoundingClientRect()
187 | let width = event.target.name === 'width' ? +event.target.value : +event.target.value * this.props.aspectRatio
188 | let height = event.target.name === 'height' ? +event.target.value : +event.target.value / this.props.aspectRatio
189 | let dimensions = this.preserveAspectRatio(width, height)
190 | width = dimensions[0]
191 | height = dimensions[1]
192 |
193 | if (width > box.width - this.state.left ||
194 | height > box.height - this.state.top) return
195 |
196 | let widthDifference = this.state.width - width
197 | let heightDifference = this.state.height - height
198 | let pos = Object.assign({}, this.state, {
199 | right: this.state.right + widthDifference,
200 | bottom: this.state.bottom + heightDifference
201 | })
202 | let coordinates = {
203 | clientX: box.right - pos.right,
204 | clientY: box.bottom - pos.bottom
205 | }
206 |
207 | this.resize(pos, coordinates)
208 | },
209 |
210 | resize (position, coordinates) {
211 | let dimensions = this.calculateDimensions(position)
212 | var widthChanged = dimensions.width !== this.state.width, heightChanged = dimensions.height !== this.state.height
213 | if (!widthChanged && !heightChanged) return
214 |
215 | this.setState(Object.assign({}, coordinates, position, dimensions), () => {
216 | this.props.onChange({
217 | top: position.top,
218 | left: position.left
219 | }, dimensions)
220 | })
221 | },
222 |
223 | // Move methods
224 | startMove (evt) {
225 | let {clientX, clientY} = this.getClientCoordinates(evt)
226 | this.setState({
227 | moving: true,
228 | clientX: clientX,
229 | clientY: clientY
230 | })
231 | },
232 |
233 | stopMove (evt) {
234 | this.setState({
235 | moving: false
236 | })
237 | },
238 |
239 | eventMoveBox (evt) {
240 | evt.preventDefault()
241 | let {clientX, clientY} = this.getClientCoordinates(evt)
242 | let movedX = clientX - this.state.clientX
243 | let movedY = clientY - this.state.clientY
244 |
245 | this.moveBox(clientX, clientY, movedX, movedY)
246 | },
247 |
248 | controlsMoveBox (evt) {
249 | let movedX = evt.target.name === 'x' ? evt.target.value - this.state.left : 0
250 | let movedY = evt.target.name === 'y' ? evt.target.value - this.state.top : 0
251 | this.moveBox(0, 0, movedX, movedY)
252 | },
253 |
254 | moveBox (clientX, clientY, movedX, movedY) {
255 | let position = {
256 | top: this.constrainBoundary(this.state.top + movedY),
257 | left: this.constrainBoundary(this.state.left + movedX),
258 | bottom: this.constrainBoundary(this.state.bottom - movedY),
259 | right: this.constrainBoundary(this.state.right - movedX)
260 | }
261 |
262 | if (!position.top) {
263 | position.bottom = this.props.height - this.state.height
264 | }
265 | if (!position.bottom) {
266 | position.top = this.props.height - this.state.height
267 | }
268 | if (!position.left) {
269 | position.right = this.props.width - this.state.width
270 | }
271 | if (!position.right) {
272 | position.left = this.props.width - this.state.width
273 | }
274 |
275 | this.setState(Object.assign({}, {
276 | clientX: clientX,
277 | clientY: clientY
278 | }, position), () => {
279 | this.props.onChange({
280 | top: position.top,
281 | left: position.left
282 | }, this.calculateDimensions(position))
283 | })
284 | },
285 |
286 | keyboardResize (change) {
287 | if (this.state.right - change < 0) { return }
288 | if (this.state.bottom - change < 0) { return }
289 |
290 | const [width, height] = this.preserveAspectRatio(
291 | this.state.width + change,
292 | this.state.height + change
293 | )
294 | const widthChange = width - this.state.width
295 | const heightChange = height - this.state.height
296 |
297 | this.setState({
298 | bottom: this.state.bottom - heightChange,
299 | right: this.state.right - widthChange,
300 | width,
301 | height
302 | })
303 | },
304 |
305 | handleKey (event) {
306 | // safari doesn't support event.key, so fall back to keyCode
307 | if (event.shiftKey) {
308 | if (event.key === 'ArrowUp' || event.keyCode === 38) {
309 | this.keyboardResize(-10)
310 | event.preventDefault()
311 | } else if (event.key === 'ArrowDown' || event.keyCode === 40) {
312 | this.keyboardResize(10)
313 | event.preventDefault()
314 | } else if (event.key === 'ArrowLeft' || event.keyCode === 37) {
315 | this.keyboardResize(-10)
316 | event.preventDefault()
317 | } else if (event.key === 'ArrowRight' || event.keyCode === 39) {
318 | this.keyboardResize(10)
319 | event.preventDefault()
320 | }
321 | } else {
322 | if (event.key === 'ArrowUp' || event.keyCode === 38) {
323 | this.moveBox(this.state.clientX, this.state.clientY, 0, -10)
324 | event.preventDefault()
325 | } else if (event.key === 'ArrowDown' || event.keyCode === 40) {
326 | this.moveBox(this.state.clientX, this.state.clientY, 0, 10)
327 | event.preventDefault()
328 | } else if (event.key === 'ArrowLeft' || event.keyCode === 37) {
329 | this.moveBox(this.state.clientX, this.state.clientY, -10, 0)
330 | event.preventDefault()
331 | } else if (event.key === 'ArrowRight' || event.keyCode === 39) {
332 | this.moveBox(this.state.clientX, this.state.clientY, 10, 0)
333 | event.preventDefault()
334 | }
335 | }
336 | },
337 |
338 | render () {
339 | let style = {
340 | position: 'absolute',
341 | top: this.state.top,
342 | left: this.state.left,
343 | right: this.state.right,
344 | bottom: this.state.bottom
345 | }
346 | let {width, height} = this.calculateDimensions(this.state)
347 | let topStyle = {
348 | height: this.state.top
349 | }
350 | let bottomStyle = {
351 | height: this.state.bottom
352 | }
353 | let leftStyle = {
354 | top: this.state.top,
355 | right: width + this.state.right,
356 | bottom: this.state.bottom
357 | }
358 | let rightStyle = {
359 | top: this.state.top,
360 | left: width + this.state.left,
361 | bottom: this.state.bottom
362 | }
363 |
364 | return (
365 |
366 |
367 |
376 |
385 |
394 |
403 |
404 |
405 |
406 |
407 | {this.props.children}
408 |
411 |
412 |
415 |
416 |
419 |
420 |
423 |
424 |
425 |
426 |
427 |
428 | )
429 | }
430 | })
431 |
--------------------------------------------------------------------------------
/dist/react-crop.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):"object"==typeof exports?exports.ReactCrop=e(require("react")):t.ReactCrop=e(t.react)}(this,function(t){return function(t){function e(s){if(i[s])return i[s].exports;var n=i[s]={exports:{},id:s,loaded:!1};return t[s].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var i={};return e.m=t,e.c=i,e.p="/",e(0)}([function(t,e,i){"use strict";function s(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var n=i(1),o=s(n),r=i(2),a=s(r),h=i(3),l=s(h);i(4),e["default"]=o["default"].createClass({displayName:"Cropper",propTypes:{width:o["default"].PropTypes.number.isRequired,height:o["default"].PropTypes.number.isRequired,center:o["default"].PropTypes.bool,image:o["default"].PropTypes.any,widthLabel:o["default"].PropTypes.string,heightLabel:o["default"].PropTypes.string,offsetXLabel:o["default"].PropTypes.string,offsetYLabel:o["default"].PropTypes.string,onImageLoaded:o["default"].PropTypes.func,minConstraints:o["default"].PropTypes.arrayOf(o["default"].PropTypes.number)},getDefaultProps:function(){return{center:!1,width:"Width",height:"Height",offsetXLabel:"Offset X",offsetYLabel:"Offset Y"}},getInitialState:function(){return{imageLoaded:!1,width:this.props.width,height:this.props.height,url:window.URL.createObjectURL(this.props.image)}},componentWillReceiveProps:function(t){this.props.image!==t.image&&this.setState({url:window.URL.createObjectURL(t.image),imageLoaded:!1})},shouldComponentUpdate:function(t,e){var i=this.props.image;return t.image.size!==i.size||t.image.name!==i.name||t.image.type!==i.type||e.imageLoaded!==this.state.imageLoaded},onLoad:function(t){var e=this,i=this.refs.box.getBoundingClientRect();this.setState({imageLoaded:!0,width:i.width,height:i.height},function(){var t=e.refs.image;e.props.onImageLoaded&&e.props.onImageLoaded(t)})},cropImage:function(){var t=this;return new Promise(function(e,i){var s=new Image;s.onload=function(){var i=t.refs.canvas,s=t.refs.image,n=i.getContext("2d"),o=s.naturalWidth/t.state.width,r=s.naturalHeight/t.state.height,a=1>o?0:t.state.offset.left*o,h=1>r?0:t.state.offset.top*r,p=1>o?s.naturalWidth:t.state.dimensions.width*o,u=1>r?s.naturalHeight:t.state.dimensions.height*r,d=1>o?Math.floor((t.state.dimensions.width-s.naturalWidth)/2):0,c=1>r?Math.floor((t.state.dimensions.height-s.naturalHeight)/2):0,f=1>o?s.naturalWidth:t.props.width,g=1>r?s.naturalHeight:t.props.height;n.clearRect(0,0,t.props.width,t.props.height),n.drawImage(s,a,h,p,u,d,c,f,g),e(l["default"](i.toDataURL()))},s.src=window.URL.createObjectURL(t.props.image)})},onChange:function(t,e){this.setState({offset:t,dimensions:e})},render:function(){return o["default"].createElement("div",{ref:"box",className:"Cropper",style:{minWidth:this.props.width,minHeight:this.props.height}},o["default"].createElement("canvas",{className:"Cropper-canvas",ref:"canvas",width:this.props.width,height:this.props.height}),o["default"].createElement("img",{ref:"image",src:this.state.url,className:"Cropper-image",onLoad:this.onLoad,style:{top:this.state.height/2}}),this.state.imageLoaded&&o["default"].createElement("div",{className:"box"},o["default"].createElement(a["default"],{aspectRatio:this.props.width/this.props.height,width:this.state.width,height:this.state.height,minConstraints:this.props.minConstraints,onChange:this.onChange,widthLabel:this.props.widthLabel,heightLabel:this.props.heightLabel,offsetXLabel:this.props.offsetXLabel,offsetYLabel:this.props.offsetYLabel},o["default"].createElement("div",{className:"Cropper-box"}))))}})},function(e,i){e.exports=t},function(t,e,i){"use strict";function s(t){return t&&t.__esModule?t:{"default":t}}Object.defineProperty(e,"__esModule",{value:!0});var n=Object.assign||function(t){for(var e=1;ethis.props.aspectRatio?[e*this.props.aspectRatio,e]:[t,e]},constrainBoundary:function(t){return 0>t?0:t},getClientCoordinates:function(t){return t.touches?{clientX:t.touches[0].clientX,clientY:t.touches[0].clientY}:{clientX:t.clientX,clientY:t.clientY}},eventMove:function(t){this.state.resizing?this.onResize(t):this.state.moving&&this.eventMoveBox(t)},eventEnd:function(t){this.state.resizing?this.stopResize(t):this.state.moving&&this.stopMove(t)},startResize:function(t,e){e.stopPropagation(),e.preventDefault(),this.setState({resizing:!0,corner:t})},stopResize:function(){this.setState({resizing:!1})},nw:function(t,e){var i=n({},this.state,{top:this.constrainBoundary(t.clientY-e.top),left:this.constrainBoundary(t.clientX-e.left)}),s=this.calculateDimensions(i),r=this.preserveAspectRatio(s.width,s.height),a=o(r,2),h=a[0],l=a[1];return i.top=this.props.height-i.bottom-l,i.left=this.props.width-i.right-h,i},ne:function(t,e){var i=n({},this.state,{top:this.constrainBoundary(t.clientY-e.top),right:this.constrainBoundary(e.right-t.clientX)}),s=this.calculateDimensions(i),r=this.preserveAspectRatio(s.width,s.height),a=o(r,2),h=a[0],l=a[1];return i.top=this.props.height-i.bottom-l,i.right=this.props.width-i.left-h,i},se:function(t,e){var i=n({},this.state,{bottom:this.constrainBoundary(e.bottom-t.clientY),right:this.constrainBoundary(e.right-t.clientX)}),s=this.calculateDimensions(i),r=this.preserveAspectRatio(s.width,s.height),a=o(r,2),h=a[0],l=a[1];return i.bottom=this.props.height-i.top-l,i.right=this.props.width-i.left-h,i},sw:function(t,e){var i=n({},this.state,{bottom:this.constrainBoundary(e.bottom-t.clientY),left:this.constrainBoundary(t.clientX-e.left)}),s=this.calculateDimensions(i),r=this.preserveAspectRatio(s.width,s.height),a=o(r,2),h=a[0],l=a[1];return i.bottom=this.props.height-i.top-l,i.left=this.props.width-i.right-h,i},onResize:function(t){var e=this.refs.box.parentElement.parentElement.getBoundingClientRect(),i=this.getClientCoordinates(t),s=this[this.state.corner](i,e);this.resize(s,i)},controlsResize:function(t){var e=this.refs.box.parentElement.parentElement.getBoundingClientRect(),i="width"===t.target.name?+t.target.value:+t.target.value*this.props.aspectRatio,s="height"===t.target.name?+t.target.value:+t.target.value/this.props.aspectRatio,o=this.preserveAspectRatio(i,s);if(i=o[0],s=o[1],!(i>e.width-this.state.left||s>e.height-this.state.top)){var r=this.state.width-i,a=this.state.height-s,h=n({},this.state,{right:this.state.right+r,bottom:this.state.bottom+a}),l={clientX:e.right-h.right,clientY:e.bottom-h.bottom};this.resize(h,l)}},resize:function(t,e){var i=this,s=this.calculateDimensions(t),o=s.width!==this.state.width,r=s.height!==this.state.height;(o||r)&&this.setState(n({},e,t,s),function(){i.props.onChange({top:t.top,left:t.left},s)})},startMove:function(t){var e=this.getClientCoordinates(t),i=e.clientX,s=e.clientY;this.setState({moving:!0,clientX:i,clientY:s})},stopMove:function(t){this.setState({moving:!1})},eventMoveBox:function(t){t.preventDefault();var e=this.getClientCoordinates(t),i=e.clientX,s=e.clientY,n=i-this.state.clientX,o=s-this.state.clientY;this.moveBox(i,s,n,o)},controlsMoveBox:function(t){var e="x"===t.target.name?t.target.value-this.state.left:0,i="y"===t.target.name?t.target.value-this.state.top:0;this.moveBox(0,0,e,i)},moveBox:function(t,e,i,s){var o=this,r={top:this.constrainBoundary(this.state.top+s),left:this.constrainBoundary(this.state.left+i),bottom:this.constrainBoundary(this.state.bottom-s),right:this.constrainBoundary(this.state.right-i)};r.top||(r.bottom=this.props.height-this.state.height),r.bottom||(r.top=this.props.height-this.state.height),r.left||(r.right=this.props.width-this.state.width),r.right||(r.left=this.props.width-this.state.width),this.setState(n({},{clientX:t,clientY:e},r),function(){o.props.onChange({top:r.top,left:r.left},o.calculateDimensions(r))})},keyboardResize:function(t){if(!(this.state.right-t<0||this.state.bottom-t<0)){var e=this.preserveAspectRatio(this.state.width+t,this.state.height+t),i=o(e,2),s=i[0],n=i[1],r=s-this.state.width,a=n-this.state.height;this.setState({bottom:this.state.bottom-a,right:this.state.right-r,width:s,height:n})}},handleKey:function(t){t.shiftKey?"ArrowUp"===t.key||38===t.keyCode?(this.keyboardResize(-10),t.preventDefault()):"ArrowDown"===t.key||40===t.keyCode?(this.keyboardResize(10),t.preventDefault()):"ArrowLeft"===t.key||37===t.keyCode?(this.keyboardResize(-10),t.preventDefault()):("ArrowRight"===t.key||39===t.keyCode)&&(this.keyboardResize(10),t.preventDefault()):"ArrowUp"===t.key||38===t.keyCode?(this.moveBox(this.state.clientX,this.state.clientY,0,-10),t.preventDefault()):"ArrowDown"===t.key||40===t.keyCode?(this.moveBox(this.state.clientX,this.state.clientY,0,10),t.preventDefault()):"ArrowLeft"===t.key||37===t.keyCode?(this.moveBox(this.state.clientX,this.state.clientY,-10,0),t.preventDefault()):("ArrowRight"===t.key||39===t.keyCode)&&(this.moveBox(this.state.clientX,this.state.clientY,10,0),t.preventDefault())},render:function(){var t={position:"absolute",top:this.state.top,left:this.state.left,right:this.state.right,bottom:this.state.bottom},e=this.calculateDimensions(this.state),i=e.width,s=e.height,n={height:this.state.top},o={height:this.state.bottom},r={top:this.state.top,right:i+this.state.right,bottom:this.state.bottom},h={top:this.state.top,left:i+this.state.left,bottom:this.state.bottom};return a["default"].createElement("div",{ref:"box",className:"DraggableResizable"},a["default"].createElement("div",{className:"DraggableResizable-controls"},a["default"].createElement("label",null,this.props.offsetXLabel,a["default"].createElement("input",{name:"x",value:Math.round(this.state.left),onChange:this.controlsMoveBox,tabIndex:"-1",type:"number"})),a["default"].createElement("label",null,this.props.offsetYLabel,a["default"].createElement("input",{name:"y",value:Math.round(this.state.top),onChange:this.controlsMoveBox,tabIndex:"-1",type:"number"})),a["default"].createElement("label",null,this.props.widthLabel,a["default"].createElement("input",{name:"width",value:Math.round(i),type:"number",tabIndex:"-1",onChange:this.controlsResize})),a["default"].createElement("label",null,this.props.heightLabel,a["default"].createElement("input",{value:Math.round(s),type:"number",name:"height",tabIndex:"-1",onChange:this.controlsResize}))),a["default"].createElement("div",{className:"DraggableResizable-top",style:n}),a["default"].createElement("div",{className:"DraggableResizable-left",style:r}),a["default"].createElement("div",{style:t,onMouseDown:this.startMove,onTouchStart:this.startMove},this.props.children,a["default"].createElement("div",{className:"resize-handle resize-handle-se",onMouseDown:this.startResize.bind(null,"se"),onTouchStart:this.startResize.bind(null,"se")}),a["default"].createElement("div",{className:"resize-handle resize-handle-ne",onMouseDown:this.startResize.bind(null,"ne"),onTouchStart:this.startResize.bind(null,"ne")}),a["default"].createElement("div",{className:"resize-handle resize-handle-sw",onMouseDown:this.startResize.bind(null,"sw"),onTouchStart:this.startResize.bind(null,"sw")}),a["default"].createElement("div",{className:"resize-handle resize-handle-nw",onMouseDown:this.startResize.bind(null,"nw"),onTouchStart:this.startResize.bind(null,"nw")})),a["default"].createElement("div",{className:"DraggableResizable-right",style:h}),a["default"].createElement("div",{className:"DraggableResizable-bottom",style:o}))}})},function(t,e){function i(t){return t.split(";")[0].slice(5)}var s=window.Blob,n=100==new s([new Uint8Array(100)]).size;t.exports=function(t){for(var e=t.split(",")[1],o=atob(e),r=new ArrayBuffer(o.length),a=new Uint8Array(r),h=0;h=0&&y.splice(e,1)}function a(t){var e=document.createElement("style");return e.type="text/css",o(t,e),e}function h(t){var e=document.createElement("link");return e.rel="stylesheet",o(t,e),e}function l(t,e){var i,s,n;if(e.singleton){var o=b++;i=v||(v=a(e)),s=p.bind(null,i,o,!1),n=p.bind(null,i,o,!0)}else t.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(i=h(e),s=d.bind(null,i),n=function(){r(i),i.href&&URL.revokeObjectURL(i.href)}):(i=a(e),s=u.bind(null,i),n=function(){r(i)});return s(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;s(t=e)}else n()}}function p(t,e,i,s){var n=i?"":s.css;if(t.styleSheet)t.styleSheet.cssText=w(e,n);else{var o=document.createTextNode(n),r=t.childNodes;r[e]&&t.removeChild(r[e]),r.length?t.insertBefore(o,r[e]):t.appendChild(o)}}function u(t,e){var i=e.css,s=e.media;if(s&&t.setAttribute("media",s),t.styleSheet)t.styleSheet.cssText=i;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(i))}}function d(t,e){var i=e.css,s=e.sourceMap;s&&(i+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(s))))+" */");var n=new Blob([i],{type:"text/css"}),o=t.href;t.href=URL.createObjectURL(n),o&&URL.revokeObjectURL(o)}var c={},f=function(t){var e;return function(){return"undefined"==typeof e&&(e=t.apply(this,arguments)),e}},g=f(function(){return/msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())}),m=f(function(){return document.head||document.getElementsByTagName("head")[0]}),v=null,b=0,y=[];t.exports=function(t,e){e=e||{},"undefined"==typeof e.singleton&&(e.singleton=g()),"undefined"==typeof e.insertAt&&(e.insertAt="bottom");var i=n(t);return s(i,e),function(t){for(var o=[],r=0;r this.props.aspectRatio) {
101 | return [height * this.props.aspectRatio, height];
102 | } else {
103 | return [width, height];
104 | }
105 | },
106 | constrainBoundary: function constrainBoundary(side) {
107 | return side < 0 ? 0 : side;
108 | },
109 | getClientCoordinates: function getClientCoordinates(evt) {
110 | return evt.touches ? {
111 | clientX: evt.touches[0].clientX,
112 | clientY: evt.touches[0].clientY
113 | } : {
114 | clientX: evt.clientX,
115 | clientY: evt.clientY
116 | };
117 | },
118 | eventMove: function eventMove(evt) {
119 | if (this.state.resizing) {
120 | this.onResize(evt);
121 | } else if (this.state.moving) {
122 | this.eventMoveBox(evt);
123 | }
124 | },
125 | eventEnd: function eventEnd(evt) {
126 | if (this.state.resizing) {
127 | this.stopResize(evt);
128 | } else if (this.state.moving) {
129 | this.stopMove(evt);
130 | }
131 | },
132 |
133 |
134 | // Resize methods
135 | startResize: function startResize(corner, event) {
136 | event.stopPropagation();
137 | event.preventDefault();
138 | this.setState({
139 | resizing: true,
140 | corner: corner
141 | });
142 | },
143 | stopResize: function stopResize() {
144 | this.setState({ resizing: false });
145 | },
146 |
147 |
148 | // resize strategies
149 | nw: function nw(mousePos, boxPos) {
150 | var pos = _extends({}, this.state, {
151 | top: this.constrainBoundary(mousePos.clientY - boxPos.top),
152 | left: this.constrainBoundary(mousePos.clientX - boxPos.left)
153 | });
154 | var dimensions = this.calculateDimensions(pos);
155 |
156 | var _preserveAspectRatio3 = this.preserveAspectRatio(dimensions.width, dimensions.height),
157 | _preserveAspectRatio4 = _slicedToArray(_preserveAspectRatio3, 2),
158 | width = _preserveAspectRatio4[0],
159 | height = _preserveAspectRatio4[1];
160 |
161 | pos.top = this.props.height - pos.bottom - height;
162 | pos.left = this.props.width - pos.right - width;
163 | return pos;
164 | },
165 | ne: function ne(mousePos, boxPos) {
166 | var pos = _extends({}, this.state, {
167 | top: this.constrainBoundary(mousePos.clientY - boxPos.top),
168 | right: this.constrainBoundary(boxPos.right - mousePos.clientX)
169 | });
170 | var dimensions = this.calculateDimensions(pos);
171 |
172 | var _preserveAspectRatio5 = this.preserveAspectRatio(dimensions.width, dimensions.height),
173 | _preserveAspectRatio6 = _slicedToArray(_preserveAspectRatio5, 2),
174 | width = _preserveAspectRatio6[0],
175 | height = _preserveAspectRatio6[1];
176 |
177 | pos.top = this.props.height - pos.bottom - height;
178 | pos.right = this.props.width - pos.left - width;
179 | return pos;
180 | },
181 | se: function se(mousePos, boxPos) {
182 | var pos = _extends({}, this.state, {
183 | bottom: this.constrainBoundary(boxPos.bottom - mousePos.clientY),
184 | right: this.constrainBoundary(boxPos.right - mousePos.clientX)
185 | });
186 | var dimensions = this.calculateDimensions(pos);
187 |
188 | var _preserveAspectRatio7 = this.preserveAspectRatio(dimensions.width, dimensions.height),
189 | _preserveAspectRatio8 = _slicedToArray(_preserveAspectRatio7, 2),
190 | width = _preserveAspectRatio8[0],
191 | height = _preserveAspectRatio8[1];
192 |
193 | pos.bottom = this.props.height - pos.top - height;
194 | pos.right = this.props.width - pos.left - width;
195 | return pos;
196 | },
197 | sw: function sw(mousePos, boxPos) {
198 | var pos = _extends({}, this.state, {
199 | bottom: this.constrainBoundary(boxPos.bottom - mousePos.clientY),
200 | left: this.constrainBoundary(mousePos.clientX - boxPos.left)
201 | });
202 | var dimensions = this.calculateDimensions(pos);
203 |
204 | var _preserveAspectRatio9 = this.preserveAspectRatio(dimensions.width, dimensions.height),
205 | _preserveAspectRatio10 = _slicedToArray(_preserveAspectRatio9, 2),
206 | width = _preserveAspectRatio10[0],
207 | height = _preserveAspectRatio10[1];
208 |
209 | pos.bottom = this.props.height - pos.top - height;
210 | pos.left = this.props.width - pos.right - width;
211 | return pos;
212 | },
213 | onResize: function onResize(event) {
214 | var box = this.refs.box.parentElement.parentElement.getBoundingClientRect();
215 | var coordinates = this.getClientCoordinates(event);
216 | var position = this[this.state.corner](coordinates, box);
217 | this.resize(position, coordinates);
218 | },
219 | controlsResize: function controlsResize(event) {
220 | var box = this.refs.box.parentElement.parentElement.getBoundingClientRect();
221 | var width = event.target.name === 'width' ? +event.target.value : +event.target.value * this.props.aspectRatio;
222 | var height = event.target.name === 'height' ? +event.target.value : +event.target.value / this.props.aspectRatio;
223 | var dimensions = this.preserveAspectRatio(width, height);
224 | width = dimensions[0];
225 | height = dimensions[1];
226 |
227 | if (width > box.width - this.state.left || height > box.height - this.state.top) return;
228 |
229 | var widthDifference = this.state.width - width;
230 | var heightDifference = this.state.height - height;
231 | var pos = _extends({}, this.state, {
232 | right: this.state.right + widthDifference,
233 | bottom: this.state.bottom + heightDifference
234 | });
235 | var coordinates = {
236 | clientX: box.right - pos.right,
237 | clientY: box.bottom - pos.bottom
238 | };
239 |
240 | this.resize(pos, coordinates);
241 | },
242 | resize: function resize(position, coordinates) {
243 | var _this = this;
244 |
245 | var dimensions = this.calculateDimensions(position);
246 | var widthChanged = dimensions.width !== this.state.width,
247 | heightChanged = dimensions.height !== this.state.height;
248 | if (!widthChanged && !heightChanged) return;
249 |
250 | this.setState(_extends({}, coordinates, position, dimensions), function () {
251 | _this.props.onChange({
252 | top: position.top,
253 | left: position.left
254 | }, dimensions);
255 | });
256 | },
257 |
258 |
259 | // Move methods
260 | startMove: function startMove(evt) {
261 | var _getClientCoordinates = this.getClientCoordinates(evt),
262 | clientX = _getClientCoordinates.clientX,
263 | clientY = _getClientCoordinates.clientY;
264 |
265 | this.setState({
266 | moving: true,
267 | clientX: clientX,
268 | clientY: clientY
269 | });
270 | },
271 | stopMove: function stopMove(evt) {
272 | this.setState({
273 | moving: false
274 | });
275 | },
276 | eventMoveBox: function eventMoveBox(evt) {
277 | evt.preventDefault();
278 |
279 | var _getClientCoordinates2 = this.getClientCoordinates(evt),
280 | clientX = _getClientCoordinates2.clientX,
281 | clientY = _getClientCoordinates2.clientY;
282 |
283 | var movedX = clientX - this.state.clientX;
284 | var movedY = clientY - this.state.clientY;
285 |
286 | this.moveBox(clientX, clientY, movedX, movedY);
287 | },
288 | controlsMoveBox: function controlsMoveBox(evt) {
289 | var movedX = evt.target.name === 'x' ? evt.target.value - this.state.left : 0;
290 | var movedY = evt.target.name === 'y' ? evt.target.value - this.state.top : 0;
291 | this.moveBox(0, 0, movedX, movedY);
292 | },
293 | moveBox: function moveBox(clientX, clientY, movedX, movedY) {
294 | var _this2 = this;
295 |
296 | var position = {
297 | top: this.constrainBoundary(this.state.top + movedY),
298 | left: this.constrainBoundary(this.state.left + movedX),
299 | bottom: this.constrainBoundary(this.state.bottom - movedY),
300 | right: this.constrainBoundary(this.state.right - movedX)
301 | };
302 |
303 | if (!position.top) {
304 | position.bottom = this.props.height - this.state.height;
305 | }
306 | if (!position.bottom) {
307 | position.top = this.props.height - this.state.height;
308 | }
309 | if (!position.left) {
310 | position.right = this.props.width - this.state.width;
311 | }
312 | if (!position.right) {
313 | position.left = this.props.width - this.state.width;
314 | }
315 |
316 | this.setState(_extends({}, {
317 | clientX: clientX,
318 | clientY: clientY
319 | }, position), function () {
320 | _this2.props.onChange({
321 | top: position.top,
322 | left: position.left
323 | }, _this2.calculateDimensions(position));
324 | });
325 | },
326 | keyboardResize: function keyboardResize(change) {
327 | if (this.state.right - change < 0) {
328 | return;
329 | }
330 | if (this.state.bottom - change < 0) {
331 | return;
332 | }
333 |
334 | var _preserveAspectRatio11 = this.preserveAspectRatio(this.state.width + change, this.state.height + change),
335 | _preserveAspectRatio12 = _slicedToArray(_preserveAspectRatio11, 2),
336 | width = _preserveAspectRatio12[0],
337 | height = _preserveAspectRatio12[1];
338 |
339 | var widthChange = width - this.state.width;
340 | var heightChange = height - this.state.height;
341 |
342 | this.setState({
343 | bottom: this.state.bottom - heightChange,
344 | right: this.state.right - widthChange,
345 | width: width,
346 | height: height
347 | });
348 | },
349 | handleKey: function handleKey(event) {
350 | // safari doesn't support event.key, so fall back to keyCode
351 | if (event.shiftKey) {
352 | if (event.key === 'ArrowUp' || event.keyCode === 38) {
353 | this.keyboardResize(-10);
354 | event.preventDefault();
355 | } else if (event.key === 'ArrowDown' || event.keyCode === 40) {
356 | this.keyboardResize(10);
357 | event.preventDefault();
358 | } else if (event.key === 'ArrowLeft' || event.keyCode === 37) {
359 | this.keyboardResize(-10);
360 | event.preventDefault();
361 | } else if (event.key === 'ArrowRight' || event.keyCode === 39) {
362 | this.keyboardResize(10);
363 | event.preventDefault();
364 | }
365 | } else {
366 | if (event.key === 'ArrowUp' || event.keyCode === 38) {
367 | this.moveBox(this.state.clientX, this.state.clientY, 0, -10);
368 | event.preventDefault();
369 | } else if (event.key === 'ArrowDown' || event.keyCode === 40) {
370 | this.moveBox(this.state.clientX, this.state.clientY, 0, 10);
371 | event.preventDefault();
372 | } else if (event.key === 'ArrowLeft' || event.keyCode === 37) {
373 | this.moveBox(this.state.clientX, this.state.clientY, -10, 0);
374 | event.preventDefault();
375 | } else if (event.key === 'ArrowRight' || event.keyCode === 39) {
376 | this.moveBox(this.state.clientX, this.state.clientY, 10, 0);
377 | event.preventDefault();
378 | }
379 | }
380 | },
381 | render: function render() {
382 | var style = {
383 | position: 'absolute',
384 | top: this.state.top,
385 | left: this.state.left,
386 | right: this.state.right,
387 | bottom: this.state.bottom
388 | };
389 |
390 | var _calculateDimensions = this.calculateDimensions(this.state),
391 | width = _calculateDimensions.width,
392 | height = _calculateDimensions.height;
393 |
394 | var topStyle = {
395 | height: this.state.top
396 | };
397 | var bottomStyle = {
398 | height: this.state.bottom
399 | };
400 | var leftStyle = {
401 | top: this.state.top,
402 | right: width + this.state.right,
403 | bottom: this.state.bottom
404 | };
405 | var rightStyle = {
406 | top: this.state.top,
407 | left: width + this.state.left,
408 | bottom: this.state.bottom
409 | };
410 |
411 | return _react2.default.createElement(
412 | 'div',
413 | { ref: 'box', className: 'DraggableResizable' },
414 | _react2.default.createElement(
415 | 'div',
416 | { className: 'DraggableResizable-controls' },
417 | _react2.default.createElement(
418 | 'label',
419 | null,
420 | this.props.offsetXLabel,
421 | _react2.default.createElement('input', {
422 | name: 'x',
423 | value: Math.round(this.state.left),
424 | onChange: this.controlsMoveBox,
425 | tabIndex: '-1',
426 | type: 'number' })
427 | ),
428 | _react2.default.createElement(
429 | 'label',
430 | null,
431 | this.props.offsetYLabel,
432 | _react2.default.createElement('input', {
433 | name: 'y',
434 | value: Math.round(this.state.top),
435 | onChange: this.controlsMoveBox,
436 | tabIndex: '-1',
437 | type: 'number' })
438 | ),
439 | _react2.default.createElement(
440 | 'label',
441 | null,
442 | this.props.widthLabel,
443 | _react2.default.createElement('input', {
444 | name: 'width',
445 | value: Math.round(width),
446 | type: 'number',
447 | tabIndex: '-1',
448 | onChange: this.controlsResize })
449 | ),
450 | _react2.default.createElement(
451 | 'label',
452 | null,
453 | this.props.heightLabel,
454 | _react2.default.createElement('input', {
455 | value: Math.round(height),
456 | type: 'number',
457 | name: 'height',
458 | tabIndex: '-1',
459 | onChange: this.controlsResize })
460 | )
461 | ),
462 | _react2.default.createElement('div', { className: 'DraggableResizable-top', style: topStyle }),
463 | _react2.default.createElement('div', { className: 'DraggableResizable-left', style: leftStyle }),
464 | _react2.default.createElement(
465 | 'div',
466 | { style: style, onMouseDown: this.startMove, onTouchStart: this.startMove },
467 | this.props.children,
468 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-se',
469 | onMouseDown: this.startResize.bind(null, 'se'),
470 | onTouchStart: this.startResize.bind(null, 'se') }),
471 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-ne',
472 | onMouseDown: this.startResize.bind(null, 'ne'),
473 | onTouchStart: this.startResize.bind(null, 'ne') }),
474 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-sw',
475 | onMouseDown: this.startResize.bind(null, 'sw'),
476 | onTouchStart: this.startResize.bind(null, 'sw') }),
477 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-nw',
478 | onMouseDown: this.startResize.bind(null, 'nw'),
479 | onTouchStart: this.startResize.bind(null, 'nw') })
480 | ),
481 | _react2.default.createElement('div', { className: 'DraggableResizable-right', style: rightStyle }),
482 | _react2.default.createElement('div', { className: 'DraggableResizable-bottom', style: bottomStyle })
483 | );
484 | }
485 | });
486 |
--------------------------------------------------------------------------------
/dist/react-crop.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory(require("react"));
4 | else if(typeof define === 'function' && define.amd)
5 | define(["react"], factory);
6 | else if(typeof exports === 'object')
7 | exports["ReactCrop"] = factory(require("react"));
8 | else
9 | root["ReactCrop"] = factory(root["react"]);
10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 | /******/
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 | /******/
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 | /******/
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 | /******/
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 | /******/
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 | /******/
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 | /******/
39 | /******/
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 | /******/
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 | /******/
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "/";
48 | /******/
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ function(module, exports, __webpack_require__) {
56 |
57 | 'use strict';
58 |
59 | Object.defineProperty(exports, "__esModule", {
60 | value: true
61 | });
62 |
63 | var _react = __webpack_require__(1);
64 |
65 | var _react2 = _interopRequireDefault(_react);
66 |
67 | var _draggableResizableBox = __webpack_require__(2);
68 |
69 | var _draggableResizableBox2 = _interopRequireDefault(_draggableResizableBox);
70 |
71 | var _dataUriToBlob = __webpack_require__(3);
72 |
73 | var _dataUriToBlob2 = _interopRequireDefault(_dataUriToBlob);
74 |
75 | __webpack_require__(4);
76 |
77 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
78 |
79 | exports.default = _react2.default.createClass({
80 | displayName: 'Cropper',
81 |
82 | propTypes: {
83 | width: _react2.default.PropTypes.number.isRequired,
84 | height: _react2.default.PropTypes.number.isRequired,
85 | center: _react2.default.PropTypes.bool,
86 | image: _react2.default.PropTypes.any,
87 | widthLabel: _react2.default.PropTypes.string,
88 | heightLabel: _react2.default.PropTypes.string,
89 | offsetXLabel: _react2.default.PropTypes.string,
90 | offsetYLabel: _react2.default.PropTypes.string,
91 | onImageLoaded: _react2.default.PropTypes.func,
92 | minConstraints: _react2.default.PropTypes.arrayOf(_react2.default.PropTypes.number)
93 | },
94 |
95 | getDefaultProps: function getDefaultProps() {
96 | return {
97 | center: false,
98 | width: 'Width',
99 | height: 'Height',
100 | offsetXLabel: 'Offset X',
101 | offsetYLabel: 'Offset Y'
102 | };
103 | },
104 | getInitialState: function getInitialState() {
105 | return {
106 | imageLoaded: false,
107 | width: this.props.width,
108 | height: this.props.height,
109 | url: window.URL.createObjectURL(this.props.image)
110 | };
111 | },
112 | componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
113 | if (this.props.image !== nextProps.image) {
114 | this.setState({
115 | url: window.URL.createObjectURL(nextProps.image),
116 | imageLoaded: false
117 | });
118 | }
119 | },
120 | shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) {
121 | var image = this.props.image;
122 |
123 | return nextProps.image.size !== image.size || nextProps.image.name !== image.name || nextProps.image.type !== image.type || nextState.imageLoaded !== this.state.imageLoaded;
124 | },
125 | onLoad: function onLoad(evt) {
126 | var _this = this;
127 |
128 | var box = this.refs.box.getBoundingClientRect();
129 | this.setState({
130 | imageLoaded: true,
131 | width: box.width,
132 | height: box.height
133 | }, function () {
134 | var img = _this.refs.image;
135 | _this.props.onImageLoaded && _this.props.onImageLoaded(img);
136 | });
137 | },
138 | cropImage: function cropImage() {
139 | var _this2 = this;
140 |
141 | return new Promise(function (resolve, reject) {
142 | var img = new Image();
143 | img.onload = function () {
144 | var canvas = _this2.refs.canvas;
145 | var img = _this2.refs.image;
146 | var ctx = canvas.getContext('2d');
147 | var xScale = img.naturalWidth / _this2.state.width,
148 | yScale = img.naturalHeight / _this2.state.height;
149 |
150 |
151 | var imageOffsetX = xScale < 1 ? 0 : _this2.state.offset.left * xScale;
152 | var imageOffsetY = yScale < 1 ? 0 : _this2.state.offset.top * yScale;
153 | var imageWidth = xScale < 1 ? img.naturalWidth : _this2.state.dimensions.width * xScale;
154 | var imageHeight = yScale < 1 ? img.naturalHeight : _this2.state.dimensions.height * yScale;
155 |
156 | var canvasOffsetX = xScale < 1 ? Math.floor((_this2.state.dimensions.width - img.naturalWidth) / 2) : 0;
157 | var canvasOffsetY = yScale < 1 ? Math.floor((_this2.state.dimensions.height - img.naturalHeight) / 2) : 0;
158 | var canvasWidth = xScale < 1 ? img.naturalWidth : _this2.props.width;
159 | var canvasHeight = yScale < 1 ? img.naturalHeight : _this2.props.height;
160 |
161 | ctx.clearRect(0, 0, _this2.props.width, _this2.props.height);
162 | ctx.drawImage(img, imageOffsetX, imageOffsetY, imageWidth, imageHeight, canvasOffsetX, canvasOffsetY, canvasWidth, canvasHeight);
163 | resolve((0, _dataUriToBlob2.default)(canvas.toDataURL()));
164 | };
165 | img.src = window.URL.createObjectURL(_this2.props.image);
166 | });
167 | },
168 | onChange: function onChange(offset, dimensions) {
169 | this.setState({ offset: offset, dimensions: dimensions });
170 | },
171 | render: function render() {
172 | return _react2.default.createElement(
173 | 'div',
174 | {
175 | ref: 'box',
176 | className: 'Cropper',
177 | style: {
178 | minWidth: this.props.width,
179 | minHeight: this.props.height
180 | } },
181 | _react2.default.createElement('canvas', {
182 | className: 'Cropper-canvas',
183 | ref: 'canvas',
184 | width: this.props.width,
185 | height: this.props.height }),
186 | _react2.default.createElement('img', {
187 | ref: 'image',
188 | src: this.state.url,
189 | className: 'Cropper-image',
190 | onLoad: this.onLoad,
191 | style: { top: this.state.height / 2 } }),
192 | this.state.imageLoaded && _react2.default.createElement(
193 | 'div',
194 | { className: 'box' },
195 | _react2.default.createElement(
196 | _draggableResizableBox2.default,
197 | {
198 | aspectRatio: this.props.width / this.props.height,
199 | width: this.state.width,
200 | height: this.state.height,
201 | minConstraints: this.props.minConstraints,
202 | onChange: this.onChange,
203 | widthLabel: this.props.widthLabel,
204 | heightLabel: this.props.heightLabel,
205 | offsetXLabel: this.props.offsetXLabel,
206 | offsetYLabel: this.props.offsetYLabel },
207 | _react2.default.createElement('div', { className: 'Cropper-box' })
208 | )
209 | )
210 | );
211 | }
212 | });
213 |
214 | /***/ },
215 | /* 1 */
216 | /***/ function(module, exports) {
217 |
218 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
219 |
220 | /***/ },
221 | /* 2 */
222 | /***/ function(module, exports, __webpack_require__) {
223 |
224 | 'use strict';
225 |
226 | Object.defineProperty(exports, "__esModule", {
227 | value: true
228 | });
229 |
230 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
231 |
232 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
233 |
234 | var _react = __webpack_require__(1);
235 |
236 | var _react2 = _interopRequireDefault(_react);
237 |
238 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
239 |
240 | exports.default = _react2.default.createClass({
241 | displayName: 'DraggableResizableBox',
242 |
243 | propTypes: {
244 | aspectRatio: _react2.default.PropTypes.number.isRequired,
245 | width: _react2.default.PropTypes.number.isRequired,
246 | height: _react2.default.PropTypes.number.isRequired,
247 | onChange: _react2.default.PropTypes.func,
248 | offset: _react2.default.PropTypes.array,
249 | minConstraints: _react2.default.PropTypes.array,
250 | children: _react2.default.PropTypes.node,
251 | widthLabel: _react2.default.PropTypes.string,
252 | heightLabel: _react2.default.PropTypes.string,
253 | offsetXLabel: _react2.default.PropTypes.string,
254 | offsetYLabel: _react2.default.PropTypes.string
255 | },
256 |
257 | getDefaultProps: function getDefaultProps() {
258 | return {
259 | widthLabel: 'Width',
260 | heightLabel: 'Height',
261 | offsetXLabel: 'Offset X',
262 | offsetYLabel: 'Offset Y'
263 | };
264 | },
265 | getInitialState: function getInitialState() {
266 | var _preserveAspectRatio = this.preserveAspectRatio(this.props.width, this.props.height),
267 | _preserveAspectRatio2 = _slicedToArray(_preserveAspectRatio, 2),
268 | width = _preserveAspectRatio2[0],
269 | height = _preserveAspectRatio2[1];
270 |
271 | var centerYOffset = (this.props.height - height) / 2;
272 | var centerXOffset = (this.props.width - width) / 2;
273 | return {
274 | top: centerYOffset,
275 | left: centerXOffset,
276 | bottom: centerYOffset,
277 | right: centerXOffset,
278 | width: width,
279 | height: height
280 | };
281 | },
282 | componentDidMount: function componentDidMount() {
283 | document.addEventListener('mousemove', this.eventMove);
284 | document.addEventListener('mouseup', this.eventEnd);
285 | document.addEventListener('touchmove', this.eventMove);
286 | document.addEventListener('touchend', this.eventEnd);
287 | document.addEventListener('keydown', this.handleKey);
288 | this.props.onChange({
289 | top: this.state.top,
290 | left: this.state.left
291 | }, {
292 | width: this.state.width,
293 | height: this.state.height
294 | });
295 | },
296 | componentWillUnmount: function componentWillUnmount() {
297 | document.removeEventListener('mousemove', this.eventMove);
298 | document.removeEventListener('mouseup', this.eventEnd);
299 | document.removeEventListener('touchmove', this.eventMove);
300 | document.removeEventListener('touchend', this.eventEnd);
301 | document.removeEventListener('keydown', this.handleKey);
302 | },
303 | calculateDimensions: function calculateDimensions(_ref) {
304 | var top = _ref.top,
305 | left = _ref.left,
306 | bottom = _ref.bottom,
307 | right = _ref.right;
308 |
309 | return { width: this.props.width - left - right, height: this.props.height - top - bottom };
310 | },
311 |
312 |
313 | // If you do this, be careful of constraints
314 | preserveAspectRatio: function preserveAspectRatio(width, height) {
315 | if (this.props.minConstraints) {
316 | width = Math.max(width, this.props.minConstraints[0]);
317 | height = Math.max(height, this.props.minConstraints[1]);
318 | }
319 | var currentAspectRatio = width / height;
320 |
321 | if (currentAspectRatio < this.props.aspectRatio) {
322 | return [width, width / this.props.aspectRatio];
323 | } else if (currentAspectRatio > this.props.aspectRatio) {
324 | return [height * this.props.aspectRatio, height];
325 | } else {
326 | return [width, height];
327 | }
328 | },
329 | constrainBoundary: function constrainBoundary(side) {
330 | return side < 0 ? 0 : side;
331 | },
332 | getClientCoordinates: function getClientCoordinates(evt) {
333 | return evt.touches ? {
334 | clientX: evt.touches[0].clientX,
335 | clientY: evt.touches[0].clientY
336 | } : {
337 | clientX: evt.clientX,
338 | clientY: evt.clientY
339 | };
340 | },
341 | eventMove: function eventMove(evt) {
342 | if (this.state.resizing) {
343 | this.onResize(evt);
344 | } else if (this.state.moving) {
345 | this.eventMoveBox(evt);
346 | }
347 | },
348 | eventEnd: function eventEnd(evt) {
349 | if (this.state.resizing) {
350 | this.stopResize(evt);
351 | } else if (this.state.moving) {
352 | this.stopMove(evt);
353 | }
354 | },
355 |
356 |
357 | // Resize methods
358 | startResize: function startResize(corner, event) {
359 | event.stopPropagation();
360 | event.preventDefault();
361 | this.setState({
362 | resizing: true,
363 | corner: corner
364 | });
365 | },
366 | stopResize: function stopResize() {
367 | this.setState({ resizing: false });
368 | },
369 |
370 |
371 | // resize strategies
372 | nw: function nw(mousePos, boxPos) {
373 | var pos = _extends({}, this.state, {
374 | top: this.constrainBoundary(mousePos.clientY - boxPos.top),
375 | left: this.constrainBoundary(mousePos.clientX - boxPos.left)
376 | });
377 | var dimensions = this.calculateDimensions(pos);
378 |
379 | var _preserveAspectRatio3 = this.preserveAspectRatio(dimensions.width, dimensions.height),
380 | _preserveAspectRatio4 = _slicedToArray(_preserveAspectRatio3, 2),
381 | width = _preserveAspectRatio4[0],
382 | height = _preserveAspectRatio4[1];
383 |
384 | pos.top = this.props.height - pos.bottom - height;
385 | pos.left = this.props.width - pos.right - width;
386 | return pos;
387 | },
388 | ne: function ne(mousePos, boxPos) {
389 | var pos = _extends({}, this.state, {
390 | top: this.constrainBoundary(mousePos.clientY - boxPos.top),
391 | right: this.constrainBoundary(boxPos.right - mousePos.clientX)
392 | });
393 | var dimensions = this.calculateDimensions(pos);
394 |
395 | var _preserveAspectRatio5 = this.preserveAspectRatio(dimensions.width, dimensions.height),
396 | _preserveAspectRatio6 = _slicedToArray(_preserveAspectRatio5, 2),
397 | width = _preserveAspectRatio6[0],
398 | height = _preserveAspectRatio6[1];
399 |
400 | pos.top = this.props.height - pos.bottom - height;
401 | pos.right = this.props.width - pos.left - width;
402 | return pos;
403 | },
404 | se: function se(mousePos, boxPos) {
405 | var pos = _extends({}, this.state, {
406 | bottom: this.constrainBoundary(boxPos.bottom - mousePos.clientY),
407 | right: this.constrainBoundary(boxPos.right - mousePos.clientX)
408 | });
409 | var dimensions = this.calculateDimensions(pos);
410 |
411 | var _preserveAspectRatio7 = this.preserveAspectRatio(dimensions.width, dimensions.height),
412 | _preserveAspectRatio8 = _slicedToArray(_preserveAspectRatio7, 2),
413 | width = _preserveAspectRatio8[0],
414 | height = _preserveAspectRatio8[1];
415 |
416 | pos.bottom = this.props.height - pos.top - height;
417 | pos.right = this.props.width - pos.left - width;
418 | return pos;
419 | },
420 | sw: function sw(mousePos, boxPos) {
421 | var pos = _extends({}, this.state, {
422 | bottom: this.constrainBoundary(boxPos.bottom - mousePos.clientY),
423 | left: this.constrainBoundary(mousePos.clientX - boxPos.left)
424 | });
425 | var dimensions = this.calculateDimensions(pos);
426 |
427 | var _preserveAspectRatio9 = this.preserveAspectRatio(dimensions.width, dimensions.height),
428 | _preserveAspectRatio10 = _slicedToArray(_preserveAspectRatio9, 2),
429 | width = _preserveAspectRatio10[0],
430 | height = _preserveAspectRatio10[1];
431 |
432 | pos.bottom = this.props.height - pos.top - height;
433 | pos.left = this.props.width - pos.right - width;
434 | return pos;
435 | },
436 | onResize: function onResize(event) {
437 | var box = this.refs.box.parentElement.parentElement.getBoundingClientRect();
438 | var coordinates = this.getClientCoordinates(event);
439 | var position = this[this.state.corner](coordinates, box);
440 | this.resize(position, coordinates);
441 | },
442 | controlsResize: function controlsResize(event) {
443 | var box = this.refs.box.parentElement.parentElement.getBoundingClientRect();
444 | var width = event.target.name === 'width' ? +event.target.value : +event.target.value * this.props.aspectRatio;
445 | var height = event.target.name === 'height' ? +event.target.value : +event.target.value / this.props.aspectRatio;
446 | var dimensions = this.preserveAspectRatio(width, height);
447 | width = dimensions[0];
448 | height = dimensions[1];
449 |
450 | if (width > box.width - this.state.left || height > box.height - this.state.top) return;
451 |
452 | var widthDifference = this.state.width - width;
453 | var heightDifference = this.state.height - height;
454 | var pos = _extends({}, this.state, {
455 | right: this.state.right + widthDifference,
456 | bottom: this.state.bottom + heightDifference
457 | });
458 | var coordinates = {
459 | clientX: box.right - pos.right,
460 | clientY: box.bottom - pos.bottom
461 | };
462 |
463 | this.resize(pos, coordinates);
464 | },
465 | resize: function resize(position, coordinates) {
466 | var _this = this;
467 |
468 | var dimensions = this.calculateDimensions(position);
469 | var widthChanged = dimensions.width !== this.state.width,
470 | heightChanged = dimensions.height !== this.state.height;
471 | if (!widthChanged && !heightChanged) return;
472 |
473 | this.setState(_extends({}, coordinates, position, dimensions), function () {
474 | _this.props.onChange({
475 | top: position.top,
476 | left: position.left
477 | }, dimensions);
478 | });
479 | },
480 |
481 |
482 | // Move methods
483 | startMove: function startMove(evt) {
484 | var _getClientCoordinates = this.getClientCoordinates(evt),
485 | clientX = _getClientCoordinates.clientX,
486 | clientY = _getClientCoordinates.clientY;
487 |
488 | this.setState({
489 | moving: true,
490 | clientX: clientX,
491 | clientY: clientY
492 | });
493 | },
494 | stopMove: function stopMove(evt) {
495 | this.setState({
496 | moving: false
497 | });
498 | },
499 | eventMoveBox: function eventMoveBox(evt) {
500 | evt.preventDefault();
501 |
502 | var _getClientCoordinates2 = this.getClientCoordinates(evt),
503 | clientX = _getClientCoordinates2.clientX,
504 | clientY = _getClientCoordinates2.clientY;
505 |
506 | var movedX = clientX - this.state.clientX;
507 | var movedY = clientY - this.state.clientY;
508 |
509 | this.moveBox(clientX, clientY, movedX, movedY);
510 | },
511 | controlsMoveBox: function controlsMoveBox(evt) {
512 | var movedX = evt.target.name === 'x' ? evt.target.value - this.state.left : 0;
513 | var movedY = evt.target.name === 'y' ? evt.target.value - this.state.top : 0;
514 | this.moveBox(0, 0, movedX, movedY);
515 | },
516 | moveBox: function moveBox(clientX, clientY, movedX, movedY) {
517 | var _this2 = this;
518 |
519 | var position = {
520 | top: this.constrainBoundary(this.state.top + movedY),
521 | left: this.constrainBoundary(this.state.left + movedX),
522 | bottom: this.constrainBoundary(this.state.bottom - movedY),
523 | right: this.constrainBoundary(this.state.right - movedX)
524 | };
525 |
526 | if (!position.top) {
527 | position.bottom = this.props.height - this.state.height;
528 | }
529 | if (!position.bottom) {
530 | position.top = this.props.height - this.state.height;
531 | }
532 | if (!position.left) {
533 | position.right = this.props.width - this.state.width;
534 | }
535 | if (!position.right) {
536 | position.left = this.props.width - this.state.width;
537 | }
538 |
539 | this.setState(_extends({}, {
540 | clientX: clientX,
541 | clientY: clientY
542 | }, position), function () {
543 | _this2.props.onChange({
544 | top: position.top,
545 | left: position.left
546 | }, _this2.calculateDimensions(position));
547 | });
548 | },
549 | keyboardResize: function keyboardResize(change) {
550 | if (this.state.right - change < 0) {
551 | return;
552 | }
553 | if (this.state.bottom - change < 0) {
554 | return;
555 | }
556 |
557 | var _preserveAspectRatio11 = this.preserveAspectRatio(this.state.width + change, this.state.height + change),
558 | _preserveAspectRatio12 = _slicedToArray(_preserveAspectRatio11, 2),
559 | width = _preserveAspectRatio12[0],
560 | height = _preserveAspectRatio12[1];
561 |
562 | var widthChange = width - this.state.width;
563 | var heightChange = height - this.state.height;
564 |
565 | this.setState({
566 | bottom: this.state.bottom - heightChange,
567 | right: this.state.right - widthChange,
568 | width: width,
569 | height: height
570 | });
571 | },
572 | handleKey: function handleKey(event) {
573 | // safari doesn't support event.key, so fall back to keyCode
574 | if (event.shiftKey) {
575 | if (event.key === 'ArrowUp' || event.keyCode === 38) {
576 | this.keyboardResize(-10);
577 | event.preventDefault();
578 | } else if (event.key === 'ArrowDown' || event.keyCode === 40) {
579 | this.keyboardResize(10);
580 | event.preventDefault();
581 | } else if (event.key === 'ArrowLeft' || event.keyCode === 37) {
582 | this.keyboardResize(-10);
583 | event.preventDefault();
584 | } else if (event.key === 'ArrowRight' || event.keyCode === 39) {
585 | this.keyboardResize(10);
586 | event.preventDefault();
587 | }
588 | } else {
589 | if (event.key === 'ArrowUp' || event.keyCode === 38) {
590 | this.moveBox(this.state.clientX, this.state.clientY, 0, -10);
591 | event.preventDefault();
592 | } else if (event.key === 'ArrowDown' || event.keyCode === 40) {
593 | this.moveBox(this.state.clientX, this.state.clientY, 0, 10);
594 | event.preventDefault();
595 | } else if (event.key === 'ArrowLeft' || event.keyCode === 37) {
596 | this.moveBox(this.state.clientX, this.state.clientY, -10, 0);
597 | event.preventDefault();
598 | } else if (event.key === 'ArrowRight' || event.keyCode === 39) {
599 | this.moveBox(this.state.clientX, this.state.clientY, 10, 0);
600 | event.preventDefault();
601 | }
602 | }
603 | },
604 | render: function render() {
605 | var style = {
606 | position: 'absolute',
607 | top: this.state.top,
608 | left: this.state.left,
609 | right: this.state.right,
610 | bottom: this.state.bottom
611 | };
612 |
613 | var _calculateDimensions = this.calculateDimensions(this.state),
614 | width = _calculateDimensions.width,
615 | height = _calculateDimensions.height;
616 |
617 | var topStyle = {
618 | height: this.state.top
619 | };
620 | var bottomStyle = {
621 | height: this.state.bottom
622 | };
623 | var leftStyle = {
624 | top: this.state.top,
625 | right: width + this.state.right,
626 | bottom: this.state.bottom
627 | };
628 | var rightStyle = {
629 | top: this.state.top,
630 | left: width + this.state.left,
631 | bottom: this.state.bottom
632 | };
633 |
634 | return _react2.default.createElement(
635 | 'div',
636 | { ref: 'box', className: 'DraggableResizable' },
637 | _react2.default.createElement(
638 | 'div',
639 | { className: 'DraggableResizable-controls' },
640 | _react2.default.createElement(
641 | 'label',
642 | null,
643 | this.props.offsetXLabel,
644 | _react2.default.createElement('input', {
645 | name: 'x',
646 | value: Math.round(this.state.left),
647 | onChange: this.controlsMoveBox,
648 | tabIndex: '-1',
649 | type: 'number' })
650 | ),
651 | _react2.default.createElement(
652 | 'label',
653 | null,
654 | this.props.offsetYLabel,
655 | _react2.default.createElement('input', {
656 | name: 'y',
657 | value: Math.round(this.state.top),
658 | onChange: this.controlsMoveBox,
659 | tabIndex: '-1',
660 | type: 'number' })
661 | ),
662 | _react2.default.createElement(
663 | 'label',
664 | null,
665 | this.props.widthLabel,
666 | _react2.default.createElement('input', {
667 | name: 'width',
668 | value: Math.round(width),
669 | type: 'number',
670 | tabIndex: '-1',
671 | onChange: this.controlsResize })
672 | ),
673 | _react2.default.createElement(
674 | 'label',
675 | null,
676 | this.props.heightLabel,
677 | _react2.default.createElement('input', {
678 | value: Math.round(height),
679 | type: 'number',
680 | name: 'height',
681 | tabIndex: '-1',
682 | onChange: this.controlsResize })
683 | )
684 | ),
685 | _react2.default.createElement('div', { className: 'DraggableResizable-top', style: topStyle }),
686 | _react2.default.createElement('div', { className: 'DraggableResizable-left', style: leftStyle }),
687 | _react2.default.createElement(
688 | 'div',
689 | { style: style, onMouseDown: this.startMove, onTouchStart: this.startMove },
690 | this.props.children,
691 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-se',
692 | onMouseDown: this.startResize.bind(null, 'se'),
693 | onTouchStart: this.startResize.bind(null, 'se') }),
694 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-ne',
695 | onMouseDown: this.startResize.bind(null, 'ne'),
696 | onTouchStart: this.startResize.bind(null, 'ne') }),
697 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-sw',
698 | onMouseDown: this.startResize.bind(null, 'sw'),
699 | onTouchStart: this.startResize.bind(null, 'sw') }),
700 | _react2.default.createElement('div', { className: 'resize-handle resize-handle-nw',
701 | onMouseDown: this.startResize.bind(null, 'nw'),
702 | onTouchStart: this.startResize.bind(null, 'nw') })
703 | ),
704 | _react2.default.createElement('div', { className: 'DraggableResizable-right', style: rightStyle }),
705 | _react2.default.createElement('div', { className: 'DraggableResizable-bottom', style: bottomStyle })
706 | );
707 | }
708 | });
709 |
710 | /***/ },
711 | /* 3 */
712 | /***/ function(module, exports) {
713 |
714 |
715 | /**
716 | * Blob constructor.
717 | */
718 |
719 | var Blob = window.Blob;
720 |
721 | /**
722 | * ArrayBufferView support.
723 | */
724 |
725 | var hasArrayBufferView = new Blob([new Uint8Array(100)]).size == 100;
726 |
727 | /**
728 | * Return a `Blob` for the given data `uri`.
729 | *
730 | * @param {String} uri
731 | * @return {Blob}
732 | * @api public
733 | */
734 |
735 | module.exports = function(uri){
736 | var data = uri.split(',')[1];
737 | var bytes = atob(data);
738 | var buf = new ArrayBuffer(bytes.length);
739 | var arr = new Uint8Array(buf);
740 | for (var i = 0; i < bytes.length; i++) {
741 | arr[i] = bytes.charCodeAt(i);
742 | }
743 |
744 | if (!hasArrayBufferView) arr = buf;
745 | var blob = new Blob([arr], { type: mime(uri) });
746 | blob.slice = blob.slice || blob.webkitSlice;
747 | return blob;
748 | };
749 |
750 | /**
751 | * Return data uri mime type.
752 | */
753 |
754 | function mime(uri) {
755 | return uri.split(';')[0].slice(5);
756 | }
757 |
758 |
759 | /***/ },
760 | /* 4 */
761 | /***/ function(module, exports, __webpack_require__) {
762 |
763 | // style-loader: Adds some css to the DOM by adding a