UNPKG

16.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _react = require('react');
8
9var React = _interopRequireWildcard(_react);
10
11var _Scaler = require('./Scaler');
12
13var _Scaler2 = _interopRequireDefault(_Scaler);
14
15var _canvasToBlob = require('./canvasToBlob');
16
17var _canvasToBlob2 = _interopRequireDefault(_canvasToBlob);
18
19var _utils = require('./utils');
20
21function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
22
23function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }
24
25function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
26
27function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
28
29function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
30
31function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
32
33function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); }
34
35var __assign = undefined && undefined.__assign || Object.assign || function (t) {
36 for (var s, i = 1, n = arguments.length; i < n; i++) {
37 s = arguments[i];
38 for (var p in s) {
39 if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
40 }
41 }
42 return t;
43};
44
45function isImage(file) {
46 return file.type && /^image\//g.test(file.type);
47}
48;
49var startX = 0;
50var startY = 0;
51var Δleft = 0;
52var Δtop = 0;
53var left = 0;
54var top = 0;
55var dragging = false;
56function limit(value, limitArray) {
57 var min = Math.min(limitArray[0], limitArray[1]);
58 var max = Math.max(limitArray[0], limitArray[1]);
59 if (value < min) {
60 return min;
61 }
62 if (value > max) {
63 return max;
64 }
65 return value;
66}
67
68var Cropper = function (_React$Component) {
69 _inherits(Cropper, _React$Component);
70
71 function Cropper(props) {
72 _classCallCheck(this, Cropper);
73
74 var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
75
76 _this.updateThumbnail = (0, _utils.debounce)(function () {
77 var _this$state = _this.state,
78 image = _this$state.image,
79 width = _this$state.width,
80 height = _this$state.height;
81 // const scaledImage = downScaleImage(image, 0.2);
82
83 _this.refs.Canvas2x.getContext('2d').drawImage(image, left, top, width, height);
84 _this.refs.Canvas1x.getContext('2d').drawImage(image, left, top, width, height);
85 }, 100);
86 _this.readFile = function (file) {
87 var reader = new FileReader();
88 if (file && isImage(file)) {
89 reader.readAsDataURL(file);
90 }
91 reader.onload = function () {
92 return _this.loadImage(reader);
93 };
94 };
95 _this.loadImage = function (reader) {
96 // const reader = new FileReader();
97 var image = new Image();
98 // Although you can use images without CORS approval in your canvas, doing so taints the canvas.
99 // Once a canvas has been tainted, you can no longer pull data back out of the canvas.
100 // For example, you can no longer use the canvas toBlob(), toDataURL(), or getImageData() methods;
101 // doing so will throw a security error.
102 // This protects users from having private data exposed by using images
103 // to pull information from remote web sites without permission.
104 image.setAttribute('crossOrigin', 'anonymous');
105 image.onload = function () {
106 return _this.normalizeImage(image);
107 };
108 image.src = reader.result;
109 };
110 _this.scaleImage = function (scale) {
111 if (scale === _this.state.scale) {
112 return;
113 }
114 var _this$state$image = _this.state.image,
115 width = _this$state$image.width,
116 height = _this$state$image.height;
117
118 var imageState = {
119 width: width * scale,
120 height: height * scale
121 };
122 _this.setState({
123 scale: scale,
124 width: imageState.width,
125 height: imageState.height,
126 widthLimit: [_this.state.viewport[0] - imageState.width, 0],
127 heightLimit: [_this.state.viewport[1] - imageState.height, 0]
128 });
129 left = limit((_this.state.viewport[0] - imageState.width) / 2 + Δleft, [_this.state.viewport[0] - imageState.width, 0]);
130 top = limit((_this.state.viewport[1] - imageState.height) / 2 + Δtop, [_this.state.viewport[1] - imageState.height, 0]);
131 _this.applyPositions();
132 };
133 _this.applyImageState = function (imageState) {
134 _this.setState({
135 width: imageState.width,
136 height: imageState.height,
137 widthLimit: [_this.state.viewport[0] - imageState.width, 0],
138 heightLimit: [_this.state.viewport[1] - imageState.height, 0]
139 }, _this.updateThumbnail);
140 left = (_this.state.viewport[0] - imageState.width) / 2;
141 top = (_this.state.viewport[1] - imageState.height) / 2;
142 _this.applyPositions();
143 };
144 // 初始化 Cropper,图片的尺寸会默认 fit 320 * 320
145 _this.normalizeImage = function (image) {
146 var _imageState;
147
148 var width = image.width,
149 height = image.height;
150 var viewport = _this.state.viewport;
151
152 var widthProportional = width / viewport[0];
153 var heightProportional = height / viewport[1];
154 var ΔProportional = widthProportional / heightProportional;
155 var IdpVar = ΔProportional > 1 ? 'height' : 'width'; // 自变量
156 var depVar = ΔProportional > 1 ? 'width' : 'height'; // 因变量
157 var scale = Number((viewport[Number(ΔProportional > 1)] / image[IdpVar]).toFixed(4));
158 // console.log('基准缩放属性:', IdpVar,':', image[IdpVar], 'px',
159 // '缩放至:', viewport[Number(ΔProportional > 1)], 'px',
160 // '缩放比例:', scale); // tslint:ignore
161 var imageState = (_imageState = {}, _defineProperty(_imageState, IdpVar, viewport[Number(ΔProportional > 1)]), _defineProperty(_imageState, depVar, viewport[Number(ΔProportional > 1)] / viewport[Number(ΔProportional > 1)] * image[depVar] * scale), _imageState);
162 _this.setState({
163 image: image,
164 scale: scale,
165 scaleRange: [scale, 1.777],
166 visible: true
167 }, function () {
168 return _this.applyImageState(imageState);
169 });
170 };
171 _this.applyPositions = function () {
172 (0, _utils.applyTransform)(_this.refs.viewport, 'translate3d(' + left + 'px,' + top + 'px,0)');
173 (0, _utils.applyTransform)(_this.refs.dragger, 'translate3d(' + left + 'px,' + top + 'px,0)');
174 };
175 _this.onMouseDown = function () {
176 _this.setState({
177 dragging: true
178 });
179 };
180 _this.dragStart = function (ev) {
181 dragging = true;
182 startX = ev.clientX;
183 startY = ev.clientY;
184 };
185 _this.dragOver = function (ev) {
186 if (dragging) {
187 Δleft += ev.clientX - startX;
188 Δtop += ev.clientY - startY;
189 left = limit(left + (ev.clientX - startX), _this.state.widthLimit);
190 top = limit(top + (ev.clientY - startY), _this.state.heightLimit);
191 startX = ev.clientX;
192 startY = ev.clientY;
193 _this.applyPositions();
194 _this.updateThumbnail();
195 // 拖动后,不再提示可拖动
196 if (_this.refs.dragNotice) {
197 _this.refs.dragNotice.style.display = 'none';
198 }
199 }
200 };
201 _this.dragEnd = function () {
202 dragging = false;
203 };
204 _this.handleCancel = function () {
205 _this.props.onChange(null);
206 _this.hideModal();
207 };
208 _this.handleOk = function () {
209 var _this$state2 = _this.state,
210 image = _this$state2.image,
211 width = _this$state2.width,
212 height = _this$state2.height,
213 scale = _this$state2.scale,
214 viewport = _this$state2.viewport;
215
216 var scaledImage = (0, _utils.downScaleImage)(image, scale);
217 var canvas = document.createElement('canvas');
218 canvas.style.width = viewport[0] + 'px';
219 canvas.style.height = viewport[1] + 'px';
220 canvas.setAttribute('width', viewport[0]);
221 canvas.setAttribute('height', viewport[1]);
222 var context = canvas.getContext('2d');
223 if (!/image\/png/g.test(_this.props.file.type)) {
224 context.fillStyle = "#fff";
225 context.fillRect(0, 0, viewport[0], viewport[1]);
226 }
227 // if circle...
228 if (_this.props.circle) {
229 context.save();
230 context.beginPath();
231 context.arc(viewport[0] / 2, viewport[1] / 2, Math.min(viewport[0] / 2, viewport[1] / 2), 0, Math.PI * 2, true);
232 context.closePath();
233 context.clip();
234 }
235 context.drawImage(scaledImage, left, top, width, height);
236 if (_this.props.circle) {
237 context.beginPath();
238 context.arc(0, 0, 2, 0, Math.PI, true);
239 context.closePath();
240 context.restore();
241 }
242 if (canvas.toBlob) {
243 canvas.toBlob(function (blob) {
244 _this.props.onChange(blob);
245 _this.hideModal();
246 }, _this.props.file.type);
247 } else {
248 var dataUrl = canvas.toDataURL(_this.props.file.type);
249 _this.props.onChange((0, _canvasToBlob2["default"])(dataUrl));
250 _this.hideModal();
251 }
252 };
253 _this.hideModal = function () {
254 _this.setState({
255 visible: false
256 });
257 document.body.style.overflow = '';
258 };
259 var size = props.size;
260
261 var imageState = void 0;
262 if (size[0] === size[1]) {
263 imageState = {
264 width: 320,
265 height: 320
266 };
267 } else {
268 imageState = {
269 height: 320,
270 width: 320 / size[1] * size[0]
271 };
272 }
273 _this.state = {
274 image: null,
275 viewport: [imageState.width, imageState.height],
276 width: 320,
277 height: 320,
278 dragging: false,
279 scaleRange: [1, 1],
280 scale: 1,
281 visible: false
282 };
283 return _this;
284 }
285
286 Cropper.prototype.componentDidMount = function componentDidMount() {
287 this.readFile(this.props.file);
288 document.addEventListener('mouseup', this.dragEnd);
289 document.addEventListener('mousemove', this.dragOver);
290 };
291
292 Cropper.prototype.componentWillUnmount = function componentWillUnmount() {
293 document.removeEventListener('mouseup', this.dragEnd);
294 document.removeEventListener('mousemove', this.dragOver);
295 };
296
297 Cropper.prototype.render = function render() {
298 var _props = this.props,
299 prefixCls = _props.prefixCls,
300 size = _props.size,
301 circle = _props.circle,
302 spin = _props.spin,
303 renderModal = _props.renderModal;
304 var _state = this.state,
305 image = _state.image,
306 width = _state.width,
307 height = _state.height,
308 scale = _state.scale,
309 scaleRange = _state.scaleRange,
310 viewport = _state.viewport;
311
312 var style = { left: 0, top: 0 };
313 var draggerEvents = {
314 onMouseDown: this.dragStart
315 };
316 var footer = [React.createElement(_Scaler2["default"], { key: "scaler", prefixCls: prefixCls, onChange: this.scaleImage, value: scale, min: scaleRange[0], max: scaleRange[1] }), React.createElement("button", { className: prefixCls + '-btn ' + prefixCls + '-btn-ghost', key: "back", type: "ghost", onClick: this.handleCancel }, (0, _utils.getLocale)('cancel', this.props.locale)), React.createElement("button", { className: prefixCls + '-btn ' + prefixCls + '-btn-primary', key: "submit", type: "primary", onClick: this.handleOk }, (0, _utils.getLocale)('submit', this.props.locale))];
317 var viewPortStyle = { width: viewport[0], height: viewport[1] };
318 var previewClassName = circle ? 'radius' : null;
319 var cropperElement = image ? React.createElement("div", { className: prefixCls + '-cropper-wrapper' }, React.createElement("div", { className: prefixCls + '-cropper' }, React.createElement("div", { className: prefixCls + '-thumbnail', style: viewPortStyle }, React.createElement("div", { className: "thumbnail-window", style: viewPortStyle }, React.createElement("img", { src: image.src, ref: "viewport", width: width, height: height, style: style })), React.createElement("img", __assign({}, draggerEvents, { ref: "dragger", src: image.src, width: width, height: height, style: style, className: prefixCls + '-background', draggable: false })), scale > scaleRange[0] ? React.createElement("div", { className: "candrag-notice-wrapper", ref: "dragNotice" }, React.createElement("span", { className: "candrag-notice" }, (0, _utils.getLocale)('drag to crop', this.props.locale))) : null)), React.createElement("div", { className: prefixCls + '-thumbnail-preview' }, React.createElement("h4", null, (0, _utils.getLocale)('preview', this.props.locale)), React.createElement("div", { className: "size-2x" }, React.createElement("canvas", { className: previewClassName, ref: "Canvas2x", width: viewport[0], height: viewport[1], style: { width: size[0] * 2, height: size[1] * 2 } }), React.createElement("p", null, "2x: ", size[0] * 2 + 'px * ' + size[1] * 2 + 'px')), React.createElement("div", { className: "size-1x" }, React.createElement("canvas", { className: previewClassName, ref: "Canvas1x", width: viewport[0], height: viewport[1], style: { width: size[0], height: size[1] } }), React.createElement("p", null, "1x: ", size[0] + 'px * ' + size[1] + 'px')))) : null;
320 if (image) {
321 return React.createElement("div", null, spin, renderModal ? React.cloneElement(renderModal(), {
322 visible: this.state.visible,
323 title: (0, _utils.getLocale)('edit picture', this.props.locale),
324 width: 800,
325 footer: footer,
326 onCancel: this.handleCancel
327 }, cropperElement) : React.createElement("div", null, cropperElement, " ", footer));
328 }
329 return React.createElement("span", null, " loading... ");
330 };
331
332 return Cropper;
333}(React.Component);
334
335exports["default"] = Cropper;
336
337Cropper.defaultProps = {
338 prefixCls: 'rc',
339 size: [32, 32],
340 circle: false,
341 onChange: function onChange() {},
342 locale: 'en-US'
343};
344module.exports = exports['default'];
\No newline at end of file