feat: 编辑器添加平移和缩放容器功能
parent
9c7535fb28
commit
a7164e0332
|
|
@ -0,0 +1,15 @@
|
|||
import { Ref } from "vue";
|
||||
import { Container } from "./container";
|
||||
import { Block } from "./block";
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
export class Editor {
|
||||
public container: Ref<Container> = {} as Ref<Container>;
|
||||
public content: Ref<Block> = {} as Ref<Block>;
|
||||
|
||||
constructor() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
@ -3,4 +3,4 @@ export * from "./blocker";
|
|||
export * from "./container";
|
||||
export * from "./context";
|
||||
export * from "./ref-line";
|
||||
|
||||
export * from "./scene";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
import { Ref } from "vue";
|
||||
import { Container } from ".";
|
||||
|
||||
/**
|
||||
* 场景
|
||||
* @description 处理平移和缩放事件
|
||||
*/
|
||||
export class Scene {
|
||||
private startX = 0;
|
||||
private startY = 0;
|
||||
private cacheX = 0;
|
||||
private cacheY = 0;
|
||||
public minZoom = 0.5;
|
||||
public maxZoom = 10;
|
||||
public zoomStep = 0.1;
|
||||
|
||||
constructor(private container: Ref<Container>) {
|
||||
this.onMouseDown = this.onMouseDown.bind(this);
|
||||
this.onMouseMove = this.onMouseMove.bind(this);
|
||||
this.onMouseUp = this.onMouseUp.bind(this);
|
||||
this.onMouseWheel = this.onMouseWheel.bind(this);
|
||||
}
|
||||
|
||||
onMouseDown(e: MouseEvent) {
|
||||
this.startX = e.x;
|
||||
this.startY = e.y;
|
||||
this.cacheX = this.container.value.x;
|
||||
this.cacheY = this.container.value.y;
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
onMouseMove(e: MouseEvent) {
|
||||
this.container.value.x = this.cacheX + (e.x - this.startX);
|
||||
this.container.value.y = this.cacheY + (e.y - this.startY);
|
||||
}
|
||||
|
||||
onMouseUp() {
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
onMouseWheel(e: WheelEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
const el = e.currentTarget as HTMLElement;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = (e.clientX - rect.x) / this.container.value.zoom;
|
||||
const y = (e.clientY - rect.y) / this.container.value.zoom;
|
||||
const delta = -e.deltaY > 0 ? this.zoomStep : -this.zoomStep;
|
||||
|
||||
this.container.value.zoom += delta;
|
||||
if (this.container.value.zoom < this.minZoom) {
|
||||
this.container.value.zoom = this.minZoom;
|
||||
return;
|
||||
}
|
||||
if (this.container.value.zoom > this.maxZoom) {
|
||||
this.container.value.zoom = this.maxZoom;
|
||||
return;
|
||||
}
|
||||
|
||||
this.container.value.x += -x * delta + el.offsetWidth * (delta / 2);
|
||||
this.container.value.y += -y * delta + el.offsetHeight * (delta / 2);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-modal :visible="true" :fullscreen="true" :footer="false" class="ani-modal">
|
||||
<div class="w-full h-full bg-slate-100 grid grid-rows-[auto_1fr]">
|
||||
<div class="w-full h-full bg-slate-100 grid grid-rows-[auto_1fr] select-none">
|
||||
<div class="h-13 bg-white border-b border-slate-200 z-10">
|
||||
<panel-header></panel-header>
|
||||
</div>
|
||||
|
|
@ -119,8 +119,7 @@ const setContainerOrigin = () => {
|
|||
const { width, height } = el.getBoundingClientRect();
|
||||
const wZoom = width / container.value.width;
|
||||
const hZoom = height / container.value.width;
|
||||
const zoom = Math.floor((wZoom > hZoom ? wZoom : hZoom) * 100) / 100;
|
||||
// console.log(width, height, wZoom, hZoom, zoom);
|
||||
const zoom = Math.floor((wZoom > hZoom ? wZoom : hZoom) * 10000) / 10000;
|
||||
container.value.zoom = zoom;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -106,6 +106,10 @@ const onItemMouseup = () => {
|
|||
<style lang="less" scoped>
|
||||
.resizer {
|
||||
outline: 1px dashed #ccc;
|
||||
outline-offset: -1px;
|
||||
&::before {
|
||||
outline-offset: -1px;
|
||||
}
|
||||
&:hover {
|
||||
outline-color: rgb(var(--primary-6));
|
||||
background-color: rgba(var(--primary-1), 0.5);
|
||||
|
|
@ -117,7 +121,7 @@ const onItemMouseup = () => {
|
|||
background-color: rgba(var(--primary-1), 0.5);
|
||||
}
|
||||
}
|
||||
::v-deep .vdr-stick {
|
||||
:deep(.vdr-stick) {
|
||||
border-color: rgb(var(--primary-6));
|
||||
}
|
||||
:deep(.content-container) {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,8 @@
|
|||
@click="onClick"
|
||||
@drop="onDragDrop"
|
||||
@dragover.prevent
|
||||
@wheel="onMouseWheel"
|
||||
@mousedown="onMouseDown"
|
||||
@mousemove="onMouseMove"
|
||||
@wheel="scene.onMouseWheel"
|
||||
@mousedown="scene.onMouseDown"
|
||||
>
|
||||
<ani-block v-for="block in blocks" :key="block.id" :data="block" :container="container"></ani-block>
|
||||
<template v-if="refLine.active.value">
|
||||
|
|
@ -54,11 +53,12 @@
|
|||
import { cloneDeep } from "lodash-es";
|
||||
import { CSSProperties } from "vue";
|
||||
import { BlockerMap } from "../blocks";
|
||||
import { ContextKey } from "../config";
|
||||
import { ContextKey, Scene } from "../config";
|
||||
import AniBlock from "./components/block.vue";
|
||||
import AniHeader from "./components/header.vue";
|
||||
|
||||
const { blocks, container, refLine, setCurrentBlock } = inject(ContextKey)!;
|
||||
const scene = new Scene(container);
|
||||
|
||||
/**
|
||||
* 清空当前组件
|
||||
|
|
@ -69,52 +69,6 @@ const onClick = (e: Event) => {
|
|||
}
|
||||
};
|
||||
|
||||
const isStart = ref(false);
|
||||
const position = ref({
|
||||
x: 0,
|
||||
y: 0,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
mouseX: 0,
|
||||
mouseY: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
* 拖拽容器:开始
|
||||
*/
|
||||
const onMouseDown = (e: MouseEvent) => {
|
||||
isStart.value = true;
|
||||
position.value.startX = e.offsetX;
|
||||
position.value.startY = e.offsetY;
|
||||
};
|
||||
|
||||
/**
|
||||
* 拖拽容器:移动
|
||||
*/
|
||||
const onMouseMove = (e: MouseEvent) => {
|
||||
if (!isStart.value) {
|
||||
return;
|
||||
}
|
||||
const scale = container.value.zoom;
|
||||
container.value.x += (e.offsetX - position.value.startX) * scale;
|
||||
container.value.y += (e.offsetY - position.value.startY) * scale;
|
||||
};
|
||||
|
||||
/**
|
||||
* 拖拽容器:结束
|
||||
*/
|
||||
const onMouseUp = () => {
|
||||
isStart.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("mouseup", onMouseUp);
|
||||
});
|
||||
|
||||
/**
|
||||
* 容器样式
|
||||
*/
|
||||
|
|
@ -128,8 +82,6 @@ const containerStyle = computed(() => {
|
|||
backgroundImage: bgImage ? `url(${bgImage})` : undefined,
|
||||
backgroundSize: "100% 100%",
|
||||
transform: `translate3d(${x}px, ${y}px, 0) scale(${zoom})`,
|
||||
// transform: `matrix(${zoom}, 0, 0, ${zoom}, ${x}, ${y})`,
|
||||
// transformOrigin: "0 0",
|
||||
} as CSSProperties;
|
||||
});
|
||||
|
||||
|
|
@ -157,35 +109,6 @@ const onDragDrop = (e: DragEvent) => {
|
|||
y: e.offsetY,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 滚轮缩放容器
|
||||
*/
|
||||
const onMouseWheel = (e: WheelEvent) => {
|
||||
e.preventDefault();
|
||||
const prezoom = container.value.zoom;
|
||||
let zoom = prezoom;
|
||||
if (e.deltaY > 0) {
|
||||
zoom += 0.1;
|
||||
if (zoom > 10) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
zoom -= 0.1;
|
||||
if (zoom < 0.1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
zoom = parseFloat(zoom.toFixed(1));
|
||||
// const { x, y } = position.value
|
||||
// const x1 = x + e.offsetX;
|
||||
// const y1 = y + e.offsetY;
|
||||
// position.value.x = x1 - (x1 - x) * (zoom / prezoom);
|
||||
// position.value.y = y1 - (y1 - y) * (zoom / prezoom);
|
||||
// position.value.x = e.clientX;
|
||||
// position.value.y = e.clientY;
|
||||
container.value.zoom = zoom;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
Loading…
Reference in New Issue