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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>