@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
480 lines (479 loc) • 19.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "AvatarCropper", {
enumerable: true,
get: function() {
return AvatarCropper;
}
});
var _interop_require_default = require("@swc/helpers/_/_interop_require_default");
var _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
var _async_to_generator = require("@swc/helpers/_/_async_to_generator");
var _object_spread = require("@swc/helpers/_/_object_spread");
var _object_spread_props = require("@swc/helpers/_/_object_spread_props");
var _object_without_properties = require("@swc/helpers/_/_object_without_properties");
var _sliced_to_array = require("@swc/helpers/_/_sliced_to_array");
var _ts_generator = require("@swc/helpers/_/_ts_generator");
var _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
var _classnames = /*#__PURE__*/ _interop_require_default._(require("classnames"));
var _button = /*#__PURE__*/ _interop_require_default._(require("../button"));
var _typings = require("../../utils/typings");
var _usetouch = require("../../hooks/use-touch");
var _utils = require("../../utils");
var _getrect = require("../../utils/get-rect");
var _configprovider = require("../configprovider");
var defaultProps = (0, _object_spread_props._)((0, _object_spread._)({}, _typings.ComponentDefaults), {
maxZoom: 3,
space: 10,
toolbar: [
/*#__PURE__*/ _react.default.createElement(_button.default, {
type: "danger",
key: "cancel"
}, "Cancel"),
/*#__PURE__*/ _react.default.createElement(_button.default, {
key: "reset"
}, "Reset"),
/*#__PURE__*/ _react.default.createElement(_button.default, {
key: "rotate"
}, "Rotate"),
/*#__PURE__*/ _react.default.createElement(_button.default, {
type: "success",
key: "confirm"
}, "Confirm")
],
toolbarPosition: 'bottom',
editText: 'Edit',
shape: 'square'
});
var classPrefix = "nut-avatar-cropper";
var AvatarCropper = function AvatarCropper(props) {
var locale = (0, _configprovider.useConfig)().locale;
defaultProps.toolbar = [
/*#__PURE__*/ _react.default.createElement(_button.default, {
type: "danger",
key: "cancel"
}, locale.cancel),
/*#__PURE__*/ _react.default.createElement(_button.default, {
key: "reset"
}, locale.reset),
/*#__PURE__*/ _react.default.createElement(_button.default, {
key: "rotate"
}, locale.avatarCropper.rotate),
/*#__PURE__*/ _react.default.createElement(_button.default, {
type: "success",
key: "confirm"
}, locale.confirm)
];
var _ref = (0, _object_spread._)({}, defaultProps, props), children = _ref.children, maxZoom = _ref.maxZoom, space = _ref.space, toolbar = _ref.toolbar, toolbarPosition = _ref.toolbarPosition, editText = _ref.editText, shape = _ref.shape, className = _ref.className, style = _ref.style, onConfirm = _ref.onConfirm, onCancel = _ref.onCancel, rest = (0, _object_without_properties._)(_ref, [
"children",
"maxZoom",
"space",
"toolbar",
"toolbarPosition",
"editText",
"shape",
"className",
"style",
"onConfirm",
"onCancel"
]);
var cls = (0, _classnames.default)(classPrefix, className, shape === 'round' && 'round');
var toolbarPositionCls = (0, _classnames.default)("".concat(classPrefix, "-popup-toolbar"), toolbarPosition);
var inputImageRef = (0, _react.useRef)(null);
var cropperPopupRef = (0, _react.useRef)(null);
var canvasRef = (0, _react.useRef)(null);
var _useState = (0, _sliced_to_array._)((0, _react.useState)(false), 2), visible = _useState[0], setVisible = _useState[1];
var _useState1 = (0, _sliced_to_array._)((0, _react.useState)(false), 2), moving = _useState1[0], setMoving = _useState1[1];
var _useState2 = (0, _sliced_to_array._)((0, _react.useState)(false), 2), zooming = _useState2[0], setZooming = _useState2[1];
var _useState3 = (0, _sliced_to_array._)((0, _react.useState)({
defScale: 1,
scale: 1,
angle: 0,
moveX: 0,
moveY: 0,
displayWidth: 0,
displayHeight: 0
}), 2), state = _useState3[0], setState = _useState3[1];
var defDrawImage = {
img: new Image(),
sx: 0,
sy: 0,
swidth: 0,
sheight: 0,
x: 0,
y: 0,
width: 0,
height: 0
};
var _useState4 = (0, _sliced_to_array._)((0, _react.useState)((0, _object_spread._)({}, defDrawImage)), 2), drawImage = _useState4[0], setDrawImg = _useState4[1];
var devicePixelRatio = window.devicePixelRatio || 1;
var touch = (0, _usetouch.useTouch)();
var highlightStyle = (0, _react.useMemo)(function() {
var width = "".concat(drawImage.swidth / devicePixelRatio, "px");
var height = width;
return {
width: width,
height: height,
borderRadius: shape === 'round' ? '50%' : ''
};
}, [
devicePixelRatio,
drawImage.swidth
]);
// 是否是横向
var isAngle = (0, _react.useMemo)(function() {
return state.angle === 90 || state.angle === 270;
}, [
state.angle
]);
// 最大横向移动距离
var maxMoveX = (0, _react.useMemo)(function() {
var swidth = drawImage.swidth, height = drawImage.height;
if (isAngle) {
return Math.max(0, (height * state.scale - swidth) / 2);
}
return Math.max(0, (state.displayWidth * state.scale - swidth) / 2);
}, [
state.scale,
state.displayWidth,
drawImage,
isAngle
]);
// 最大纵向移动距离
var maxMoveY = (0, _react.useMemo)(function() {
var swidth = drawImage.swidth, height = drawImage.height;
if (isAngle) {
return Math.max(0, (state.displayWidth * state.scale - swidth) / 2);
}
return Math.max(0, (height * state.scale - swidth) / 2);
}, [
state.scale,
state.displayWidth,
drawImage,
isAngle
]);
// 文件转base64
var fileToDataURL = function fileToDataURL(file) {
return new Promise(function(resolve) {
var reader = new FileReader();
reader.onloadend = function(e) {
return resolve(e.target.result);
};
reader.readAsDataURL(file);
});
};
// base64转图片
var dataURLToImage = function dataURLToImage(dataURL) {
return new Promise(function(resolve) {
var img = new Image();
img.onload = function() {
return resolve(img);
};
img.src = dataURL;
});
};
// 绘制
var draw = (0, _react.useCallback)(function() {
var img = drawImage.img, width = drawImage.width, height = drawImage.height, x = drawImage.x, y = drawImage.y, swidth = drawImage.swidth;
var moveX = state.moveX, moveY = state.moveY, scale = state.scale, angle = state.angle, displayWidth = state.displayWidth, displayHeight = state.displayHeight;
var canvas = canvasRef.current;
if (!canvas) return;
var ctx = canvas.getContext('2d');
canvas.width = displayWidth;
canvas.height = displayHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#666';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#000';
ctx.fillRect(space * devicePixelRatio, (canvas.height - swidth) / 2, swidth, swidth);
ctx.translate(canvas.width / 2 + moveX, canvas.height / 2 + moveY);
ctx.rotate(Math.PI / 180 * angle);
ctx.scale(scale, scale);
ctx.drawImage(img, x, y, width, height);
}, [
drawImage,
state,
devicePixelRatio,
space
]);
(0, _react.useEffect)(function() {
if (Math.abs(state.moveX) > maxMoveX) {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
moveX: maxMoveX
}));
}
if (Math.abs(state.moveY) > maxMoveY) {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
moveY: maxMoveY
}));
}
draw();
}, [
state,
maxMoveX,
maxMoveY,
draw
]);
// 设置绘制图片
var setDrawImgs = function setDrawImgs(image) {
var rect = (0, _getrect.getRect)(cropperPopupRef.current);
if (!rect) return;
var clientWidth = rect.width, clientHeight = rect.height;
var canvasWidth = state.displayWidth = clientWidth * devicePixelRatio;
var canvasHeight = state.displayHeight = clientHeight * devicePixelRatio;
var copyDrawImg = (0, _object_spread._)({}, defDrawImage);
var imgWidth = image.width, imgHeight = image.height;
copyDrawImg.img = image;
var isPortrait = imgHeight > imgWidth;
var rate = isPortrait ? imgWidth / imgHeight : imgHeight / imgWidth;
copyDrawImg.width = canvasWidth;
copyDrawImg.height = isPortrait ? canvasWidth / rate : canvasWidth * rate;
copyDrawImg.x = -copyDrawImg.width / 2;
copyDrawImg.y = -copyDrawImg.height / 2;
copyDrawImg.swidth = canvasWidth - space * 2 * devicePixelRatio;
copyDrawImg.sheight = isPortrait ? copyDrawImg.swidth / rate : copyDrawImg.swidth * rate;
copyDrawImg.sx = space * devicePixelRatio;
copyDrawImg.sy = (canvasHeight - copyDrawImg.swidth) / 2;
setDrawImg(copyDrawImg);
var scale = copyDrawImg.swidth / (isPortrait ? copyDrawImg.width : copyDrawImg.height);
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
defScale: scale
}));
resetScale(scale);
};
var inputImageChange = /*#__PURE__*/ function() {
var _ref = (0, _async_to_generator._)(function(event) {
var $el, files, base64, image;
return (0, _ts_generator._)(this, function(_state) {
switch(_state.label){
case 0:
setVisible(true);
$el = event.target;
files = $el.files;
if (!(files === null || files === void 0 ? void 0 : files.length)) return [
2
];
return [
4,
fileToDataURL(files[0])
];
case 1:
base64 = _state.sent();
return [
4,
dataURLToImage(base64)
];
case 2:
image = _state.sent();
setDrawImgs(image);
return [
2
];
}
});
});
return function inputImageChange(event) {
return _ref.apply(this, arguments);
};
}();
var resetScale = function resetScale(scale) {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
moveX: 0,
moveY: 0,
angle: 0,
scale: scale || state.defScale,
defScale: scale || state.defScale
}));
};
var setScale = function setScale(scale) {
scale = (0, _utils.clamp)(scale, +0.3, +maxZoom + 1);
if (scale !== state.scale) {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
scale: scale
}));
}
};
// 计算两个点的距离
var getDistance = function getDistance(touches) {
return Math.sqrt(Math.pow(touches[0].clientX - touches[1].clientX, 2) + Math.pow(touches[0].clientY - touches[1].clientY, 2));
};
var _useState5 = (0, _sliced_to_array._)((0, _react.useState)({
startMoveX: 0,
startMoveY: 0,
startScale: 0,
startDistance: 0
}), 2), startMove = _useState5[0], setStartMove = _useState5[1];
var startMoveX = startMove.startMoveX, startMoveY = startMove.startMoveY, startScale = startMove.startScale, startDistance = startMove.startDistance;
var onTouchStart = function onTouchStart(event) {
var touches = event.touches;
var offsetX = touch.offsetX;
touch.start(event);
var fingerNum = touches === null || touches === void 0 ? void 0 : touches.length;
setStartMove((0, _object_spread_props._)((0, _object_spread._)({}, startMove), {
startMoveX: state.moveX,
startMoveY: state.moveY
}));
setMoving(fingerNum === 1);
setZooming(fingerNum === 2 && !offsetX.current);
if (fingerNum === 2 && !offsetX.current) {
setStartMove((0, _object_spread_props._)((0, _object_spread._)({}, startMove), {
startScale: state.scale,
startDistance: getDistance(event.touches)
}));
}
};
var onTouchMove = function onTouchMove(event) {
var touches = event.touches;
touch.move(event);
if (moving || zooming) {
(0, _utils.preventDefault)(event, true);
}
if (moving) {
var deltaX = touch.deltaX, deltaY = touch.deltaY;
var moveX = deltaX.current * state.scale + startMoveX;
var moveY = deltaY.current * state.scale + startMoveY;
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
moveX: (0, _utils.clamp)(moveX, -maxMoveX, maxMoveX),
moveY: (0, _utils.clamp)(moveY, -maxMoveY, maxMoveY)
}));
}
if (zooming && touches.length === 2) {
var distance = getDistance(touches);
var scale = startScale * distance / startDistance;
setScale(scale);
}
};
var onTouchEnd = function onTouchEnd(event) {
var stopPropagation = false;
if (moving || zooming) {
stopPropagation = !(moving && startMoveX === state.moveX && startMoveY === state.moveY);
if (!event.touches.length) {
if (zooming) {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
moveX: (0, _utils.clamp)(state.moveX, -maxMoveX, maxMoveX),
moveY: (0, _utils.clamp)(state.moveY, -maxMoveY, maxMoveY)
}));
setZooming(false);
}
setMoving(false);
setStartMove((0, _object_spread_props._)((0, _object_spread._)({}, startMove), {
startMoveX: 0,
startMoveY: 0,
startScale: state.defScale
}));
if (state.scale < state.defScale) {
resetScale();
}
if (state.scale > maxZoom) {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
scale: +maxZoom
}));
}
}
}
(0, _utils.preventDefault)(event, stopPropagation);
touch.reset();
};
var reset = function reset() {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
angle: 0
}));
};
var rotate = function rotate() {
if (state.angle === 270) {
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
angle: 0
}));
return;
}
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
angle: state.angle + 90
}));
};
var cancel = function cancel() {
var isEmit = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : true;
setVisible(false);
resetScale();
inputImageRef.current && (inputImageRef.current.value = '');
isEmit && onCancel && onCancel();
};
// 裁剪图片
var confirm = function confirm() {
var canvas = canvasRef.current;
var sx = drawImage.sx, sy = drawImage.sy, swidth = drawImage.swidth;
var width = swidth;
var height = swidth;
// 创建一个新的canvas元素,用于裁剪后的内容
var croppedCanvas = document.createElement('canvas');
var croppedCtx = croppedCanvas.getContext('2d');
// 设置新canvas的大小与裁剪区域相同
croppedCanvas.width = width;
croppedCanvas.height = height;
// 使用drawImage方法将原canvas中指定区域的内容绘制到新canvas上
canvas && croppedCtx.drawImage(canvas, sx, sy, width, height, 0, 0, width, height);
// 将裁剪后的内容转换为图片格式
var imageDataURL = croppedCanvas.toDataURL('image/png');
onConfirm && onConfirm(imageDataURL);
cancel(false);
};
var ToolBar = function ToolBar() {
var actions = [
cancel,
reset,
rotate,
confirm
];
return /*#__PURE__*/ _react.default.createElement("div", {
className: "".concat(classPrefix, "-popup-toolbar-flex")
}, actions.map(function(action, index) {
return /*#__PURE__*/ _react.default.createElement("div", {
key: index,
className: "".concat(classPrefix, "-popup-toolbar-item"),
onClick: function onClick(_e) {
return action();
}
}, toolbar[index]);
}));
};
var CropperPopup = function CropperPopup() {
return /*#__PURE__*/ _react.default.createElement("div", {
ref: cropperPopupRef,
className: "".concat(classPrefix, "-popup"),
style: {
display: visible ? 'block' : 'none'
}
}, /*#__PURE__*/ _react.default.createElement("canvas", {
ref: canvasRef,
className: "".concat(classPrefix, "-popup-canvas")
}), /*#__PURE__*/ _react.default.createElement("div", {
className: "".concat(classPrefix, "-popup-highlight"),
onTouchStart: onTouchStart,
onTouchMove: onTouchMove,
onTouchEnd: onTouchEnd
}, /*#__PURE__*/ _react.default.createElement("div", {
className: "highlight",
style: highlightStyle
})), /*#__PURE__*/ _react.default.createElement("div", {
className: toolbarPositionCls
}, /*#__PURE__*/ _react.default.createElement(ToolBar, null)));
};
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement("div", (0, _object_spread_props._)((0, _object_spread._)({
className: cls
}, rest), {
style: style
}), children, /*#__PURE__*/ _react.default.createElement("input", {
ref: inputImageRef,
type: "file",
accept: "image/*",
className: "".concat(classPrefix, "-input"),
onChange: function onChange(e) {
return inputImageChange(e);
},
"aria-label": locale.avatarCropper.selectImage
}), /*#__PURE__*/ _react.default.createElement("div", {
className: "nut-avatar-cropper-edit-text"
}, editText)), CropperPopup());
};
AvatarCropper.displayName = 'NutAvatarCropper';