: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
_this.saveContainerPosition();
var containerAspect = _this.containerRect.width / _this.containerRect.height;
var naturalWidth = ((_a = _this.imageRef.current) === null || _a === void 0 ? void 0 : _a.naturalWidth) || ((_b = _this.videoRef.current) === null || _b === void 0 ? void 0 : _b.videoWidth) || 0;
var naturalHeight = ((_c = _this.imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalHeight) || ((_d = _this.videoRef.current) === null || _d === void 0 ? void 0 : _d.videoHeight) || 0;
var isMediaScaledDown = mediaRef.offsetWidth < naturalWidth || mediaRef.offsetHeight < naturalHeight;
var mediaAspect = naturalWidth / naturalHeight;
// We do not rely on the offsetWidth/offsetHeight if the media is scaled down
// as the values they report are rounded. That will result in precision losses
// when calculating zoom. We use the fact that the media is positionned relative
// to the container. That allows us to use the container's dimensions
// and natural aspect ratio of the media to calculate accurate media size.
// However, for this to work, the container should not be rotated
var renderedMediaSize = void 0;
switch (_this.state.mediaObjectFit) {
renderedMediaSize = containerAspect > mediaAspect ? {
width: _this.containerRect.height * mediaAspect,
height: _this.containerRect.height
width: _this.containerRect.width,
height: _this.containerRect.width / mediaAspect
width: _this.containerRect.width,
height: _this.containerRect.width / mediaAspect
width: _this.containerRect.height * mediaAspect,
height: _this.containerRect.height
width: mediaRef.offsetWidth,
height: mediaRef.offsetHeight
_this.mediaSize = __assign(__assign({}, renderedMediaSize), {
naturalWidth: naturalWidth,
naturalHeight: naturalHeight
// set media size in the parent
if (_this.props.setMediaSize) {
_this.props.setMediaSize(_this.mediaSize);
var cropSize = _this.props.cropSize ? _this.props.cropSize : getCropSize(_this.mediaSize.width, _this.mediaSize.height, _this.containerRect.width, _this.containerRect.height, _this.props.aspect, _this.props.rotation);
if (((_e = _this.state.cropSize) === null || _e === void 0 ? void 0 : _e.height) !== cropSize.height || ((_f = _this.state.cropSize) === null || _f === void 0 ? void 0 : _f.width) !== cropSize.width) {
_this.props.onCropSizeChange && _this.props.onCropSizeChange(cropSize);
}, _this.recomputeCropPosition);
// pass crop size to parent
if (_this.props.setCropSize) {
_this.props.setCropSize(cropSize);
_this.saveContainerPosition = function () {
if (_this.containerRef) {
var bounds = _this.containerRef.getBoundingClientRect();
_this.containerPosition = {
_this.onMouseDown = function (e) {
if (!_this.currentDoc) return;
_this.currentDoc.addEventListener('mousemove', _this.onMouseMove);
_this.currentDoc.addEventListener('mouseup', _this.onDragStopped);
_this.saveContainerPosition();
_this.onDragStart(Cropper.getMousePoint(e));
_this.onMouseMove = function (e) {
return _this.onDrag(Cropper.getMousePoint(e));
_this.onScroll = function (e) {
if (!_this.currentDoc) return;
_this.saveContainerPosition();
_this.onTouchStart = function (e) {
if (!_this.currentDoc) return;
if (_this.props.onTouchRequest && !_this.props.onTouchRequest(e)) {
_this.currentDoc.addEventListener('touchmove', _this.onTouchMove, {
}); // iOS 11 now defaults to passive: true
_this.currentDoc.addEventListener('touchend', _this.onDragStopped);
_this.saveContainerPosition();
if (e.touches.length === 2) {
} else if (e.touches.length === 1) {
_this.onDragStart(Cropper.getTouchPoint(e.touches[0]));
_this.onTouchMove = function (e) {
// Prevent whole page from scrolling on iOS.
if (e.touches.length === 2) {
} else if (e.touches.length === 1) {
_this.onDrag(Cropper.getTouchPoint(e.touches[0]));
_this.onGestureStart = function (e) {
if (!_this.currentDoc) return;
_this.currentDoc.addEventListener('gesturechange', _this.onGestureMove);
_this.currentDoc.addEventListener('gestureend', _this.onGestureEnd);
_this.gestureZoomStart = _this.props.zoom;
_this.gestureRotationStart = _this.props.rotation;
_this.onGestureMove = function (e) {
// this is to avoid conflict between gesture and touch events
var point = Cropper.getMousePoint(e);
var newZoom = _this.gestureZoomStart - 1 + e.scale;
_this.setNewZoom(newZoom, point, {
shouldUpdatePosition: true
if (_this.props.onRotationChange) {
var newRotation = _this.gestureRotationStart + e.rotation;
_this.props.onRotationChange(newRotation);
_this.onGestureEnd = function (e) {
_this.onDragStart = function (_a) {
_this.dragStartPosition = {
_this.dragStartCrop = __assign({}, _this.props.crop);
(_c = (_b = _this.props).onInteractionStart) === null || _c === void 0 ? void 0 : _c.call(_b);
_this.onDrag = function (_a) {
if (!_this.currentWindow) return;
if (_this.rafDragTimeout) _this.currentWindow.cancelAnimationFrame(_this.rafDragTimeout);
_this.rafDragTimeout = _this.currentWindow.requestAnimationFrame(function () {
if (!_this.state.cropSize) return;
if (x === undefined || y === undefined) return;
var offsetX = x - _this.dragStartPosition.x;
var offsetY = y - _this.dragStartPosition.y;
var requestedPosition = {
x: _this.dragStartCrop.x + offsetX,
y: _this.dragStartCrop.y + offsetY
var newPosition = _this.props.restrictPosition ? restrictPosition(requestedPosition, _this.mediaSize, _this.state.cropSize, _this.props.zoom, _this.props.rotation) : requestedPosition;
_this.props.onCropChange(newPosition);
_this.onDragStopped = function () {
_this.isTouching = false;
(_b = (_a = _this.props).onInteractionEnd) === null || _b === void 0 ? void 0 : _b.call(_a);
_this.onWheel = function (e) {
if (!_this.currentWindow) return;
if (_this.props.onWheelRequest && !_this.props.onWheelRequest(e)) {
var point = Cropper.getMousePoint(e);
var pixelY = normalize_wheel_default()(e).pixelY;
var newZoom = _this.props.zoom - pixelY * _this.props.zoomSpeed / 200;
_this.setNewZoom(newZoom, point, {
shouldUpdatePosition: true
if (!_this.state.hasWheelJustStarted) {
hasWheelJustStarted: true
return (_b = (_a = _this.props).onInteractionStart) === null || _b === void 0 ? void 0 : _b.call(_a);
clearTimeout(_this.wheelTimer);
_this.wheelTimer = _this.currentWindow.setTimeout(function () {
hasWheelJustStarted: false
return (_b = (_a = _this.props).onInteractionEnd) === null || _b === void 0 ? void 0 : _b.call(_a);
_this.getPointOnContainer = function (_a, containerTopLeft) {
if (!_this.containerRect) {
throw new Error('The Cropper is not mounted');
x: _this.containerRect.width / 2 - (x - containerTopLeft.x),
y: _this.containerRect.height / 2 - (y - containerTopLeft.y)
_this.getPointOnMedia = function (_a) {
_this.setNewZoom = function (zoom, point, _a) {
var _b = _a === void 0 ? {} : _a,
_c = _b.shouldUpdatePosition,
shouldUpdatePosition = _c === void 0 ? true : _c;
if (!_this.state.cropSize || !_this.props.onZoomChange) return;
var newZoom = clamp(zoom, _this.props.minZoom, _this.props.maxZoom);
if (shouldUpdatePosition) {
var zoomPoint = _this.getPointOnContainer(point, _this.containerPosition);
var zoomTarget = _this.getPointOnMedia(zoomPoint);
var requestedPosition = {
x: zoomTarget.x * newZoom - zoomPoint.x,
y: zoomTarget.y * newZoom - zoomPoint.y
var newPosition = _this.props.restrictPosition ? restrictPosition(requestedPosition, _this.mediaSize, _this.state.cropSize, newZoom, _this.props.rotation) : requestedPosition;
_this.props.onCropChange(newPosition);
_this.props.onZoomChange(newZoom);
_this.getCropData = function () {
if (!_this.state.cropSize) {
// this is to ensure the crop is correctly restricted after a zoom back (https://github.com/ValentinH/react-easy-crop/issues/6)
var restrictedPosition = _this.props.restrictPosition ? restrictPosition(_this.props.crop, _this.mediaSize, _this.state.cropSize, _this.props.zoom, _this.props.rotation) : _this.props.crop;
return computeCroppedArea(restrictedPosition, _this.mediaSize, _this.state.cropSize, _this.getAspect(), _this.props.zoom, _this.props.rotation, _this.props.restrictPosition);
_this.emitCropData = function () {
var cropData = _this.getCropData();
var croppedAreaPercentages = cropData.croppedAreaPercentages,
croppedAreaPixels = cropData.croppedAreaPixels;
if (_this.props.onCropComplete) {
_this.props.onCropComplete(croppedAreaPercentages, croppedAreaPixels);
if (_this.props.onCropAreaChange) {
_this.props.onCropAreaChange(croppedAreaPercentages, croppedAreaPixels);
_this.emitCropAreaChange = function () {
var cropData = _this.getCropData();
var croppedAreaPercentages = cropData.croppedAreaPercentages,
croppedAreaPixels = cropData.croppedAreaPixels;
if (_this.props.onCropAreaChange) {
_this.props.onCropAreaChange(croppedAreaPercentages, croppedAreaPixels);
_this.recomputeCropPosition = function () {
if (!_this.state.cropSize) return;
var newPosition = _this.props.restrictPosition ? restrictPosition(_this.props.crop, _this.mediaSize, _this.state.cropSize, _this.props.zoom, _this.props.rotation) : _this.props.crop;
_this.props.onCropChange(newPosition);
Cropper.prototype.componentDidMount = function () {
if (!this.currentDoc || !this.currentWindow) return;
if (this.containerRef.ownerDocument) {
this.currentDoc = this.containerRef.ownerDocument;
if (this.currentDoc.defaultView) {
this.currentWindow = this.currentDoc.defaultView;
this.initResizeObserver();
// only add window resize listener if ResizeObserver is not supported. Otherwise, it would be redundant
if (typeof window.ResizeObserver === 'undefined') {
this.currentWindow.addEventListener('resize', this.computeSizes);
this.props.zoomWithScroll && this.containerRef.addEventListener('wheel', this.onWheel, {
this.containerRef.addEventListener('gesturestart', this.onGestureStart);
this.currentDoc.addEventListener('scroll', this.onScroll);
if (!this.props.disableAutomaticStylesInjection) {
this.styleRef = this.currentDoc.createElement('style');
this.styleRef.setAttribute('type', 'text/css');
this.styleRef.setAttribute('nonce', this.props.nonce);
this.styleRef.innerHTML = css_248z;
this.currentDoc.head.appendChild(this.styleRef);
// when rendered via SSR, the image can already be loaded and its onLoad callback will never be called
if (this.imageRef.current && this.imageRef.current.complete) {
// set image and video refs in the parent if the callbacks exist
if (this.props.setImageRef) {
this.props.setImageRef(this.imageRef);
if (this.props.setVideoRef) {
this.props.setVideoRef(this.videoRef);
Cropper.prototype.componentWillUnmount = function () {
if (!this.currentDoc || !this.currentWindow) return;
if (typeof window.ResizeObserver === 'undefined') {
this.currentWindow.removeEventListener('resize', this.computeSizes);
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
this.containerRef.removeEventListener('gesturestart', this.preventZoomSafari);
(_b = this.styleRef.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(this.styleRef);
this.props.zoomWithScroll && this.clearScrollEvent();
Cropper.prototype.componentDidUpdate = function (prevProps) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
if (prevProps.rotation !== this.props.rotation) {
this.recomputeCropPosition();
} else if (prevProps.aspect !== this.props.aspect) {
} else if (prevProps.objectFit !== this.props.objectFit) {
} else if (prevProps.zoom !== this.props.zoom) {
this.recomputeCropPosition();
} else if (((_a = prevProps.cropSize) === null || _a === void 0 ? void 0 : _a.height) !== ((_b = this.props.cropSize) === null || _b === void 0 ? void 0 : _b.height) || ((_c = prevProps.cropSize) === null || _c === void 0 ? void 0 : _c.width) !== ((_d = this.props.cropSize) === null || _d === void 0 ? void 0 : _d.width)) {
} else if (((_e = prevProps.crop) === null || _e === void 0 ? void 0 : _e.x) !== ((_f = this.props.crop) === null || _f === void 0 ? void 0 : _f.x) || ((_g = prevProps.crop) === null || _g === void 0 ? void 0 : _g.y) !== ((_h = this.props.crop) === null || _h === void 0 ? void 0 : _h.y)) {
this.emitCropAreaChange();
if (prevProps.zoomWithScroll !== this.props.zoomWithScroll && this.containerRef) {
this.props.zoomWithScroll ? this.containerRef.addEventListener('wheel', this.onWheel, {
}) : this.clearScrollEvent();
if (prevProps.video !== this.props.video) {
(_j = this.videoRef.current) === null || _j === void 0 ? void 0 : _j.load();
var objectFit = this.getObjectFit();
if (objectFit !== this.state.mediaObjectFit) {
mediaObjectFit: objectFit
Cropper.prototype.getAspect = function () {
return cropSize.width / cropSize.height;
Cropper.prototype.getObjectFit = function () {
if (this.props.objectFit === 'cover') {
var mediaRef = this.imageRef.current || this.videoRef.current;
if (mediaRef && this.containerRef) {
this.containerRect = this.containerRef.getBoundingClientRect();
var containerAspect = this.containerRect.width / this.containerRect.height;
var naturalWidth = ((_a = this.imageRef.current) === null || _a === void 0 ? void 0 : _a.naturalWidth) || ((_b = this.videoRef.current) === null || _b === void 0 ? void 0 : _b.videoWidth) || 0;
var naturalHeight = ((_c = this.imageRef.current) === null || _c === void 0 ? void 0 : _c.naturalHeight) || ((_d = this.videoRef.current) === null || _d === void 0 ? void 0 : _d.videoHeight) || 0;
var mediaAspect = naturalWidth / naturalHeight;
return mediaAspect < containerAspect ? 'horizontal-cover' : 'vertical-cover';
return 'horizontal-cover';
return this.props.objectFit;
Cropper.prototype.onPinchStart = function (e) {
var pointA = Cropper.getTouchPoint(e.touches[0]);
var pointB = Cropper.getTouchPoint(e.touches[1]);
this.lastPinchDistance = getDistanceBetweenPoints(pointA, pointB);
this.lastPinchRotation = getRotationBetweenPoints(pointA, pointB);
this.onDragStart(getCenter(pointA, pointB));
Cropper.prototype.onPinchMove = function (e) {
if (!this.currentDoc || !this.currentWindow) return;
var pointA = Cropper.getTouchPoint(e.touches[0]);
var pointB = Cropper.getTouchPoint(e.touches[1]);
var center = getCenter(pointA, pointB);
if (this.rafPinchTimeout) this.currentWindow.cancelAnimationFrame(this.rafPinchTimeout);
this.rafPinchTimeout = this.currentWindow.requestAnimationFrame(function () {
var distance = getDistanceBetweenPoints(pointA, pointB);
var newZoom = _this.props.zoom * (distance / _this.lastPinchDistance);
_this.setNewZoom(newZoom, center, {
shouldUpdatePosition: false
_this.lastPinchDistance = distance;
var rotation = getRotationBetweenPoints(pointA, pointB);
var newRotation = _this.props.rotation + (rotation - _this.lastPinchRotation);
_this.props.onRotationChange && _this.props.onRotationChange(newRotation);
_this.lastPinchRotation = rotation;
Cropper.prototype.render = function () {
mediaProps = _a.mediaProps,
transform = _a.transform,
cropShape = _a.cropShape,
containerStyle = _c.containerStyle,
cropAreaStyle = _c.cropAreaStyle,
mediaStyle = _c.mediaStyle,
containerClassName = _d.containerClassName,
cropAreaClassName = _d.cropAreaClassName,
mediaClassName = _d.mediaClassName;
var objectFit = this.state.mediaObjectFit;
return external_React_.createElement("div", {
onMouseDown: this.onMouseDown,
onTouchStart: this.onTouchStart,
return _this.containerRef = el;
"data-testid": "container",
className: classNames('reactEasyCrop_Container', containerClassName)
}, image ? external_React_.createElement("img", __assign({
className: classNames('reactEasyCrop_Image', objectFit === 'contain' && 'reactEasyCrop_Contain', objectFit === 'horizontal-cover' && 'reactEasyCrop_Cover_Horizontal', objectFit === 'vertical-cover' && 'reactEasyCrop_Cover_Vertical', mediaClassName)
style: __assign(__assign({}, mediaStyle), {
transform: transform || "translate(".concat(x, "px, ").concat(y, "px) rotate(").concat(rotation, "deg) scale(").concat(zoom, ")")
})) : video && external_React_.createElement("video", __assign({
className: classNames('reactEasyCrop_Video', objectFit === 'contain' && 'reactEasyCrop_Contain', objectFit === 'horizontal-cover' && 'reactEasyCrop_Cover_Horizontal', objectFit === 'vertical-cover' && 'reactEasyCrop_Cover_Vertical', mediaClassName)
onLoadedMetadata: this.onMediaLoad,
style: __assign(__assign({}, mediaStyle), {
transform: transform || "translate(".concat(x, "px, ").concat(y, "px) rotate(").concat(rotation, "deg) scale(").concat(zoom, ")")
}), (Array.isArray(video) ? video : [{
}]).map(function (item) {
return external_React_.createElement("source", __assign({
})), this.state.cropSize && external_React_.createElement("div", {
style: __assign(__assign({}, cropAreaStyle), {
width: this.state.cropSize.width,
height: this.state.cropSize.height
"data-testid": "cropper",
className: classNames('reactEasyCrop_CropArea', cropShape === 'round' && 'reactEasyCrop_CropAreaRound', showGrid && 'reactEasyCrop_CropAreaGrid', cropAreaClassName)