OverallCanvas
This commit is contained in:
346
src/component/Canvas/OverallCanvas/demo.vue
Normal file
346
src/component/Canvas/OverallCanvas/demo.vue
Normal file
@@ -0,0 +1,346 @@
|
||||
<template>
|
||||
<div class="demo">
|
||||
<div
|
||||
class="control"
|
||||
:class="{ active: item.id === activeId }"
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id"
|
||||
@click="onSelect(item.id)"
|
||||
>
|
||||
<div>
|
||||
<b>{{ item.name }}</b>
|
||||
<button
|
||||
v-if="index !== 0"
|
||||
@click="onMove(item.id, list[index - 1].id)"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
<button
|
||||
v-if="index !== list.length - 1"
|
||||
@click="onMove(item.id, list[index + 1].id)"
|
||||
>
|
||||
→
|
||||
</button>
|
||||
<button @click.stop="onDelete(item.id)">删除</button>
|
||||
</div>
|
||||
<div>
|
||||
<span>偏移X</span>
|
||||
<input type="number" v-model="item.location[0]" />
|
||||
</div>
|
||||
<div>
|
||||
<span>偏移Y</span>
|
||||
<input type="number" v-model="item.location[1]" />
|
||||
</div>
|
||||
<div>
|
||||
<span>角度</span>
|
||||
<input type="number" v-model="item.angle" />
|
||||
</div>
|
||||
<div>
|
||||
<span>缩放</span>
|
||||
<input type="number" v-model="item.scale[0]" />
|
||||
</div>
|
||||
<div>
|
||||
<span>水平间距</span>
|
||||
<input type="number" v-model="item.object.gapX" />
|
||||
</div>
|
||||
<div>
|
||||
<span>垂直间距</span>
|
||||
<input type="number" v-model="item.object.gapY" />
|
||||
</div>
|
||||
<hr />
|
||||
<hr />
|
||||
<div>
|
||||
<span>缩放X</span>
|
||||
<input type="number" v-model="item.object.scaleX" step="0.1" />
|
||||
</div>
|
||||
<div>
|
||||
<span>缩放Y</span>
|
||||
<input type="number" v-model="item.object.scaleY" step="0.1" />
|
||||
</div>
|
||||
<div>
|
||||
<span>X</span>
|
||||
<input type="number" v-model="item.object.left" />
|
||||
</div>
|
||||
<div>
|
||||
<span>Y</span>
|
||||
<input type="number" v-model="item.object.top" />
|
||||
</div>
|
||||
<div>
|
||||
<span>角度</span>
|
||||
<input type="number" v-model="item.object.angle" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>透明度</span>
|
||||
<input
|
||||
type="range"
|
||||
v-model="item.object.opacity"
|
||||
step="0.1"
|
||||
min="0"
|
||||
max="1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="onAdd">添加</button>
|
||||
<div class="box">
|
||||
<pingpu
|
||||
:list="list"
|
||||
ref="pingpuRef"
|
||||
@change-canvas="updateCanvas"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import pingpu from "./index.vue";
|
||||
const ACTIONS = {
|
||||
ADD: "add",
|
||||
UPDATE: "update",
|
||||
DELETE: "delete",
|
||||
SELECT: "select",
|
||||
SORT: "sort",
|
||||
};
|
||||
|
||||
const convertDotNotationToBracket = (str) =>
|
||||
str.replace(/(?:^|\.)(\d+)(?=\.|$)/g, "[#$1]").replace(/\[#/g, "[");
|
||||
const activeId = ref("1");
|
||||
const list = ref([
|
||||
{
|
||||
id: "1",
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [0, 0],
|
||||
scale: [1, 1],
|
||||
angle: 0,
|
||||
name: "Print1",
|
||||
priority: 1,
|
||||
object: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
opacity: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
gapX: 10,
|
||||
gapY: 20,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [0, 0],
|
||||
scale: [2, 2],
|
||||
angle: -45,
|
||||
name: "Print2",
|
||||
priority: 1,
|
||||
object: {
|
||||
top: 150,
|
||||
left: 250,
|
||||
scaleX: 0.5,
|
||||
scaleY: 0.5,
|
||||
opacity: 1,
|
||||
angle: 45,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
gapX: 0,
|
||||
gapY: 0,
|
||||
},
|
||||
},
|
||||
]);
|
||||
// 深拷贝
|
||||
const deepCopy = (obj) => JSON.parse(JSON.stringify(obj));
|
||||
const oldList = ref(deepCopy(list.value));
|
||||
const pingpuRef = ref(null);
|
||||
const updateCanvas = (arr) => {
|
||||
oldList.value = deepCopy(list.value);
|
||||
arr.forEach((item) => {
|
||||
list.value.forEach((v) => {
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
if (v.id === item.id) {
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
try {
|
||||
const key = item.key;
|
||||
const str = /^\[/.test(item.key)
|
||||
? "v" + key
|
||||
: "v." + key;
|
||||
eval(`${str} = item.value`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (item.action === ACTIONS.SELECT) {
|
||||
activeId.value = item.id;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onSelect = (id) => {
|
||||
activeId.value = id;
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
id: id,
|
||||
action: ACTIONS.SELECT,
|
||||
},
|
||||
]);
|
||||
};
|
||||
const onMove = (id, id2) => {
|
||||
const obj1 = list.value.find((v) => v.id === id);
|
||||
const obj2 = list.value.find((v) => v.id === id2);
|
||||
const index1 = list.value.indexOf(obj1);
|
||||
const index2 = list.value.indexOf(obj2);
|
||||
if (index1 < index2) {
|
||||
list.value.splice(index2, 0, list.value.splice(index1, 1)[0]);
|
||||
} else {
|
||||
list.value.splice(index1, 0, list.value.splice(index2, 1)[0]);
|
||||
}
|
||||
const ids = list.value.map((v) => v.id);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
action: ACTIONS.SORT,
|
||||
ids,
|
||||
},
|
||||
]);
|
||||
};
|
||||
const onDelete = (id) => {
|
||||
list.value = list.value.filter((v) => v.id !== id);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
id: id,
|
||||
action: ACTIONS.DELETE,
|
||||
},
|
||||
]);
|
||||
};
|
||||
const onAdd = () => {
|
||||
const obj = {
|
||||
id: Date.now().toString(),
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [0, 0],
|
||||
scale: [1, 1],
|
||||
angle: 0,
|
||||
name: "Print" + (list.value.length + 1),
|
||||
priority: 1,
|
||||
object: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
opacity: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
gapX: 10,
|
||||
gapY: 20,
|
||||
},
|
||||
};
|
||||
list.value.push(obj);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
action: ACTIONS.ADD,
|
||||
data: obj,
|
||||
},
|
||||
]);
|
||||
};
|
||||
watch(
|
||||
() => list.value,
|
||||
() => updateList(),
|
||||
{ deep: true }
|
||||
);
|
||||
// 监听列表变化属性变更
|
||||
const updateList = () => {
|
||||
const changeList = [];
|
||||
oldList.value.forEach((oldItem) => {
|
||||
const newItem = list.value.find((v) => v.id === oldItem.id);
|
||||
if (newItem) {
|
||||
const arr = findDiffProps(oldItem, newItem);
|
||||
arr.forEach((item) => {
|
||||
changeList.push({
|
||||
...item,
|
||||
id: oldItem.id,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: convertDotNotationToBracket(item.key),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
changeList.push({
|
||||
id: oldItem.id,
|
||||
action: ACTIONS.DELETE,
|
||||
});
|
||||
}
|
||||
});
|
||||
oldList.value = deepCopy(list.value);
|
||||
if (changeList.length) {
|
||||
pingpuRef.value.updataList(changeList);
|
||||
}
|
||||
};
|
||||
// 递归查找不同的属性
|
||||
const findDiffProps = (obj1, obj2, diffProps = [], path = "") => {
|
||||
for (const key in obj1) {
|
||||
if (typeof obj1[key] === "object") {
|
||||
findDiffProps(obj1[key], obj2[key], diffProps, `${path}${key}.`);
|
||||
} else if (obj1[key] !== obj2[key]) {
|
||||
diffProps.push({
|
||||
key: `${path}${key}`,
|
||||
value: obj2[key],
|
||||
});
|
||||
}
|
||||
}
|
||||
return diffProps;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.demo {
|
||||
> .control {
|
||||
display: inline-block;
|
||||
border: 1px solid #000;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
&.active {
|
||||
border-color: rgb(17, 68, 223);
|
||||
box-shadow: 0 0 5px rgb(17, 68, 223);
|
||||
}
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
> * {
|
||||
margin-right: 10px;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
> span {
|
||||
width: 80px;
|
||||
}
|
||||
> input:not([type="range"]) {
|
||||
padding-left: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .box {
|
||||
width: 400px;
|
||||
height: 500px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,346 +1,10 @@
|
||||
<template>
|
||||
<div class="test">
|
||||
<div
|
||||
class="control"
|
||||
:class="{ active: item.id === activeId }"
|
||||
v-for="(item, index) in list"
|
||||
:key="item.id"
|
||||
@click="onSelect(item.id)"
|
||||
>
|
||||
<div>
|
||||
<b>{{ item.name }}</b>
|
||||
<button
|
||||
v-if="index !== 0"
|
||||
@click="onMove(item.id, list[index - 1].id)"
|
||||
>
|
||||
←
|
||||
</button>
|
||||
<button
|
||||
v-if="index !== list.length - 1"
|
||||
@click="onMove(item.id, list[index + 1].id)"
|
||||
>
|
||||
→
|
||||
</button>
|
||||
<button @click.stop="onDelete(item.id)">删除</button>
|
||||
</div>
|
||||
<div>
|
||||
<span>偏移X</span>
|
||||
<input type="number" v-model="item.location[0]" />
|
||||
</div>
|
||||
<div>
|
||||
<span>偏移Y</span>
|
||||
<input type="number" v-model="item.location[1]" />
|
||||
</div>
|
||||
<div>
|
||||
<span>角度</span>
|
||||
<input type="number" v-model="item.angle" />
|
||||
</div>
|
||||
<div>
|
||||
<span>缩放</span>
|
||||
<input type="number" v-model="item.scale[0]" />
|
||||
</div>
|
||||
<div>
|
||||
<span>水平间距</span>
|
||||
<input type="number" v-model="item.object.gapX" />
|
||||
</div>
|
||||
<div>
|
||||
<span>垂直间距</span>
|
||||
<input type="number" v-model="item.object.gapY" />
|
||||
</div>
|
||||
<hr />
|
||||
<hr />
|
||||
<div>
|
||||
<span>缩放X</span>
|
||||
<input type="number" v-model="item.object.scaleX" step="0.1" />
|
||||
</div>
|
||||
<div>
|
||||
<span>缩放Y</span>
|
||||
<input type="number" v-model="item.object.scaleY" step="0.1" />
|
||||
</div>
|
||||
<div>
|
||||
<span>X</span>
|
||||
<input type="number" v-model="item.object.left" />
|
||||
</div>
|
||||
<div>
|
||||
<span>Y</span>
|
||||
<input type="number" v-model="item.object.top" />
|
||||
</div>
|
||||
<div>
|
||||
<span>角度</span>
|
||||
<input type="number" v-model="item.object.angle" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>透明度</span>
|
||||
<input
|
||||
type="range"
|
||||
v-model="item.object.opacity"
|
||||
step="0.1"
|
||||
min="0"
|
||||
max="1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="onAdd">添加</button>
|
||||
<div class="box">
|
||||
<pingpu
|
||||
:list="list"
|
||||
ref="pingpuRef"
|
||||
@change-canvas="updateCanvas"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<demo />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import pingpu from "./pingpu.vue";
|
||||
const ACTIONS = {
|
||||
ADD: "add",
|
||||
UPDATE: "update",
|
||||
DELETE: "delete",
|
||||
SELECT: "select",
|
||||
SORT: "sort",
|
||||
};
|
||||
|
||||
const convertDotNotationToBracket = (str) =>
|
||||
str.replace(/(?:^|\.)(\d+)(?=\.|$)/g, "[#$1]").replace(/\[#/g, "[");
|
||||
const activeId = ref("1");
|
||||
const list = ref([
|
||||
{
|
||||
id: "1",
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [0, 0],
|
||||
scale: [1, 1],
|
||||
angle: 0,
|
||||
name: "Print1",
|
||||
priority: 1,
|
||||
object: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
opacity: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
gapX: 10,
|
||||
gapY: 20,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [0, 0],
|
||||
scale: [2, 2],
|
||||
angle: -45,
|
||||
name: "Print2",
|
||||
priority: 1,
|
||||
object: {
|
||||
top: 150,
|
||||
left: 250,
|
||||
scaleX: 0.5,
|
||||
scaleY: 0.5,
|
||||
opacity: 1,
|
||||
angle: 45,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
gapX: 0,
|
||||
gapY: 0,
|
||||
},
|
||||
},
|
||||
]);
|
||||
// 深拷贝
|
||||
const deepCopy = (obj) => JSON.parse(JSON.stringify(obj));
|
||||
const oldList = ref(deepCopy(list.value));
|
||||
const pingpuRef = ref(null);
|
||||
const updateCanvas = (arr) => {
|
||||
oldList.value = deepCopy(list.value);
|
||||
arr.forEach((item) => {
|
||||
list.value.forEach((v) => {
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
if (v.id === item.id) {
|
||||
if (item.action === ACTIONS.UPDATE) {
|
||||
try {
|
||||
const key = item.key;
|
||||
const str = /^\[/.test(item.key)
|
||||
? "v" + key
|
||||
: "v." + key;
|
||||
eval(`${str} = item.value`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (item.action === ACTIONS.SELECT) {
|
||||
activeId.value = item.id;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onSelect = (id) => {
|
||||
activeId.value = id;
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
id: id,
|
||||
action: ACTIONS.SELECT,
|
||||
},
|
||||
]);
|
||||
};
|
||||
const onMove = (id, id2) => {
|
||||
const obj1 = list.value.find((v) => v.id === id);
|
||||
const obj2 = list.value.find((v) => v.id === id2);
|
||||
const index1 = list.value.indexOf(obj1);
|
||||
const index2 = list.value.indexOf(obj2);
|
||||
if (index1 < index2) {
|
||||
list.value.splice(index2, 0, list.value.splice(index1, 1)[0]);
|
||||
} else {
|
||||
list.value.splice(index1, 0, list.value.splice(index2, 1)[0]);
|
||||
}
|
||||
const ids = list.value.map((v) => v.id);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
action: ACTIONS.SORT,
|
||||
ids,
|
||||
},
|
||||
]);
|
||||
};
|
||||
const onDelete = (id) => {
|
||||
list.value = list.value.filter((v) => v.id !== id);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
id: id,
|
||||
action: ACTIONS.DELETE,
|
||||
},
|
||||
]);
|
||||
};
|
||||
const onAdd = () => {
|
||||
const obj = {
|
||||
id: Date.now().toString(),
|
||||
ifSingle: false,
|
||||
level2Type: "Pattern",
|
||||
designType: "Library",
|
||||
path: "/src/assets/images/canvas/yinhua1.jpg",
|
||||
location: [0, 0],
|
||||
scale: [1, 1],
|
||||
angle: 0,
|
||||
name: "Print" + (list.value.length + 1),
|
||||
priority: 1,
|
||||
object: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
opacity: 1,
|
||||
angle: 0,
|
||||
flipX: false,
|
||||
flipY: false,
|
||||
blendMode: "multiply",
|
||||
gapX: 10,
|
||||
gapY: 20,
|
||||
},
|
||||
};
|
||||
list.value.push(obj);
|
||||
pingpuRef.value.updataList([
|
||||
{
|
||||
action: ACTIONS.ADD,
|
||||
data: obj,
|
||||
},
|
||||
]);
|
||||
};
|
||||
watch(
|
||||
() => list.value,
|
||||
() => updateList(),
|
||||
{ deep: true }
|
||||
);
|
||||
// 监听列表变化属性变更
|
||||
const updateList = () => {
|
||||
const changeList = [];
|
||||
oldList.value.forEach((oldItem) => {
|
||||
const newItem = list.value.find((v) => v.id === oldItem.id);
|
||||
if (newItem) {
|
||||
const arr = findDiffProps(oldItem, newItem);
|
||||
arr.forEach((item) => {
|
||||
changeList.push({
|
||||
...item,
|
||||
id: oldItem.id,
|
||||
action: ACTIONS.UPDATE,
|
||||
key: convertDotNotationToBracket(item.key),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
changeList.push({
|
||||
id: oldItem.id,
|
||||
action: ACTIONS.DELETE,
|
||||
});
|
||||
}
|
||||
});
|
||||
oldList.value = deepCopy(list.value);
|
||||
if (changeList.length) {
|
||||
pingpuRef.value.updataList(changeList);
|
||||
}
|
||||
};
|
||||
// 递归查找不同的属性
|
||||
const findDiffProps = (obj1, obj2, diffProps = [], path = "") => {
|
||||
for (const key in obj1) {
|
||||
if (typeof obj1[key] === "object") {
|
||||
findDiffProps(obj1[key], obj2[key], diffProps, `${path}${key}.`);
|
||||
} else if (obj1[key] !== obj2[key]) {
|
||||
diffProps.push({
|
||||
key: `${path}${key}`,
|
||||
value: obj2[key],
|
||||
});
|
||||
}
|
||||
}
|
||||
return diffProps;
|
||||
};
|
||||
import demo from "./OverallCanvas/demo.vue";
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.test {
|
||||
> .control {
|
||||
display: inline-block;
|
||||
border: 1px solid #000;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
&.active {
|
||||
border-color: rgb(17, 68, 223);
|
||||
box-shadow: 0 0 5px rgb(17, 68, 223);
|
||||
}
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
> * {
|
||||
margin-right: 10px;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
> span {
|
||||
width: 80px;
|
||||
}
|
||||
> input:not([type="range"]) {
|
||||
padding-left: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .box {
|
||||
width: 400px;
|
||||
height: 500px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user