feat: 编辑器添加辅助线
parent
85c5e68db7
commit
9c7535fb28
|
|
@ -2,7 +2,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="vdr"
|
class="vdr"
|
||||||
:style="positionStyle"
|
:style="{
|
||||||
|
top: rect.top + 'px',
|
||||||
|
left: rect.left + 'px',
|
||||||
|
zIndex: zIndex,
|
||||||
|
}"
|
||||||
:class="`${active || isActive ? 'active' : 'inactive'} ${contentClass ? contentClass : ''}`"
|
:class="`${active || isActive ? 'active' : 'inactive'} ${contentClass ? contentClass : ''}`"
|
||||||
@mousedown="bodyDown($event)"
|
@mousedown="bodyDown($event)"
|
||||||
@touchstart="bodyDown($event)"
|
@touchstart="bodyDown($event)"
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ interface MockParams {
|
||||||
|
|
||||||
export const mockLoad = async (params: MockParams) => {
|
export const mockLoad = async (params: MockParams) => {
|
||||||
const { page, size } = params;
|
const { page, size } = params;
|
||||||
await sleep(1000);
|
|
||||||
const counts = Array(15).fill(9);
|
const counts = Array(15).fill(9);
|
||||||
const data = counts.map((v, i) => {
|
const data = counts.map((v, i) => {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { InjectionKey, Ref } from "vue";
|
import { InjectionKey, Ref } from "vue";
|
||||||
import { Block } from "./block";
|
import { Block } from "./block";
|
||||||
import { Container } from "./container";
|
import { Container } from "./container";
|
||||||
|
import { ReferenceLine } from "./ref-line";
|
||||||
|
|
||||||
export interface Current {
|
export interface Current {
|
||||||
block: Block | null;
|
block: Block | null;
|
||||||
|
|
@ -20,6 +21,10 @@ export interface Context {
|
||||||
* 画布配置
|
* 画布配置
|
||||||
*/
|
*/
|
||||||
container: Ref<Container>;
|
container: Ref<Container>;
|
||||||
|
/**
|
||||||
|
* 参考线
|
||||||
|
*/
|
||||||
|
refLine: ReferenceLine;
|
||||||
/**
|
/**
|
||||||
* 设置当前组件
|
* 设置当前组件
|
||||||
* @param block 组件
|
* @param block 组件
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
export * from './block';
|
export * from "./block";
|
||||||
export * from './blocker';
|
export * from "./blocker";
|
||||||
export * from './container';
|
export * from "./container";
|
||||||
export * from './context';
|
export * from "./context";
|
||||||
|
export * from "./ref-line";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
import { Ref } from "vue";
|
||||||
|
import { Block } from "./block";
|
||||||
|
import { Current } from "./context";
|
||||||
|
import { getClosestValInSortedArr } from "./util";
|
||||||
|
|
||||||
|
export interface DragRect {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考线管理
|
||||||
|
*/
|
||||||
|
export class ReferenceLine {
|
||||||
|
private xYsMap = new Map<number, number[]>();
|
||||||
|
private yXsMap = new Map<number, number[]>();
|
||||||
|
private sortedXs: number[] = [];
|
||||||
|
private sortedYs: number[] = [];
|
||||||
|
private xLines: { y: number; xs: number[] }[] = [];
|
||||||
|
private yLines: { x: number; ys: number[] }[] = [];
|
||||||
|
public active = ref(false);
|
||||||
|
public xl = ref<{ x: number; y: number; w: number }[]>([]);
|
||||||
|
public yl = ref<{ x: number; y: number; h: number }[]>([]);
|
||||||
|
|
||||||
|
constructor(private blocks: Ref<Block[]>, private current: Ref<Current>) {
|
||||||
|
this.updateRefLine = this.updateRefLine.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录所有组件坐标
|
||||||
|
*/
|
||||||
|
recordBlocksXY() {
|
||||||
|
this.clear();
|
||||||
|
const { xYsMap, yXsMap, blocks, current } = this;
|
||||||
|
for (const block of blocks.value) {
|
||||||
|
if (block === current.value.block) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { minX, minY, midX, midY, maxX, maxY } = this.getBlockBox(block);
|
||||||
|
|
||||||
|
this.addBoxToMap(xYsMap, minX, [minY, maxY]);
|
||||||
|
this.addBoxToMap(xYsMap, midX, [minY, maxY]);
|
||||||
|
this.addBoxToMap(xYsMap, maxX, [minY, maxY]);
|
||||||
|
|
||||||
|
this.addBoxToMap(yXsMap, minY, [minX, maxX]);
|
||||||
|
this.addBoxToMap(yXsMap, midY, [minX, maxX]);
|
||||||
|
this.addBoxToMap(yXsMap, maxY, [minX, maxX]);
|
||||||
|
}
|
||||||
|
this.sortedXs = Array.from(xYsMap.keys()).sort((a, b) => a - b);
|
||||||
|
this.sortedYs = Array.from(yXsMap.keys()).sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加组件坐标
|
||||||
|
*/
|
||||||
|
addBoxToMap(map: Map<number, number[]>, xOrY: number, xsOrYs: number[]) {
|
||||||
|
if (!map.get(xOrY)) {
|
||||||
|
map.set(xOrY, []);
|
||||||
|
}
|
||||||
|
map.get(xOrY)?.push(...xsOrYs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取组件左中右坐标
|
||||||
|
*/
|
||||||
|
getBlockBox(block: Block) {
|
||||||
|
const { x, y, w, h } = block ?? {};
|
||||||
|
return {
|
||||||
|
minX: x,
|
||||||
|
minY: y,
|
||||||
|
midX: x + w / 2,
|
||||||
|
midY: y + h / 2,
|
||||||
|
maxX: x + w,
|
||||||
|
maxY: y + h,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取组件左中右坐标
|
||||||
|
*/
|
||||||
|
getRectBox(rect: DragRect) {
|
||||||
|
const { left: x, top: y, width: w, height: h } = rect;
|
||||||
|
return {
|
||||||
|
minX: x,
|
||||||
|
minY: y,
|
||||||
|
midX: x + w / 2,
|
||||||
|
midY: y + h / 2,
|
||||||
|
maxX: x + w,
|
||||||
|
maxY: y + h,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理数据
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
this.xYsMap.clear();
|
||||||
|
this.yXsMap.clear();
|
||||||
|
this.sortedXs = [];
|
||||||
|
this.sortedYs = [];
|
||||||
|
this.xl.value = [];
|
||||||
|
this.yl.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. 记录参考线
|
||||||
|
* 2. 找出最近的参考线
|
||||||
|
* 3. 计算偏移距离
|
||||||
|
* 4. 标记参考线段
|
||||||
|
* 5. 更新组件坐标
|
||||||
|
* 6. 绘制参考线段
|
||||||
|
*/
|
||||||
|
updateRefLine(rect: DragRect) {
|
||||||
|
this.xLines = [];
|
||||||
|
this.yLines = [];
|
||||||
|
const box = this.getRectBox(rect);
|
||||||
|
const { xYsMap, yXsMap, sortedXs, sortedYs } = this;
|
||||||
|
if (!sortedXs.length && !sortedYs.length) {
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
let offsetX: number | undefined = undefined;
|
||||||
|
let offsetY: number | undefined = undefined;
|
||||||
|
|
||||||
|
// 离最近X的距离
|
||||||
|
const closetMinX = getClosestValInSortedArr(sortedXs, box.minX);
|
||||||
|
const closetMidX = getClosestValInSortedArr(sortedXs, box.midX);
|
||||||
|
const closetMaxX = getClosestValInSortedArr(sortedXs, box.maxX);
|
||||||
|
const distMinX = Math.abs(closetMinX - box.minX);
|
||||||
|
const distMidX = Math.abs(closetMidX - box.midX);
|
||||||
|
const distMaxX = Math.abs(closetMaxX - box.maxX);
|
||||||
|
const closetDistX = Math.min(distMinX, distMidX, distMaxX);
|
||||||
|
|
||||||
|
// 离最近Y的距离
|
||||||
|
const closetMinY = getClosestValInSortedArr(sortedYs, box.minY);
|
||||||
|
const closetMidY = getClosestValInSortedArr(sortedYs, box.midY);
|
||||||
|
const closetMaxY = getClosestValInSortedArr(sortedYs, box.maxY);
|
||||||
|
const distMinY = Math.abs(closetMinY - box.minY);
|
||||||
|
const distMidY = Math.abs(closetMidY - box.midY);
|
||||||
|
const distMaxY = Math.abs(closetMaxY - box.maxY);
|
||||||
|
const closetDistY = Math.min(distMinY, distMidY, distMaxY);
|
||||||
|
|
||||||
|
const isEqualNum = (a: number, b: number) => Math.abs(a - b) < 0.00001;
|
||||||
|
const tol = 5 / 0.7;
|
||||||
|
|
||||||
|
if (closetDistX <= tol) {
|
||||||
|
if (isEqualNum(closetDistX, distMinX)) {
|
||||||
|
offsetX = closetMinX - box.minX;
|
||||||
|
} else if (isEqualNum(closetDistX, distMidX)) {
|
||||||
|
offsetX = closetMidX - box.midX;
|
||||||
|
} else if (isEqualNum(closetDistX, distMaxX)) {
|
||||||
|
offsetX = closetMaxX - box.maxX;
|
||||||
|
} else {
|
||||||
|
throw new Error("un");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closetDistY <= tol) {
|
||||||
|
if (isEqualNum(closetDistY, distMinY)) {
|
||||||
|
offsetY = closetMinY - box.minY;
|
||||||
|
} else if (isEqualNum(closetDistY, distMidY)) {
|
||||||
|
offsetY = closetMidY - box.midY;
|
||||||
|
} else if (isEqualNum(closetDistY, distMaxY)) {
|
||||||
|
offsetY = closetMaxY - box.maxY;
|
||||||
|
} else {
|
||||||
|
throw new Error("un");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetBox = { ...box };
|
||||||
|
|
||||||
|
if (offsetX !== undefined) {
|
||||||
|
targetBox.minX += offsetX;
|
||||||
|
targetBox.midX += offsetX;
|
||||||
|
targetBox.maxX += offsetX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsetY !== undefined) {
|
||||||
|
targetBox.minY += offsetY;
|
||||||
|
targetBox.midY += offsetY;
|
||||||
|
targetBox.maxY += offsetY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsetX !== undefined) {
|
||||||
|
if (isEqualNum(0, closetMinX - targetBox.minX)) {
|
||||||
|
this.yLines.push({
|
||||||
|
x: closetMinX,
|
||||||
|
ys: [targetBox.minY, targetBox.maxY, ...(xYsMap.get(closetMinX) ?? [])],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isEqualNum(0, closetMidX - targetBox.midX)) {
|
||||||
|
this.yLines.push({
|
||||||
|
x: closetMidX,
|
||||||
|
ys: [targetBox.midX, ...(xYsMap.get(closetMidX) ?? [])],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isEqualNum(0, closetMaxX - targetBox.maxX)) {
|
||||||
|
this.yLines.push({
|
||||||
|
x: closetMaxX,
|
||||||
|
ys: [targetBox.minY, targetBox.maxY, ...(xYsMap.get(closetMaxX) ?? [])],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsetY !== undefined) {
|
||||||
|
if (isEqualNum(0, closetMinY - targetBox.minY)) {
|
||||||
|
this.xLines.push({
|
||||||
|
y: closetMinY,
|
||||||
|
xs: [targetBox.minX, targetBox.maxX, ...(yXsMap.get(closetMinY) ?? [])],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isEqualNum(0, closetMidY - targetBox.midY)) {
|
||||||
|
this.xLines.push({
|
||||||
|
y: closetMidY,
|
||||||
|
xs: [targetBox.midX, ...(yXsMap.get(closetMidY) ?? [])],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (isEqualNum(0, closetMaxY - targetBox.maxY)) {
|
||||||
|
this.xLines.push({
|
||||||
|
y: closetMaxY,
|
||||||
|
xs: [targetBox.minX, targetBox.maxX, ...(yXsMap.get(closetMaxY) ?? [])],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const yl: any[] = [];
|
||||||
|
for (const line of this.yLines) {
|
||||||
|
const y = Math.min(...line.ys);
|
||||||
|
const h = Math.max(...line.ys) - y;
|
||||||
|
yl.push({ x: line.x, y, h });
|
||||||
|
}
|
||||||
|
|
||||||
|
const xl: any[] = [];
|
||||||
|
for (const line of this.xLines) {
|
||||||
|
const x = Math.min(...line.xs);
|
||||||
|
const w = Math.max(...line.xs) - x;
|
||||||
|
xl.push({ y: line.y, x, w });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.yl.value = yl;
|
||||||
|
this.xl.value = xl;
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: offsetX,
|
||||||
|
y: offsetY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* 找出数组中最近的元素
|
||||||
|
*/
|
||||||
|
export function getClosestValInSortedArr(sortedArr: number[], target: number) {
|
||||||
|
if (sortedArr.length === 0) {
|
||||||
|
throw new Error("sortedArr can not be empty");
|
||||||
|
}
|
||||||
|
if (sortedArr.length === 1) {
|
||||||
|
return sortedArr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
let left = 0;
|
||||||
|
let right = sortedArr.length - 1;
|
||||||
|
|
||||||
|
while (left <= right) {
|
||||||
|
const mid = Math.floor((left + right) / 2);
|
||||||
|
|
||||||
|
if (sortedArr[mid] === target) {
|
||||||
|
return sortedArr[mid];
|
||||||
|
} else if (sortedArr[mid] < target) {
|
||||||
|
left = mid + 1;
|
||||||
|
} else {
|
||||||
|
right = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left >= sortedArr.length) {
|
||||||
|
return sortedArr[right];
|
||||||
|
}
|
||||||
|
if (right < 0) {
|
||||||
|
return sortedArr[left];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.abs(sortedArr[right] - target) <= Math.abs(sortedArr[left] - target) ? sortedArr[right] : sortedArr[left];
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Block, Container, ContextKey } from "./config";
|
import { Block, Container, ContextKey, ReferenceLine } from "./config";
|
||||||
import PanelHeader from "./panel-header/index.vue";
|
import PanelHeader from "./panel-header/index.vue";
|
||||||
import PanelLeft from "./panel-left/index.vue";
|
import PanelLeft from "./panel-left/index.vue";
|
||||||
import PanelMain from "./panel-main/index.vue";
|
import PanelMain from "./panel-main/index.vue";
|
||||||
|
|
@ -43,6 +43,11 @@ const current = ref({
|
||||||
*/
|
*/
|
||||||
const blocks = ref<Block[]>([]);
|
const blocks = ref<Block[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考线
|
||||||
|
*/
|
||||||
|
const refLine = new ReferenceLine(blocks, current);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 画布容器
|
* 画布容器
|
||||||
*/
|
*/
|
||||||
|
|
@ -127,6 +132,7 @@ provide(ContextKey, {
|
||||||
current,
|
current,
|
||||||
container,
|
container,
|
||||||
blocks,
|
blocks,
|
||||||
|
refLine,
|
||||||
setCurrentBlock,
|
setCurrentBlock,
|
||||||
setContainerOrigin,
|
setContainerOrigin,
|
||||||
loadData,
|
loadData,
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,11 @@
|
||||||
:isResizable="data.resizable"
|
:isResizable="data.resizable"
|
||||||
:style="blockStyle"
|
:style="blockStyle"
|
||||||
:class="'resizer'"
|
:class="'resizer'"
|
||||||
@dragging="onItemDragOrResize"
|
@dragging="onItemDragging"
|
||||||
@resizing="onItemDragOrResize"
|
@resizing="onItemResizing"
|
||||||
@activated="setCurrentBlock(data)"
|
@activated="onItemActivated(data)"
|
||||||
|
@mousedown="onItemMouseDown"
|
||||||
|
@mouseup="onItemMouseup"
|
||||||
>
|
>
|
||||||
<component :is="BlockerMap[data.type].render" :data="data" />
|
<component :is="BlockerMap[data.type].render" :data="data" />
|
||||||
</drag-resizer>
|
</drag-resizer>
|
||||||
|
|
@ -39,8 +41,11 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { setCurrentBlock } = inject(ContextKey)!;
|
const { setCurrentBlock, refLine } = inject(ContextKey)!;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件样式
|
||||||
|
*/
|
||||||
const blockStyle = computed(() => {
|
const blockStyle = computed(() => {
|
||||||
const { bgColor, bgImage } = props.data;
|
const { bgColor, bgImage } = props.data;
|
||||||
return {
|
return {
|
||||||
|
|
@ -50,12 +55,52 @@ const blockStyle = computed(() => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const onItemDragOrResize = (rect: any) => {
|
/**
|
||||||
|
* 拖拽组件
|
||||||
|
*/
|
||||||
|
const onItemDragging = (rect: any) => {
|
||||||
|
if (refLine.active.value) {
|
||||||
|
const { x = 0, y = 0 } = refLine.updateRefLine(rect);
|
||||||
|
rect.left += x;
|
||||||
|
rect.top += y;
|
||||||
|
}
|
||||||
props.data.x = rect.left;
|
props.data.x = rect.left;
|
||||||
props.data.y = rect.top;
|
props.data.y = rect.top;
|
||||||
props.data.w = rect.width;
|
props.data.w = rect.width;
|
||||||
props.data.h = rect.height;
|
props.data.h = rect.height;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉伸组件
|
||||||
|
*/
|
||||||
|
const onItemResizing = (rect: any) => {
|
||||||
|
props.data.x = rect.left;
|
||||||
|
props.data.y = rect.top;
|
||||||
|
props.data.w = rect.width;
|
||||||
|
props.data.h = rect.height;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按下鼠标
|
||||||
|
*/
|
||||||
|
const onItemMouseDown = () => {
|
||||||
|
refLine.active.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 激活组件
|
||||||
|
*/
|
||||||
|
const onItemActivated = (block: Block) => {
|
||||||
|
setCurrentBlock(block);
|
||||||
|
refLine.recordBlocksXY();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 松开鼠标
|
||||||
|
*/
|
||||||
|
const onItemMouseup = () => {
|
||||||
|
refLine.active.value = false;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
@ -63,13 +108,13 @@ const onItemDragOrResize = (rect: any) => {
|
||||||
outline: 1px dashed #ccc;
|
outline: 1px dashed #ccc;
|
||||||
&:hover {
|
&:hover {
|
||||||
outline-color: rgb(var(--primary-6));
|
outline-color: rgb(var(--primary-6));
|
||||||
background-color: rgba(var(--primary-1), .5);
|
background-color: rgba(var(--primary-1), 0.5);
|
||||||
}
|
}
|
||||||
&.active {
|
&.active {
|
||||||
&::before {
|
&::before {
|
||||||
outline-style: solid;
|
outline-style: solid;
|
||||||
outline-color: rgb(var(--primary-6));
|
outline-color: rgb(var(--primary-6));
|
||||||
background-color: rgba(var(--primary-1), .5);
|
background-color: rgba(var(--primary-1), 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
::v-deep .vdr-stick {
|
::v-deep .vdr-stick {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
<ani-header :container="container"></ani-header>
|
<ani-header :container="container"></ani-header>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-full w-full overflow-hidden p-4">
|
<div class="h-full w-full overflow-hidden p-4">
|
||||||
<div class="juetan-editor-container w-full h-full flex items-center justify-center overflow-hidden relative bg-slate-50">
|
<div
|
||||||
|
class="juetan-editor-container w-full h-full flex items-center justify-center overflow-hidden relative bg-slate-50"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="relative"
|
class="relative"
|
||||||
:style="containerStyle"
|
:style="containerStyle"
|
||||||
|
|
@ -16,6 +18,32 @@
|
||||||
@mousemove="onMouseMove"
|
@mousemove="onMouseMove"
|
||||||
>
|
>
|
||||||
<ani-block v-for="block in blocks" :key="block.id" :data="block" :container="container"></ani-block>
|
<ani-block v-for="block in blocks" :key="block.id" :data="block" :container="container"></ani-block>
|
||||||
|
<template v-if="refLine.active.value">
|
||||||
|
<div
|
||||||
|
v-for="line in refLine.xl.value"
|
||||||
|
:key="line.y"
|
||||||
|
:style="{
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${line.x}px`,
|
||||||
|
top: `${line.y}px`,
|
||||||
|
width: `${line.w}px`,
|
||||||
|
height: '1px',
|
||||||
|
backgroundColor: 'red',
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
v-for="line in refLine.yl.value"
|
||||||
|
:key="line.x"
|
||||||
|
:style="{
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${line.x}px`,
|
||||||
|
top: `${line.y}px`,
|
||||||
|
width: '1px',
|
||||||
|
height: `${line.h}px`,
|
||||||
|
backgroundColor: 'red',
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -30,7 +58,7 @@ import { ContextKey } from "../config";
|
||||||
import AniBlock from "./components/block.vue";
|
import AniBlock from "./components/block.vue";
|
||||||
import AniHeader from "./components/header.vue";
|
import AniHeader from "./components/header.vue";
|
||||||
|
|
||||||
const { blocks, container, setCurrentBlock } = inject(ContextKey)!;
|
const { blocks, container, refLine, setCurrentBlock } = inject(ContextKey)!;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空当前组件
|
* 清空当前组件
|
||||||
|
|
@ -162,6 +190,17 @@ const onMouseWheel = (e: WheelEvent) => {
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.juetan-editor-container {
|
.juetan-editor-container {
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
|
--color: rgba(0, 0, 0, 0.2);
|
||||||
|
background: linear-gradient(
|
||||||
|
45deg,
|
||||||
|
var(--color) 25%,
|
||||||
|
transparent 25%,
|
||||||
|
transparent 75%,
|
||||||
|
var(--color) 75%,
|
||||||
|
var(--color) 100%
|
||||||
|
),
|
||||||
|
linear-gradient(45deg, var(--color) 25%, transparent 25%, transparent 75%, var(--color) 75%, var(--color) 100%);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
background-position: 0 0, 10px 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
v-if="visible"
|
v-if="visible"
|
||||||
:style="{
|
:style="{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
overflow: 'hidden',
|
||||||
width: `${container.width}px`,
|
width: `${container.width}px`,
|
||||||
height: `${container.height}px`,
|
height: `${container.height}px`,
|
||||||
backgroundImage: `url(${container.bgImage})`,
|
backgroundImage: `url(${container.bgImage})`,
|
||||||
|
|
@ -17,6 +18,7 @@
|
||||||
:key="block.id"
|
:key="block.id"
|
||||||
:style="{
|
:style="{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
overflow: 'hidden',
|
||||||
left: `${block.x}px`,
|
left: `${block.x}px`,
|
||||||
top: `${block.y}px`,
|
top: `${block.y}px`,
|
||||||
width: `${block.w}px`,
|
width: `${block.w}px`,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue