You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

396 lines
11 KiB
Vue

<script setup lang="ts">
import useSelect from "@/hooks/select";
import { fetchArrayByAttrObject, setAttrObjectByArray } from "@/utils/utils";
// 属性选项
import { textTypeConf } from "@/config/attribute/baseType";
import video_type_1 from "@/assets/modelSetting/video_type_1.png";
import video_type_2 from "@/assets/modelSetting/video_type_2.png";
import video_type_3 from "@/assets/modelSetting/video_type_3.png";
const event = inject("event");
const update = getCurrentInstance();
const { mixinState, canvasEditor } = useSelect();
/**业务属性 */
const formData = ref({
iconType: 1
});
const rules = {
iconType: [{ required: true, message: "请选择图标类型", trigger: "change" }]
};
const iconOptions = ref([
{
value: 1,
label: "图标1",
type: "1",
url: video_type_1
},
{
value: 2,
label: "图标2",
type: "2",
url: video_type_2
},
{
value: 3,
label: "图标3",
type: "3",
url: video_type_3
}
]);
const formRef = ref(null);
const submitForm = () => {
formRef.value?.validate((valid: boolean) => {
if (valid) {
// 模拟提交接口
// axios
// .post("/api/submit-icon", form.value)
// .then(() => {
// ElMessage.success("提交成功");
// })
// .catch(() => {
// ElMessage.error("提交失败");
// });
} else {
// ElMessage.error("表单验证失败");
}
});
};
/**拓展属性 */
// 通用属性
const baseAttr = reactive({
id: "",
opacity: 0,
angle: 0,
fill: "#fff",
left: 0,
top: 0,
strokeWidth: 0,
strokeDashArray: [],
stroke: "#fff",
shadow: {
color: "#fff",
blur: 0,
offsetX: 0,
offsetY: 0
},
points: {},
selectable: false,
userProperty: [
{
key: "key",
value: "value"
}
]
});
// 动效属性
const animationAttr = reactive({
type: "None"
});
// 字体属性
const fontAttr = reactive({
fontSize: 0,
fontFamily: "",
lineHeight: 0,
charSpacing: 0,
fontWeight: "",
textBackgroundColor: "#fff",
textAlign: "",
fontStyle: "",
underline: false,
linethrough: false,
overline: false
});
// 获取画布边界
const getCanvasBoundaries = () => {
const canvasObj = canvasEditor.canvas.getObjects()[1];
return {
width: canvasObj.width,
height: canvasObj.height
};
};
const constrainObjectWithinCanvas = obj => {
const { width, height } = getCanvasBoundaries();
// 获取对象的当前尺寸和位置
const objLeft = obj.left;
const objTop = obj.top;
const objWidth = obj.width * obj.scaleX;
const objHeight = obj.height * obj.scaleY;
// 限制对象在画布内移动
if (objLeft < 0) {
obj.left = 0;
}
if (objTop < 0) {
obj.top = 0;
}
if (objLeft + objWidth > width) {
obj.left = width - objWidth;
}
if (objTop + objHeight > height) {
obj.top = height - objHeight;
}
// 更新对象的位置
obj.setCoords();
};
const getObjectAttr = e => {
const activeObject = canvasEditor.canvas.getActiveObject();
// 不是当前obj跳过
if (e && e.target && e.target !== activeObject) return;
if (activeObject) {
// base
baseAttr.id = activeObject.get("id");
baseAttr.userProperty = fetchArrayByAttrObject(
activeObject.get("userProperty")
);
baseAttr.opacity = activeObject.get("opacity") * 100;
baseAttr.fill = activeObject.get("fill");
baseAttr.left = activeObject.get("left");
baseAttr.top = activeObject.get("top");
baseAttr.stroke = activeObject.get("stroke");
baseAttr.strokeWidth = activeObject.get("strokeWidth");
baseAttr.shadow = activeObject.get("shadow") || {};
baseAttr.angle = activeObject.get("angle") || 0;
baseAttr.points = activeObject.get("points") || {};
baseAttr.selectable = activeObject.get("selectable");
console.log("activeObject", activeObject);
const textTypes = ["i-text", "text", "textbox"];
if (textTypes.includes(activeObject.type)) {
fontAttr.fontSize = activeObject.get("fontSize");
fontAttr.fontFamily = activeObject.get("fontFamily");
fontAttr.lineHeight = activeObject.get("lineHeight");
fontAttr.textAlign = activeObject.get("textAlign");
fontAttr.underline = activeObject.get("underline");
fontAttr.linethrough = activeObject.get("linethrough");
fontAttr.charSpacing = activeObject.get("charSpacing");
fontAttr.overline = activeObject.get("overline");
fontAttr.fontStyle = activeObject.get("fontStyle");
fontAttr.textBackgroundColor = activeObject.get("textBackgroundColor");
fontAttr.fontWeight = activeObject.get("fontWeight");
}
// update 动画属性
if (activeObject?.animation && activeObject?.animation?.type != "None") {
const animateObject = activeObject.get("animation");
animationAttr.type = animateObject.type;
}
}
};
const selectCancel = () => {
baseAttr.fill = "";
update?.proxy?.$forceUpdate();
};
const init = () => {
// 获取字体数据
event.on("selectCancel", selectCancel);
event.on("selectOne", getObjectAttr);
canvasEditor.canvas.on("object:modified", getObjectAttr);
// 监听 object:moving 事件以限制移动范围
canvasEditor.canvas.on("object:moving", e => {
const activeObject = e.target;
if (activeObject) {
constrainObjectWithinCanvas(activeObject);
canvasEditor.canvas.renderAll();
}
});
};
// 通用属性改变
const changeCommon = (key, value) => {
const activeObject = canvasEditor.canvas.getActiveObjects()[0];
// 透明度特殊转换
if (key === "opacity") {
activeObject && activeObject.set(key, value / 100);
canvasEditor.canvas.renderAll();
return;
}
// 旋转角度适配
if (key === "angle") {
activeObject.rotate(value);
canvasEditor.canvas.renderAll();
return;
}
// 用户属性
if (key === "userProperty_key") {
activeObject &&
activeObject.set(
"userProperty",
setAttrObjectByArray(baseAttr.userProperty)
);
canvasEditor.canvas.renderAll();
return;
}
if (key === "userProperty_value") {
activeObject &&
activeObject.set(
"userProperty",
setAttrObjectByArray(baseAttr.userProperty)
);
canvasEditor.canvas.renderAll();
return;
}
if (key === "iconType") {
// 在 group 对象中查找唯一的 fabric.Image 对象
const imageObject = activeObject._objects[1];
if (imageObject) {
const targetIcon = toRaw(iconOptions.value).find(
item => item.value === value
);
console.log(targetIcon, "activeObject_iconType", activeObject);
if (targetIcon) {
// 修改图片路径
imageObject.setSrc(
targetIcon.url,
() => {
// 刷新画布
console.log("Image loaded successfully activeObject_iconType");
activeObject.addWithUpdate(); // 更新对象的坐标和边界框
canvasEditor.canvas.renderAll();
},
{ crossOrigin: "anonymous" }
);
// 加载新的图片并替换旧的图片对象
// fabric.Image.fromURL(
// targetIcon.url,
// img => {
// // 使用 oldImage.get() 获取所有属性,并设置到新图片上
// // img.set(imageObject.get());
// // 在 group 中替换旧的图片对象
// console.log(img, "old_img", imageObject);
// const index = activeObject._objects.indexOf(imageObject);
// if (index !== -1) {
// activeObject._objects[index] = {
// ...imageObject.get(),
// src: img.src
// }; // 替换为新的图片对象
// activeObject.addWithUpdate(); // 更新 group 的边界和变换
// canvasEditor.canvas.renderAll(); // 刷新画布
// } else {
// console.error("Image object not found in the group.");
// }
// },
// {
// crossOrigin: "anonymous" // 如果图片跨域需要设置
// }
// );
}
return;
}
return;
}
activeObject && activeObject.set(key, value);
canvasEditor.canvas.renderAll();
// 更新属性
getObjectAttr();
};
onMounted(init);
onBeforeUnmount(() => {
event.off("selectCancel", selectCancel);
event.off("selectOne", getObjectAttr);
canvasEditor.canvas.off("object:modified", getObjectAttr);
canvasEditor.canvas.off("object:moving");
});
</script>
<template>
<div class="box" v-if="mixinState.mSelectMode === 'one'">
<!-- 字体属性 -->
<div v-show="textTypeConf.includes(mixinState.mSelectOneType)">
<!-- 字体属性 -->
</div>
<!-- ID属性 -->
<!-- <div>
<div class="flex-view">
<div class="flex-item">
<span class="label">{{ $t("attributes.id") }}</span>
<div class="content slider-box">
<input
v-model="baseAttr.id"
@change="changeCommon('id', baseAttr.id)"
/>
</div>
</div>
</div>
</div> -->
<el-form
ref="formRef"
:rules="rules"
:inline="true"
:model="formData"
class="demo-form-inline"
label-position="top"
>
<el-form-item label="选择图标" class="w-full" prop="iconType">
<el-radio-group
v-model="formData.iconType"
@change="changeCommon('iconType', formData.iconType)"
>
<el-radio
v-for="option in iconOptions"
:key="option.value"
:label="option.value"
>
<i>
<!-- 图标 -->
</i>
{{ option.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- 动态属性 -->
<!-- <div>
<ul>
<li class="flex-view" v-for="(v, k) in baseAttr.userProperty" :key="k">
<div class="flex-item">
<span class="content slider-box">
<input
v-model="baseAttr.userProperty[k].key"
@change="
changeCommon('userProperty_key', baseAttr.userProperty)
"
/>
</span>
<div class="content slider-box">
<input
v-model="baseAttr.userProperty[k].value"
@change="
changeCommon('userProperty_value', baseAttr.userProperty)
"
/>
</div>
</div>
</li>
<li
class="flex-view"
style="justify-content: center; color: dodgerblue; text-align: center"
@click="
() => {
baseAttr.userProperty.push({
key: 'key' + baseAttr.userProperty.length,
value: 'value'
});
}
"
>
<span>新增一项</span>
</li>
</ul>
</div> -->
</div>
</template>