@nutui/nutui-react-taro
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
663 lines (662 loc) • 27.8 kB
JavaScript
;
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 _taro = /*#__PURE__*/ _interop_require_wildcard._(require("@tarojs/taro"));
var _classnames = /*#__PURE__*/ _interop_require_default._(require("classnames"));
var _components = require("@tarojs/components");
var _getsysteminfo = require("../../utils/taro/get-system-info");
var _button = require("../button/button");
var _index = require("../configprovider/index");
var _typings = require("../../utils/typings");
var _usetouch = require("../../hooks/use-touch");
var _utils = require("../../utils");
var defaultProps = (0, _object_spread_props._)((0, _object_spread._)({}, _typings.ComponentDefaults), {
maxZoom: 3,
space: 10,
toolbar: [
/*#__PURE__*/ _react.default.createElement(_button.Button, {
type: "danger",
key: "cancel"
}, "Cancel"),
/*#__PURE__*/ _react.default.createElement(_button.Button, {
key: "reset"
}, "Reset"),
/*#__PURE__*/ _react.default.createElement(_button.Button, {
key: "rotate"
}, "Rotate"),
/*#__PURE__*/ _react.default.createElement(_button.Button, {
type: "success",
key: "confirm"
}, "Confirm")
],
toolbarPosition: 'bottom',
editText: 'Edit',
sizeType: [
'original',
'compressed'
],
sourceType: [
'album',
'camera'
],
shape: 'square'
});
var classPrefix = "nut-avatar-cropper";
var AvatarCropper = function AvatarCropper(props) {
var locale = (0, _index.useConfig)().locale;
defaultProps.toolbar = [
/*#__PURE__*/ _react.default.createElement(_button.Button, {
type: "danger",
key: "cancel"
}, locale.cancel),
/*#__PURE__*/ _react.default.createElement(_button.Button, {
key: "reset"
}, locale.reset),
/*#__PURE__*/ _react.default.createElement(_button.Button, {
key: "rotate"
}, locale.avatarCropper.rotate),
/*#__PURE__*/ _react.default.createElement(_button.Button, {
type: "success",
key: "confirm"
}, locale.confirm)
];
defaultProps.editText = locale.edit;
var _ref = (0, _object_spread._)({}, defaultProps, props), children = _ref.children, toolbar = _ref.toolbar, maxZoom = _ref.maxZoom, space = _ref.space, toolbarPosition = _ref.toolbarPosition, editText = _ref.editText, sizeType = _ref.sizeType, sourceType = _ref.sourceType, shape = _ref.shape, className = _ref.className, style = _ref.style, onConfirm = _ref.onConfirm, onCancel = _ref.onCancel, rest = (0, _object_without_properties._)(_ref, [
"children",
"toolbar",
"maxZoom",
"space",
"toolbarPosition",
"editText",
"sizeType",
"sourceType",
"shape",
"className",
"style",
"onConfirm",
"onCancel"
]);
var cls = (0, _classnames.default)(classPrefix, 'taro', className, shape === 'round' && 'round');
var toolbarPositionCls = (0, _classnames.default)("".concat(classPrefix, "-popup-toolbar"), toolbarPosition);
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 systemInfo = (0, _getsysteminfo.getWindowInfo)();
// 支付宝基础库2.7.0以上支持,需要开启支付宝小程序canvas2d
var showAlipayCanvas2D = (0, _react.useMemo)(function() {
return _taro.default.getEnv() === 'ALIPAY' && parseInt(_taro.default.SDKVersion.replace(/\./g, '')) >= 270;
}, []);
var showPixelRatio = _taro.default.getEnv() === 'WEB' || showAlipayCanvas2D;
// 设备像素比
var pixelRatio = showPixelRatio ? systemInfo.pixelRatio : 1;
var _useState3 = (0, _sliced_to_array._)((0, _react.useState)({
defScale: 1,
scale: 1,
angle: 0,
moveX: 0,
moveY: 0,
displayWidth: systemInfo.windowWidth * pixelRatio,
displayHeight: systemInfo.windowHeight * pixelRatio,
cropperWidth: systemInfo.windowWidth * pixelRatio - space * pixelRatio * 2,
cropperHeight: systemInfo.windowWidth * pixelRatio - space * pixelRatio * 2
}), 2), state = _useState3[0], setState = _useState3[1];
var defDrawImage = {
src: '',
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 _useState5 = (0, _sliced_to_array._)((0, _react.useState)({
canvasId: "canvas-".concat(Date.now()),
cropperCanvas: null,
cropperCanvasContext: null
}), 2), canvasAll = _useState5[0], setCanvasAll = _useState5[1];
(0, _taro.useReady)(function() {
if (showAlipayCanvas2D) {
var canvasId = canvasAll.canvasId;
(0, _taro.createSelectorQuery)().select("#".concat(canvasId)).node(function(param) {
var canvas = param.node;
canvas.width = state.displayWidth;
canvas.height = state.displayHeight;
setCanvasAll((0, _object_spread_props._)((0, _object_spread._)({}, canvasAll), {
cropperCanvas: canvas
}));
}).exec();
}
});
(0, _react.useEffect)(function() {
setCanvasAll((0, _object_spread_props._)((0, _object_spread._)({}, canvasAll), {
cropperCanvasContext: _taro.default.createCanvasContext(canvasAll.canvasId)
}));
}, []);
var touch = (0, _usetouch.useTouch)();
var highlightStyle = (0, _react.useMemo)(function() {
var width = "".concat(state.cropperWidth / pixelRatio, "px");
var height = width;
return {
width: width,
height: height,
borderRadius: shape === 'round' ? '50%' : ''
};
}, [
pixelRatio,
state.cropperWidth
]);
// 是否是横向
var isAngle = (0, _react.useMemo)(function() {
return state.angle === 90 || state.angle === 270;
}, [
state.angle
]);
var canvasStyle = (0, _react.useMemo)(function() {
return {
width: "".concat(state.displayWidth / pixelRatio, "px"),
height: "".concat(state.displayHeight / pixelRatio, "px")
};
}, [
pixelRatio,
state.displayHeight,
state.displayWidth
]);
// 最大横向移动距离
var maxMoveX = (0, _react.useMemo)(function() {
var displayWidth = state.displayWidth, scale = state.scale, cropperWidth = state.cropperWidth;
if (isAngle) {
return Math.max(0, (drawImage.height * scale - cropperWidth) / 2);
}
return Math.max(0, (displayWidth * scale - cropperWidth) / 2);
}, [
drawImage.height,
isAngle,
state
]);
// 最大纵向移动距离
var maxMoveY = (0, _react.useMemo)(function() {
var displayWidth = state.displayWidth, scale = state.scale, cropperWidth = state.cropperWidth;
if (isAngle) {
return Math.max(0, (displayWidth * scale - cropperWidth) / 2);
}
return Math.max(0, (drawImage.height * scale - cropperWidth) / 2);
}, [
drawImage.height,
isAngle,
state
]);
// base64转图片
var dataURLToImage = function dataURLToImage(dataURL) {
return new Promise(function(resolve) {
var img = new Image();
img.onload = function() {
return resolve(img);
};
img.src = dataURL;
});
};
// base64转图片(canvasImage)
var dataURLToCanvasImage = function dataURLToCanvasImage(canvas, dataURL) {
return new Promise(function(resolve) {
var img = canvas.createImage();
img.onload = function() {
return resolve(img);
};
img.src = dataURL;
});
};
var canvas2dDraw = (0, _react.useCallback)(function(ctx) {
var src = drawImage.src, width = drawImage.width, height = drawImage.height, x = drawImage.x, y = drawImage.y;
if (!ctx || !src) return;
var moveX = state.moveX, moveY = state.moveY, scale = state.scale, angle = state.angle, displayWidth = state.displayWidth, displayHeight = state.displayHeight, cropperWidth = state.cropperWidth;
ctx.clearRect(0, 0, displayWidth, displayHeight);
ctx.fillStyle = '#666';
ctx.fillRect(0, 0, displayWidth, displayHeight);
ctx.fillStyle = '#000';
ctx.fillRect(space * pixelRatio, (displayHeight - cropperWidth) / 2, cropperWidth, cropperWidth);
ctx.translate(displayWidth / 2 + moveX, displayHeight / 2 + moveY);
ctx.rotate(Math.PI / 180 * angle);
ctx.scale(scale, scale);
ctx.drawImage(src, x, y, width, height);
}, [
drawImage,
state
]);
// web绘制
var webDraw = (0, _react.useCallback)(function() {
var canvasDom = document.getElementById(canvasAll.canvasId);
var canvas = canvasDom;
if ((canvasDom === null || canvasDom === void 0 ? void 0 : canvasDom.tagName) !== 'CANVAS') {
canvas = canvasDom === null || canvasDom === void 0 ? void 0 : canvasDom.getElementsByTagName('canvas')[0];
}
if (!canvas) return;
canvas.width = state.displayWidth;
canvas.height = state.displayHeight;
var ctx = canvas.getContext('2d');
canvas2dDraw(ctx);
}, [
canvas2dDraw
]);
var alipayDraw = (0, _react.useCallback)(function() {
var ctx = canvasAll.cropperCanvas.getContext('2d');
ctx && ctx.resetTransform();
canvas2dDraw(ctx);
}, [
canvas2dDraw,
canvasAll.cropperCanvas
]);
// 绘制显示的canvas内容
var draw = (0, _react.useCallback)(function() {
if (_taro.default.getEnv() === 'WEB') {
webDraw();
return;
}
if (showAlipayCanvas2D) {
alipayDraw();
return;
}
var src = drawImage.src, width = drawImage.width, height = drawImage.height, x = drawImage.x, y = drawImage.y;
var moveX = state.moveX, moveY = state.moveY, scale = state.scale, angle = state.angle, displayWidth = state.displayWidth, displayHeight = state.displayHeight, cropperWidth = state.cropperWidth;
var cropperCanvasContext = canvasAll.cropperCanvasContext;
var ctx = cropperCanvasContext;
if (!ctx) return;
// 绘制背景
ctx.clearRect(0, 0, displayWidth, displayHeight);
ctx.fillStyle = '#666';
ctx.setFillStyle('#666');
ctx.fillRect(0, 0, displayWidth, displayHeight);
ctx.stroke();
ctx.fill();
ctx.fillStyle = '#000';
ctx.setFillStyle('#000');
ctx.fillRect(space, (displayHeight - cropperWidth) / 2, cropperWidth, cropperWidth);
ctx.stroke();
ctx.fill();
// 绘制偏移量
ctx.translate(displayWidth / 2 + moveX, displayHeight / 2 + moveY);
ctx.rotate(Math.PI / 180 * angle);
ctx.scale(scale, scale);
ctx.drawImage(src, x, y, width, height);
ctx.draw();
}, [
drawImage,
state.scale,
state.angle,
state.moveX,
state.moveY
]);
(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 setDrawImgInfo = function setDrawImgInfo(image) {
return (0, _async_to_generator._)(function() {
var displayWidth, cropperWidth, copyDrawImg, imgWidth, imgHeight, isPortrait, rate, scale;
return (0, _ts_generator._)(this, function(_state) {
switch(_state.label){
case 0:
displayWidth = state.displayWidth, cropperWidth = state.cropperWidth;
copyDrawImg = (0, _object_spread._)({}, defDrawImage);
imgWidth = image.width, imgHeight = image.height;
copyDrawImg.src = image.path;
if (!(_taro.default.getEnv() === 'WEB')) return [
3,
2
];
return [
4,
dataURLToImage(image.path)
];
case 1:
copyDrawImg.src = _state.sent();
_state.label = 2;
case 2:
if (!showAlipayCanvas2D) return [
3,
4
];
return [
4,
dataURLToCanvasImage(canvasAll.cropperCanvas, image.path)
];
case 3:
copyDrawImg.src = _state.sent();
_state.label = 4;
case 4:
isPortrait = imgHeight > imgWidth;
rate = isPortrait ? imgWidth / imgHeight : imgHeight / imgWidth;
copyDrawImg.width = displayWidth;
copyDrawImg.height = isPortrait ? displayWidth / rate : displayWidth * rate;
copyDrawImg.x = -copyDrawImg.width / 2;
copyDrawImg.y = -copyDrawImg.height / 2;
setDrawImg(copyDrawImg);
scale = cropperWidth / (isPortrait ? copyDrawImg.width : copyDrawImg.height);
setState((0, _object_spread_props._)((0, _object_spread._)({}, state), {
defScale: scale
}));
resetScale(scale);
return [
2
];
}
});
})();
};
var chooseImage = function chooseImage() {
_taro.default.chooseImage({
count: 1,
// 可以指定是原图还是压缩图,默认二者都有
sizeType: sizeType,
sourceType: sourceType,
success: function success(res) {
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
var tempFiles = res.tempFiles;
!!tempFiles.length && imageChange(tempFiles[0]);
}
});
};
var imageChange = function imageChange(file) {
return (0, _async_to_generator._)(function() {
return (0, _ts_generator._)(this, function(_state) {
_taro.default.getImageInfo({
src: file.path
}).then(function(res) {
setVisible(true);
setDrawImgInfo(res);
});
return [
2
];
});
})();
};
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 _useState6 = (0, _sliced_to_array._)((0, _react.useState)({
startMoveX: 0,
startMoveY: 0,
startScale: 0,
startDistance: 0
}), 2), startMove = _useState6[0], setStartMove = _useState6[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();
isEmit && onCancel && onCancel();
};
// web裁剪图片
var confirmWEB = function confirmWEB() {
var cropperWidth = state.cropperWidth, displayHeight = state.displayHeight;
var canvasDom = document.getElementById(canvasAll.canvasId);
var canvas = canvasDom;
if ((canvasDom === null || canvasDom === void 0 ? void 0 : canvasDom.tagName) !== 'CANVAS') {
canvas = canvasDom === null || canvasDom === void 0 ? void 0 : canvasDom.getElementsByTagName('canvas')[0];
}
var width = cropperWidth;
var height = cropperWidth;
// 创建一个新的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, space * pixelRatio, (displayHeight - cropperWidth) / 2, width, height, 0, 0, width, height);
// 将裁剪后的内容转换为图片格式
var imageDataURL = croppedCanvas.toDataURL('image/png');
onConfirm && onConfirm(imageDataURL);
cancel(false);
};
// 支付宝基础库2.7.0以上支持,需要开启支付宝小程序canvas2d
var confirmALIPAY = function confirmALIPAY() {
var cropperWidth = state.cropperWidth, displayHeight = state.displayHeight;
var cropperCanvas = canvasAll.cropperCanvas;
_taro.default.canvasToTempFilePath({
canvas: cropperCanvas,
x: props.space,
y: (displayHeight - cropperWidth) / 2,
width: cropperWidth,
height: cropperWidth,
destWidth: cropperWidth,
destHeight: cropperWidth,
success: function success(res) {
return (0, _async_to_generator._)(function() {
var filePath;
return (0, _ts_generator._)(this, function(_state) {
filePath = res.tempFilePath;
onConfirm && onConfirm(filePath);
cancel(false);
return [
2
];
});
})();
}
});
};
// 裁剪图片
var confirm = function confirm() {
if (_taro.default.getEnv() === 'WEB') {
confirmWEB();
return;
}
if (showAlipayCanvas2D) {
confirmALIPAY();
return;
}
var cropperWidth = state.cropperWidth, displayHeight = state.displayHeight;
var canvasId = canvasAll.canvasId;
// 将编辑后的canvas内容转成图片
_taro.default.canvasToTempFilePath({
canvasId: canvasId,
x: props.space,
y: (displayHeight - cropperWidth) / 2,
width: cropperWidth,
height: cropperWidth,
destWidth: cropperWidth * systemInfo.pixelRatio,
destHeight: cropperWidth * systemInfo.pixelRatio,
success: function success(res) {
return (0, _async_to_generator._)(function() {
var filePath;
return (0, _ts_generator._)(this, function(_state) {
filePath = res.tempFilePath;
onConfirm && onConfirm(filePath);
cancel(false);
return [
2
];
});
})();
}
});
};
var ToolBar = function ToolBar() {
var actions = [
cancel,
reset,
rotate,
confirm
];
return /*#__PURE__*/ _react.default.createElement(_components.View, {
className: "".concat(classPrefix, "-popup-toolbar-flex")
}, actions.map(function(action, index) {
return /*#__PURE__*/ _react.default.createElement(_components.View, {
key: index,
className: "".concat(classPrefix, "-popup-toolbar-item"),
onClick: function onClick(_e) {
return action();
}
}, toolbar[index]);
}));
};
var CropperPopup = function CropperPopup() {
var canvasId = canvasAll.canvasId;
return /*#__PURE__*/ _react.default.createElement(_components.View, {
className: "".concat(classPrefix, "-popup"),
style: {
display: visible ? 'block' : 'none'
}
}, /*#__PURE__*/ _react.default.createElement(_components.Canvas, {
id: canvasId,
"canvas-id": canvasId,
type: showAlipayCanvas2D ? '2d' : undefined,
style: canvasStyle,
className: "".concat(classPrefix, "-popup-canvas")
}), /*#__PURE__*/ _react.default.createElement(_components.View, {
className: "".concat(classPrefix, "-popup-highlight"),
onTouchStart: onTouchStart,
onTouchMove: onTouchMove,
onTouchEnd: onTouchEnd
}, /*#__PURE__*/ _react.default.createElement(_components.View, {
className: "highlight",
style: highlightStyle
})), /*#__PURE__*/ _react.default.createElement(_components.View, {
className: toolbarPositionCls
}, /*#__PURE__*/ _react.default.createElement(ToolBar, null)));
};
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement(_components.View, (0, _object_spread_props._)((0, _object_spread._)({
className: cls
}, rest), {
style: style
}), children, /*#__PURE__*/ _react.default.createElement(_components.View, {
className: "nut-avatar-cropper-edit-text",
onClick: chooseImage
}, editText)), CropperPopup());
};
AvatarCropper.displayName = 'NutAvatarCropper';