nutui-taro-upgrade
Version:
@nutui/nutui-taro 对京东风格组件库的taro4 版本支持
675 lines (674 loc) • 22.6 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __pow = Math.pow;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
import { reactive, ref, computed, onMounted, watch, toRefs, resolveComponent, openBlock, createElementBlock, Fragment, createElementVNode, normalizeClass, renderSlot, createTextVNode, withModifiers, toDisplayString, withDirectives, normalizeStyle, createVNode, withCtx, vShow } from "vue";
import { Button as _sfc_main$1 } from "../button/Button.js";
import { c as createComponent } from "../component-669c158a.js";
import { a as preventDefault, c as clamp } from "../util-f26975e1.js";
import { IconFont } from "@nutui/icons-vue-taro";
import { u as useTouch } from "../index-7a7385e4.js";
import Taro, { useReady } from "@tarojs/taro";
import { _ as _export_sfc } from "../_plugin-vue_export-helper-cc2b3d55.js";
import "../with-install-783bc31f.js";
const compareVersion = (v1Old, v2Old) => {
const v1 = v1Old.split(".");
const v2 = v2Old.split(".");
const len = Math.max(v1.length, v2.length);
while (v1.length < len) {
v1.push("0");
}
while (v2.length < len) {
v2.push("0");
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i]);
const num2 = parseInt(v2[i]);
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
}
}
return 0;
};
const isWeapp = () => {
return process.env.TARO_ENV === "weapp";
};
function _easyCanvasContextBase(systemInfo, lowCallback, highCallback, targetVersion = "1.9.90") {
if (isWeapp() && compareVersion(systemInfo.SDKVersion, targetVersion) >= 0) {
highCallback();
} else {
lowCallback();
}
}
function easySetFillStyle(systemInfo, canvasContext, color) {
_easyCanvasContextBase(
systemInfo,
() => {
canvasContext.setFillStyle(color);
},
() => {
if (typeof color === "string") {
canvasContext.fillStyle = color;
}
}
);
}
const { create } = createComponent("avatar-cropper");
const _sfc_main = create({
components: {
NutButton: _sfc_main$1,
IconFont
},
props: {
maxZoom: {
type: Number,
default: 3
},
space: {
type: Number,
default: 10
},
toolbarPosition: {
type: String,
default: "bottom"
},
editText: {
type: String,
default: "编辑"
},
cancelText: {
type: String,
default: "取消"
},
confirmText: {
type: String,
default: "确定"
},
shape: {
type: String,
default: "square"
},
sizeType: {
type: Array,
default: () => ["original", "compressed"]
},
sourceType: {
type: Array,
default: () => ["album", "camera"]
}
},
emits: ["confirm", "cancel"],
setup(props, { emit, expose }) {
const state = reactive({
visible: false,
defScale: 1,
scale: 1,
angle: 0,
moveX: 0,
moveY: 0,
moving: false,
zooming: false,
displayWidth: 0,
displayHeight: 0,
cropperWidth: 0,
cropperHeight: 0
});
const canvasAll = {
canvasId: `canvas-${Date.now()}`,
cropperCanvas: null,
cropperCanvasContext: null
};
const drawImage = ref({
x: 0,
// 在画布上x的坐标位置
y: 0,
// 在画布上y的坐标位置
width: 0,
// 要使用的图像的宽度
height: 0
// 要使用的图像的高度
});
let canvasImage = null;
const touch = useTouch();
const systemInfo = Taro.getSystemInfoSync();
const showCanvas2D = computed(() => {
return Taro.getEnv() === "ALIPAY" && parseInt(Taro.SDKVersion.replace(/\./g, "")) >= 270 || systemInfo.SDKVersion && parseInt(systemInfo.SDKVersion.replace(/\./g, "")) >= 290 && Taro.getEnv() === "WEAPP";
});
const showPixelRatio = Taro.getEnv() === "WEB" || showCanvas2D.value;
const pixelRatio = showPixelRatio ? systemInfo.pixelRatio : 1;
state.displayWidth = systemInfo.windowWidth * pixelRatio;
state.displayHeight = systemInfo.windowHeight * pixelRatio;
state.cropperWidth = state.cropperHeight = state.displayWidth - props.space * pixelRatio * 2;
useReady(() => {
if (showCanvas2D.value) {
const { canvasId } = canvasAll;
Taro.createSelectorQuery().select(`#${canvasId}`).node(({ node: canvas }) => {
canvas.width = state.displayWidth;
canvas.height = state.displayHeight;
canvasAll.cropperCanvas = canvas;
}).exec();
}
});
onMounted(() => {
const { canvasId } = canvasAll;
canvasAll.cropperCanvasContext = Taro.createCanvasContext(canvasId);
});
const isAngle = computed(() => {
return state.angle === 90 || state.angle === 270;
});
const highlightStyle = computed(() => {
const { cropperWidth } = state;
const width = cropperWidth / pixelRatio + "px";
const height = width;
return {
width,
height
};
});
const canvasStyle = computed(() => {
const { displayWidth, displayHeight } = state;
return {
width: `${displayWidth / pixelRatio}px`,
height: `${displayHeight / pixelRatio}px`
};
});
const cutCanvasStyle = computed(() => {
const { displayWidth, displayHeight, cropperWidth } = state;
return {
top: `${(displayHeight / pixelRatio - cropperWidth / pixelRatio) / 2}px`,
left: `${(displayWidth / pixelRatio - cropperWidth / pixelRatio) / 2}px`,
width: `${cropperWidth / pixelRatio}px`,
height: `${cropperWidth / pixelRatio}px`
};
});
const maxMoveX = computed(() => {
const { displayWidth, scale, cropperWidth } = state;
const { height } = drawImage.value;
if (isAngle.value) {
return Math.max(0, (height * scale - cropperWidth) / 2);
}
return Math.max(0, (displayWidth * scale - cropperWidth) / 2);
});
const maxMoveY = computed(() => {
const { displayWidth, scale, cropperWidth } = state;
const { height } = drawImage.value;
if (isAngle.value) {
return Math.max(0, (displayWidth * scale - cropperWidth) / 2);
}
return Math.max(0, (height * scale - cropperWidth) / 2);
});
const dataURLToImage = (dataURL) => {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = dataURL;
});
};
const dataURLToCanvasImage = (canvas, dataURL) => {
if (!canvas) {
return Promise.reject(new Error("Invalid canvas element"));
}
return new Promise((resolve) => {
const img = canvas.createImage();
img.onload = () => resolve(img);
img.src = dataURL;
});
};
const canvas2dDraw = (ctx) => {
if (!ctx)
return;
const { width, height, x, y } = drawImage.value;
const { moveX, moveY, scale, angle, displayWidth, displayHeight } = state;
ctx.clearRect(0, 0, displayWidth, displayHeight);
ctx.translate(displayWidth / 2 + moveX, displayHeight / 2 + moveY);
ctx.rotate(Math.PI / 180 * angle);
ctx.scale(scale, scale);
ctx.drawImage(canvasImage, x, y, width, height);
};
const webDraw = () => {
const { displayWidth, displayHeight } = state;
const canvasDom = document.getElementById(canvasAll.canvasId);
let canvas = canvasDom;
if ((canvasDom == null ? void 0 : canvasDom.tagName) !== "CANVAS") {
canvas = canvasDom == null ? void 0 : canvasDom.getElementsByTagName("canvas")[0];
canvas.width = displayWidth;
canvas.height = displayHeight;
}
const ctx = canvas.getContext("2d");
canvas2dDraw(ctx);
};
const canvas2dContextDraw = () => {
const { cropperCanvas } = canvasAll;
let ctx = cropperCanvas.getContext("2d");
ctx && ctx.resetTransform();
canvas2dDraw(ctx);
};
const draw = () => {
if (Taro.getEnv() === "WEB") {
webDraw();
return;
}
if (showCanvas2D.value) {
canvas2dContextDraw();
return;
}
const { width, height, x, y } = drawImage.value;
const { moveX, moveY, scale, angle, displayWidth, displayHeight, cropperWidth } = state;
const { cropperCanvasContext } = canvasAll;
let ctx = cropperCanvasContext;
if (!ctx)
return;
ctx.clearRect(0, 0, displayWidth, displayHeight);
easySetFillStyle(systemInfo, ctx, "#666");
ctx.fillRect(0, 0, displayWidth, displayHeight);
ctx.stroke();
ctx.fill();
easySetFillStyle(systemInfo, ctx, "#000");
ctx.fillRect(props.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(canvasImage, x, y, width, height);
ctx.draw();
};
const setDrawImg = (image) => __async(this, null, function* () {
const { displayWidth, cropperWidth } = state;
let drawImg = __spreadValues({}, drawImage.value);
const { width: imgWidth, height: imgHeight } = image;
canvasImage = image.path;
if (Taro.getEnv() === "WEB") {
canvasImage = yield dataURLToImage(image.path);
}
if (showCanvas2D.value) {
canvasImage = yield dataURLToCanvasImage(canvasAll.cropperCanvas, image.path);
}
const isPortrait = imgHeight > imgWidth;
const rate = isPortrait ? imgWidth / imgHeight : imgHeight / imgWidth;
drawImg.width = displayWidth;
drawImg.height = isPortrait ? displayWidth / rate : displayWidth * rate;
drawImg.x = -drawImg.width / 2;
drawImg.y = -drawImg.height / 2;
drawImage.value = drawImg;
state.defScale = cropperWidth / (isPortrait ? drawImg.width : drawImg.height);
resetScale();
draw();
});
const chooseImage = () => {
Taro.chooseImage({
count: 1,
// 可以指定是原图还是压缩图,默认二者都有
sizeType: props.sizeType,
sourceType: props.sourceType,
success: (res) => {
const { tempFiles } = res;
!!tempFiles.length && imageChange(tempFiles[0]);
}
});
};
const imageChange = (file) => __async(this, null, function* () {
Taro.getImageInfo({
src: file.path
}).then((res) => {
state.visible = true;
setDrawImg(res);
});
});
const resetScale = () => {
setScale(state.defScale);
state.moveX = 0;
state.moveY = 0;
state.angle = 0;
};
const setScale = (scale) => {
scale = clamp(scale, 0.3, +props.maxZoom + 1);
if (scale !== state.scale) {
state.scale = scale;
}
};
const getDistance = (touches) => Math.sqrt(__pow(touches[0].clientX - touches[1].clientX, 2) + __pow(touches[0].clientY - touches[1].clientY, 2));
let startMoveX;
let startMoveY;
let startScale;
let startDistance;
let fingerNum;
const onTouchStart = (event) => {
const { touches } = event;
const { offsetX } = touch;
touch.start(event);
fingerNum = touches.length;
startMoveX = state.moveX;
startMoveY = state.moveY;
state.moving = fingerNum === 1;
state.zooming = fingerNum === 2 && !offsetX.value;
if (state.zooming) {
startScale = state.scale;
startDistance = getDistance(event.touches);
}
};
const onTouchMove = (event) => {
const { touches } = event;
touch.move(event);
if (state.moving || state.zooming) {
preventDefault(event, true);
}
if (state.moving) {
const { deltaX, deltaY } = touch;
const moveX = deltaX.value * state.scale + startMoveX;
const moveY = deltaY.value * state.scale + startMoveY;
state.moveX = clamp(moveX, -maxMoveX.value, maxMoveX.value);
state.moveY = clamp(moveY, -maxMoveY.value, maxMoveY.value);
}
if (state.zooming && touches.length === 2) {
const distance = getDistance(touches);
const scale = startScale * distance / startDistance;
setScale(scale);
}
};
const onTouchEnd = (event) => {
let stopPropagation = false;
if (state.moving || state.zooming) {
stopPropagation = !(state.moving && startMoveX === state.moveX && startMoveY === state.moveY);
if (!event.touches.length) {
if (state.zooming) {
state.moveX = clamp(state.moveX, -maxMoveX.value, maxMoveX.value);
state.moveY = clamp(state.moveY, -maxMoveY.value, maxMoveY.value);
state.zooming = false;
}
state.moving = false;
startMoveX = 0;
startMoveY = 0;
startScale = state.defScale;
if (state.scale < state.defScale) {
resetScale();
}
if (state.scale > props.maxZoom) {
state.scale = +props.maxZoom;
}
}
}
preventDefault(event, stopPropagation);
touch.reset();
};
const reset = () => {
state.angle = 0;
};
const rotate = () => {
if (state.angle === 270) {
state.angle = 0;
return;
}
state.angle += 90;
};
const cancel = (isEmit = true) => {
state.visible = false;
resetScale();
isEmit && emit("cancel");
};
const confirmWEB = () => {
const { cropperWidth, displayHeight } = state;
const canvasDom = document.getElementById(canvasAll.canvasId);
let canvas = canvasDom;
if ((canvasDom == null ? void 0 : canvasDom.tagName) !== "CANVAS") {
canvas = canvasDom == null ? void 0 : canvasDom.getElementsByTagName("canvas")[0];
}
const width = cropperWidth;
const height = cropperWidth;
const croppedCanvas = document.createElement("canvas");
const croppedCtx = croppedCanvas.getContext("2d");
croppedCanvas.width = width;
croppedCanvas.height = height;
canvas && croppedCtx.drawImage(
canvas,
props.space * pixelRatio,
(displayHeight - cropperWidth) / 2,
width,
height,
0,
0,
width,
height
);
const imageDataURL = croppedCanvas.toDataURL("image/png");
emit("confirm", imageDataURL);
cancel(false);
};
const confirmCanvas2D = () => {
const { cropperWidth, displayHeight } = state;
const { cropperCanvas } = canvasAll;
const pixelRatio2 = Taro.getEnv() === "ALIPAY" ? 1 : systemInfo.pixelRatio;
Taro.canvasToTempFilePath({
canvas: cropperCanvas,
x: props.space,
y: (displayHeight - cropperWidth) / pixelRatio2 / 2,
width: cropperWidth / pixelRatio2,
height: cropperWidth / pixelRatio2,
destWidth: cropperWidth,
destHeight: cropperWidth,
success: (res) => __async(this, null, function* () {
let filePath = res.tempFilePath;
emit("confirm", filePath);
cancel(false);
return;
})
});
};
const confirm = () => {
if (Taro.getEnv() === "WEB") {
confirmWEB();
return;
}
if (showCanvas2D.value) {
confirmCanvas2D();
return;
}
const { cropperWidth, displayHeight } = state;
const { canvasId } = canvasAll;
Taro.canvasToTempFilePath({
canvasId,
x: props.space,
y: (displayHeight - cropperWidth) / 2,
width: cropperWidth,
height: cropperWidth,
destWidth: cropperWidth * systemInfo.pixelRatio,
destHeight: cropperWidth * systemInfo.pixelRatio,
success: (res) => __async(this, null, function* () {
let filePath = res.tempFilePath;
emit("confirm", filePath);
cancel(false);
return;
})
});
};
watch(
() => state.scale,
() => {
draw();
}
);
watch(
() => state.angle,
() => {
if (Math.abs(state.moveX) > maxMoveX.value) {
state.moveX = maxMoveX.value;
}
if (Math.abs(state.moveY) > maxMoveY.value) {
state.moveY = maxMoveY.value;
}
draw();
}
);
watch(
() => state.moveX,
() => {
draw();
}
);
watch(
() => state.moveY,
() => {
draw();
}
);
expose({
cancel,
reset,
rotate,
confirm
});
return __spreadProps(__spreadValues(__spreadValues({}, toRefs(state)), toRefs(canvasAll)), {
showCanvas2D,
highlightStyle,
canvasStyle,
cutCanvasStyle,
chooseImage,
reset,
rotate,
cancel,
confirm,
onTouchStart,
onTouchMove,
onTouchEnd
});
}
});
const _hoisted_1 = { class: "nut-cropper-popup" };
const _hoisted_2 = ["id", "canvas-id", "type"];
const _hoisted_3 = {
key: 1,
class: "flex-sb"
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_nut_button = resolveComponent("nut-button");
const _component_IconFont = resolveComponent("IconFont");
return openBlock(), createElementBlock(Fragment, null, [
createElementVNode("view", {
class: normalizeClass(["nut-avatar-cropper taro", { round: _ctx.shape === "round" }])
}, [
renderSlot(_ctx.$slots, "default"),
_cache[9] || (_cache[9] = createTextVNode()),
createElementVNode("view", {
class: "nut-avatar-cropper__edit-text",
onClick: _cache[0] || (_cache[0] = withModifiers((...args) => _ctx.chooseImage && _ctx.chooseImage(...args), ["stop"]))
}, toDisplayString(_ctx.editText), 1)
], 2),
_cache[15] || (_cache[15] = createTextVNode()),
withDirectives(createElementVNode("view", _hoisted_1, [
createElementVNode("canvas", {
id: _ctx.canvasId,
"canvas-id": _ctx.canvasId,
type: _ctx.showCanvas2D ? "2d" : void 0,
style: normalizeStyle(_ctx.canvasStyle),
class: "nut-cropper-popup__canvas"
}, null, 12, _hoisted_2),
_cache[13] || (_cache[13] = createTextVNode()),
createElementVNode("view", {
class: "nut-cropper-popup__highlight",
onTouchstart: _cache[1] || (_cache[1] = (...args) => _ctx.onTouchStart && _ctx.onTouchStart(...args)),
onTouchmove: _cache[2] || (_cache[2] = (...args) => _ctx.onTouchMove && _ctx.onTouchMove(...args)),
onTouchend: _cache[3] || (_cache[3] = (...args) => _ctx.onTouchEnd && _ctx.onTouchEnd(...args)),
onTouchcancel: _cache[4] || (_cache[4] = (...args) => _ctx.onTouchEnd && _ctx.onTouchEnd(...args))
}, [
createElementVNode("view", {
class: normalizeClass(["highlight", { highlight__round: _ctx.shape === "round" }]),
style: normalizeStyle(_ctx.highlightStyle)
}, null, 6)
], 32),
_cache[14] || (_cache[14] = createTextVNode()),
createElementVNode("view", {
class: normalizeClass(["nut-cropper-popup__toolbar", [_ctx.toolbarPosition]])
}, [
_ctx.$slots.toolbar ? renderSlot(_ctx.$slots, "toolbar", { key: 0 }) : (openBlock(), createElementBlock("view", _hoisted_3, [
createElementVNode("view", {
class: "nut-cropper-popup__toolbar-item",
onClick: _cache[5] || (_cache[5] = ($event) => _ctx.cancel())
}, [
createVNode(_component_nut_button, { type: "danger" }, {
default: withCtx(() => [
createTextVNode(toDisplayString(_ctx.cancelText), 1)
]),
_: 1
})
]),
_cache[10] || (_cache[10] = createTextVNode()),
createElementVNode("view", {
class: "nut-cropper-popup__toolbar-item",
onClick: _cache[6] || (_cache[6] = (...args) => _ctx.reset && _ctx.reset(...args))
}, [
createVNode(_component_IconFont, {
name: "refresh2",
color: "#fff"
})
]),
_cache[11] || (_cache[11] = createTextVNode()),
createElementVNode("view", {
class: "nut-cropper-popup__toolbar-item",
onClick: _cache[7] || (_cache[7] = (...args) => _ctx.rotate && _ctx.rotate(...args))
}, [
createVNode(_component_IconFont, {
name: "retweet",
color: "#fff"
})
]),
_cache[12] || (_cache[12] = createTextVNode()),
createElementVNode("view", {
class: "nut-cropper-popup__toolbar-item",
onClick: _cache[8] || (_cache[8] = (...args) => _ctx.confirm && _ctx.confirm(...args))
}, [
createVNode(_component_nut_button, { type: "success" }, {
default: withCtx(() => [
createTextVNode(toDisplayString(_ctx.confirmText), 1)
]),
_: 1
})
])
]))
], 2)
], 512), [
[vShow, _ctx.visible]
])
], 64);
}
const index_taro = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
export {
index_taro as default
};