|
@@ -0,0 +1,460 @@
|
|
|
|
+<template>
|
|
|
|
+ <div
|
|
|
|
+ ref="elRef"
|
|
|
|
+ v-ele-move-directive.prevent.stop="{
|
|
|
|
+ moveStart,
|
|
|
|
+ moveElement,
|
|
|
|
+ moveStop: moveElementOver,
|
|
|
|
+ }"
|
|
|
|
+ :class="classes"
|
|
|
|
+ :style="styles"
|
|
|
|
+ >
|
|
|
|
+ <slot></slot>
|
|
|
|
+ <div class="resize-control">
|
|
|
|
+ <template v-for="(control, index) in controlPoints" :key="index">
|
|
|
|
+ <div
|
|
|
|
+ v-ele-move-directive.prevent.stop="{
|
|
|
|
+ moveElement: control.movePoint,
|
|
|
|
+ moveStop: control.movePointOver,
|
|
|
|
+ }"
|
|
|
|
+ :class="control.classes"
|
|
|
|
+ ></div>
|
|
|
|
+ </template>
|
|
|
|
+ <div class="control-line control-line-left"></div>
|
|
|
|
+ <div class="control-line control-line-right"></div>
|
|
|
|
+ <div class="control-line control-line-top"></div>
|
|
|
|
+ <div class="control-line control-line-bottom"></div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup lang="ts">
|
|
|
|
+import { reactive, ref, computed, onMounted, onBeforeMount } from "vue";
|
|
|
|
+import { vEleMoveDirective } from "../../directives/eleMove";
|
|
|
|
+import { objModifyAssign } from "../../utils/tool";
|
|
|
|
+
|
|
|
|
+import {
|
|
|
|
+ defaultActive,
|
|
|
|
+ defaultFitParent,
|
|
|
|
+ SizeData,
|
|
|
|
+ ActionType,
|
|
|
|
+ FitParentItem,
|
|
|
|
+ ContronItem,
|
|
|
|
+ PositionData,
|
|
|
|
+} from "./types";
|
|
|
|
+
|
|
|
|
+defineOptions({
|
|
|
|
+ name: "ElementResize",
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const emit = defineEmits<{
|
|
|
|
+ (event: "update:modelValue", data: SizeData): void;
|
|
|
|
+ (event: "change", data: SizeData): void;
|
|
|
|
+ (event: "resizeOver", data: SizeData): void;
|
|
|
|
+ (event: "onClick"): void;
|
|
|
|
+}>();
|
|
|
|
+
|
|
|
|
+interface Props {
|
|
|
|
+ modelValue: SizeData;
|
|
|
|
+ active?: ActionType[];
|
|
|
|
+ move?: boolean;
|
|
|
|
+ minWidth?: number;
|
|
|
|
+ maxWidth?: number;
|
|
|
|
+ minHeight?: number;
|
|
|
|
+ maxHeight?: number;
|
|
|
|
+ fitParent?: FitParentItem[];
|
|
|
|
+ isCompact?: boolean;
|
|
|
|
+}
|
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
|
+ active: () => [...defaultActive],
|
|
|
|
+ move: true,
|
|
|
|
+ minWidth: 30,
|
|
|
|
+ maxWidth: 0,
|
|
|
|
+ minHeight: 30,
|
|
|
|
+ maxHeight: 0,
|
|
|
|
+ fitParent: () => [...defaultFitParent],
|
|
|
|
+ isCompact: false,
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const sizePosOrigin = reactive({ x: 0, y: 0, w: 0, h: 0 });
|
|
|
|
+const sizePos = reactive({ x: 0, y: 0, w: 0, h: 0 });
|
|
|
|
+const offsetTopOrigin = ref(0);
|
|
|
|
+const lastSizePos = reactive({ x: 0, y: 0, w: 0, h: 0 });
|
|
|
|
+const initOver = ref(false);
|
|
|
|
+const controlPoints = ref<ContronItem[]>([]);
|
|
|
|
+const parentNodeSize = reactive({ w: 0, h: 0 });
|
|
|
|
+const elRef = ref();
|
|
|
|
+
|
|
|
|
+const styles = computed(() => {
|
|
|
|
+ return initOver.value
|
|
|
|
+ ? {
|
|
|
|
+ left: `${sizePos.x}px`,
|
|
|
|
+ top: `${sizePos.y}px`,
|
|
|
|
+ width: `${sizePos.w}px`,
|
|
|
|
+ height: `${sizePos.h}px`,
|
|
|
|
+ zIndex: props.modelValue.zindex || "auto",
|
|
|
|
+ position: "absolute",
|
|
|
|
+ }
|
|
|
|
+ : {};
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const classes = computed(() => {
|
|
|
|
+ return [
|
|
|
|
+ "element-resize",
|
|
|
|
+ {
|
|
|
|
+ "element-resize-move": props.move,
|
|
|
|
+ "element-resize-init": initOver.value,
|
|
|
|
+ "element-resize-compact": props.isCompact,
|
|
|
|
+ },
|
|
|
|
+ ];
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const fitParentTypeWidth = computed(() => {
|
|
|
|
+ return props.fitParent.includes("w");
|
|
|
|
+});
|
|
|
|
+const fitParentTypeHeight = computed(() => {
|
|
|
|
+ return props.fitParent.includes("h");
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+function initControlPoints() {
|
|
|
|
+ const actions = {
|
|
|
|
+ l: moveLeftPoint,
|
|
|
|
+ r: moveRightPoint,
|
|
|
|
+ t: moveTopPoint,
|
|
|
|
+ b: moveBottomPoint,
|
|
|
|
+ lt: moveLeftTopPoint,
|
|
|
|
+ rt: moveRightTopPoint,
|
|
|
|
+ lb: moveLeftBottomPoint,
|
|
|
|
+ rb: moveRightBottomPoint,
|
|
|
|
+ };
|
|
|
|
+ controlPoints.value = props.active.map((type) => {
|
|
|
|
+ return {
|
|
|
|
+ classes: ["control-point", `control-point-${type}`],
|
|
|
|
+ movePoint: actions[type],
|
|
|
|
+ movePointOver: moveOver,
|
|
|
|
+ };
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function initSize() {
|
|
|
|
+ const elDom = elRef.value as HTMLElement;
|
|
|
|
+ const resizeDom = elDom.firstElementChild as Element;
|
|
|
|
+
|
|
|
|
+ objModifyAssign(sizePos, props.modelValue);
|
|
|
|
+ objModifyAssign(lastSizePos, props.modelValue);
|
|
|
|
+ objModifyAssign(sizePosOrigin, props.modelValue);
|
|
|
|
+ initOver.value = true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function fetchValidSizePos(
|
|
|
|
+ sizeData: SizeData,
|
|
|
|
+ actionType: ActionType | "move"
|
|
|
|
+) {
|
|
|
|
+ if (sizeData.w <= props.minWidth) {
|
|
|
|
+ sizeData.w = props.minWidth;
|
|
|
|
+ }
|
|
|
|
+ if (props.maxWidth !== 0 && sizeData.w >= props.maxWidth) {
|
|
|
|
+ sizeData.w = props.maxWidth;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (sizeData.h <= props.minHeight) {
|
|
|
|
+ sizeData.h = props.minHeight;
|
|
|
|
+ }
|
|
|
|
+ if (props.maxHeight !== 0 && sizeData.h >= props.maxHeight) {
|
|
|
|
+ sizeData.h = props.maxHeight;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!props.fitParent.length) {
|
|
|
|
+ objModifyAssign(lastSizePos, sizeData);
|
|
|
|
+ return sizeData;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 不同的定位方式,计算方式有差异
|
|
|
|
+ const elDom = elRef.value as HTMLElement;
|
|
|
|
+ const elParentDom = elDom.offsetParent as HTMLElement;
|
|
|
|
+ parentNodeSize.w = elParentDom.offsetWidth;
|
|
|
|
+ parentNodeSize.h = elParentDom.offsetHeight;
|
|
|
|
+
|
|
|
|
+ if (fitParentTypeWidth.value) {
|
|
|
|
+ if (sizeData.x <= 0) {
|
|
|
|
+ sizeData.x = 0;
|
|
|
|
+ if (actionType.includes("l")) sizeData.w = lastSizePos.w;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (sizeData.x + sizeData.w > parentNodeSize.w) {
|
|
|
|
+ sizeData.x = lastSizePos.x;
|
|
|
|
+ sizeData.w = parentNodeSize.w - sizeData.x;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (fitParentTypeHeight.value) {
|
|
|
|
+ if (sizeData.y <= 0) {
|
|
|
|
+ sizeData.y = 0;
|
|
|
|
+ if (actionType.includes("t")) sizeData.h = lastSizePos.h;
|
|
|
|
+ }
|
|
|
|
+ if (sizeData.y + sizeData.h > parentNodeSize.h) {
|
|
|
|
+ sizeData.y = lastSizePos.y;
|
|
|
|
+ sizeData.h = parentNodeSize.h - sizeData.y;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ objModifyAssign(lastSizePos, sizeData);
|
|
|
|
+ return sizeData;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function getLeftSize(left: number) {
|
|
|
|
+ return {
|
|
|
|
+ w: -left + sizePosOrigin.w,
|
|
|
|
+ x: left + sizePosOrigin.x,
|
|
|
|
+ };
|
|
|
|
+}
|
|
|
|
+function getRightSize(left: number) {
|
|
|
|
+ return {
|
|
|
|
+ w: left + sizePosOrigin.w,
|
|
|
|
+ };
|
|
|
|
+}
|
|
|
|
+function getTopSize(top: number) {
|
|
|
|
+ return {
|
|
|
|
+ h: -top + sizePosOrigin.h,
|
|
|
|
+ y: top + sizePosOrigin.y,
|
|
|
|
+ };
|
|
|
|
+}
|
|
|
|
+function getBottomSize(top: number) {
|
|
|
|
+ return {
|
|
|
|
+ h: top + sizePosOrigin.h,
|
|
|
|
+ };
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveLeftPoint({ left }: Pick<PositionData, "left">) {
|
|
|
|
+ const sp = { ...sizePos, ...getLeftSize(left) };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "l"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+function moveRightPoint({ left }: Pick<PositionData, "left">) {
|
|
|
|
+ const sp = { ...sizePos, ...getRightSize(left) };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "r"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveTopPoint({ top }: Pick<PositionData, "top">) {
|
|
|
|
+ const sp = { ...sizePos, ...getTopSize(top) };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "t"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveBottomPoint({ top }: Pick<PositionData, "top">) {
|
|
|
|
+ const sp = { ...sizePos, ...getBottomSize(top) };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "b"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveLeftTopPoint({ left, top }: PositionData) {
|
|
|
|
+ const sp = {
|
|
|
|
+ ...sizePos,
|
|
|
|
+ ...getLeftSize(left),
|
|
|
|
+ ...getTopSize(top),
|
|
|
|
+ };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "lt"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveRightTopPoint({ left, top }: PositionData) {
|
|
|
|
+ const sp = {
|
|
|
|
+ ...sizePos,
|
|
|
|
+ ...getRightSize(left),
|
|
|
|
+ ...getTopSize(top),
|
|
|
|
+ };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "rt"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveLeftBottomPoint({ left, top }: PositionData) {
|
|
|
|
+ const sp = {
|
|
|
|
+ ...sizePos,
|
|
|
|
+ ...getLeftSize(left),
|
|
|
|
+ ...getBottomSize(top),
|
|
|
|
+ };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "lb"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveRightBottomPoint({ left, top }: PositionData) {
|
|
|
|
+ const sp = {
|
|
|
|
+ ...sizePos,
|
|
|
|
+ ...getRightSize(left),
|
|
|
|
+ ...getBottomSize(top),
|
|
|
|
+ };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "rb"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveOver() {
|
|
|
|
+ objModifyAssign(sizePosOrigin, sizePos);
|
|
|
|
+ objModifyAssign(lastSizePos, sizePos);
|
|
|
|
+ emitChange();
|
|
|
|
+ emit("resizeOver", sizePos);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveStart() {
|
|
|
|
+ emit("onClick");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveElement({ left, top }: PositionData) {
|
|
|
|
+ if (!props.move) return;
|
|
|
|
+
|
|
|
|
+ const sp = {
|
|
|
|
+ ...sizePos,
|
|
|
|
+ ...{
|
|
|
|
+ x: left + sizePosOrigin.x,
|
|
|
|
+ y: top + sizePosOrigin.y,
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ objModifyAssign(sizePos, fetchValidSizePos(sp, "move"));
|
|
|
|
+ emitChange();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function moveElementOver() {
|
|
|
|
+ if (!props.move) return;
|
|
|
|
+ moveOver();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function emitChange() {
|
|
|
|
+ emit("update:modelValue", sizePos);
|
|
|
|
+ emit("change", sizePos);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+onMounted(() => {
|
|
|
|
+ initSize();
|
|
|
|
+});
|
|
|
|
+onBeforeMount(() => {
|
|
|
|
+ initControlPoints();
|
|
|
|
+});
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="less" scoped>
|
|
|
|
+.element-resize {
|
|
|
|
+ position: static;
|
|
|
|
+ z-index: auto;
|
|
|
|
+ background: transparent;
|
|
|
|
+ box-sizing: content-box;
|
|
|
|
+
|
|
|
|
+ &-move {
|
|
|
|
+ cursor: move;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &-init {
|
|
|
|
+ > div:first-child {
|
|
|
|
+ width: 100% !important;
|
|
|
|
+ height: 100% !important;
|
|
|
|
+ position: relative !important;
|
|
|
|
+ top: 0 !important;
|
|
|
|
+ left: 0 !important;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ .control-point {
|
|
|
|
+ position: absolute;
|
|
|
|
+ width: 12px;
|
|
|
|
+ height: 12px;
|
|
|
|
+ background: #f53f3f;
|
|
|
|
+ z-index: 99;
|
|
|
|
+ &-l {
|
|
|
|
+ left: 0;
|
|
|
|
+ top: 50%;
|
|
|
|
+ width: 12px;
|
|
|
|
+ height: 12px;
|
|
|
|
+ margin-top: -6px;
|
|
|
|
+ border-radius: 0;
|
|
|
|
+ cursor: w-resize;
|
|
|
|
+ }
|
|
|
|
+ &-lt {
|
|
|
|
+ left: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ cursor: nw-resize;
|
|
|
|
+ }
|
|
|
|
+ &-lb {
|
|
|
|
+ left: 0;
|
|
|
|
+ bottom: 0;
|
|
|
|
+ cursor: sw-resize;
|
|
|
|
+ }
|
|
|
|
+ &-r {
|
|
|
|
+ right: 0;
|
|
|
|
+ top: 50%;
|
|
|
|
+ margin-top: -6px;
|
|
|
|
+ cursor: e-resize;
|
|
|
|
+ }
|
|
|
|
+ &-rt {
|
|
|
|
+ right: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ cursor: ne-resize;
|
|
|
|
+ }
|
|
|
|
+ &-rb {
|
|
|
|
+ right: 0;
|
|
|
|
+ bottom: 0;
|
|
|
|
+ cursor: se-resize;
|
|
|
|
+ }
|
|
|
|
+ &-t {
|
|
|
|
+ left: 50%;
|
|
|
|
+ top: 0;
|
|
|
|
+ margin-left: -6px;
|
|
|
|
+ cursor: n-resize;
|
|
|
|
+ }
|
|
|
|
+ &-b {
|
|
|
|
+ left: 50%;
|
|
|
|
+ bottom: 0;
|
|
|
|
+ margin-left: -6px;
|
|
|
|
+ cursor: s-resize;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ .control-line {
|
|
|
|
+ position: absolute;
|
|
|
|
+ z-index: 98;
|
|
|
|
+
|
|
|
|
+ &-left {
|
|
|
|
+ height: 100%;
|
|
|
|
+ left: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ border-left: 4px solid #f53f3f;
|
|
|
|
+ }
|
|
|
|
+ &-right {
|
|
|
|
+ height: 100%;
|
|
|
|
+ right: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ border-left: 4px solid #f53f3f;
|
|
|
|
+ }
|
|
|
|
+ &-top {
|
|
|
|
+ width: 100%;
|
|
|
|
+ left: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ border-top: 4px solid #f53f3f;
|
|
|
|
+ }
|
|
|
|
+ &-bottom {
|
|
|
|
+ width: 100%;
|
|
|
|
+ left: 0;
|
|
|
|
+ bottom: 0;
|
|
|
|
+ border-top: 4px solid #f53f3f;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &-compact {
|
|
|
|
+ .control-line {
|
|
|
|
+ &-left {
|
|
|
|
+ left: 0;
|
|
|
|
+ border-left: 1px dashed #bbb;
|
|
|
|
+ }
|
|
|
|
+ &-right {
|
|
|
|
+ right: 0;
|
|
|
|
+ border-left: 1px dashed #bbb;
|
|
|
|
+ }
|
|
|
|
+ &-top {
|
|
|
|
+ top: 0;
|
|
|
|
+ border-top: 1px dashed #bbb;
|
|
|
|
+ }
|
|
|
|
+ &-bottom {
|
|
|
|
+ bottom: 0;
|
|
|
|
+ border-top: 1px dashed #bbb;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|