onMounted(() => {
// 创建图片对象
const imgElement = new Image();
// imgElement.src = testImage; // 图片路径,注意使用公共路径
// imgElement.src = '@/assets/images/test.png'
// imgElement.src = './../../assets/images/test.png'
// imgElement.src = './vite.svg'
// imgElement.src = './test.png'
imgElement.src = './1.jpg'
imgElement.onload = function() {
// 获取图片宽高
const imgWidth = imgElement.width;
const imgHeight = imgElement.height;
// 初始化 Fabric 画布,尺寸与图片一致
const canvas = new fabric.Canvas(canvasRef.value, {
width: imgWidth,
height: imgHeight
});
// 创建背景图片
const bgImage = new fabric.Image(imgElement, {
scaleX: 1,
scaleY: 1,
selectable: false, // 设置背景图片不可选
});
// 将图片设置为背景
canvas.setBackgroundImage(bgImage, canvas.renderAll.bind(canvas));
// 定义变量
let isDrawing = false; // 是否正在绘制多边形
let points = []; // 存储多边形的点
let pointObjects = []; // 存储点的可视化对象
let lineObjects = []; // 存储永久线段对象
let finalPolygon = null; // 最终多边形
let firstPoint = null; // 第一个点(用于闭合多边形)
// 动态元素 - 随鼠标移动更新
let activeLine = null; // 当前鼠标位置的活动线
let activeShape = null; // 当前动态多边形
let blueColor = 'blue'
let redColor = 'red'
// let otherColor = 'rgb(201, 201, 201)'
let otherColor = blueColor
let linkLineColor = 'rgb(51, 164, 255)'
let fillColor = 'rgb(232, 241, 249)'
let endColor = 'rgb(227, 242, 202)'
// let endColor = blueColor
// let endStrokeColor = 'rgb(169, 224, 36)'
let endStrokeColor = blueColor
// 创建点的函数(用于可视化)
const createPoint = (x, y, isFirst = false) => {
const circle = new fabric.Circle({
radius: 5,
fill: 'white',
stroke: isFirst ? redColor : otherColor,
strokeWidth: 1,
selectable: false,
originX: 'center',
originY: 'center',
left: x,
top: y
});
canvas.add(circle);
return circle;
};
// 创建连接线的函数
const createLine = (fromX, fromY, toX, toY) => {
const line = new fabric.Line([fromX, fromY, toX, toY], {
stroke: linkLineColor,
strokeWidth: 2,
selectable: false
});
canvas.add(line);
return line;
};
// 检查点是否接近第一个点
const isNearFirstPoint = (x, y) => {
if (!firstPoint) return false;
const distance = Math.sqrt(
Math.pow(x - firstPoint.x, 2) +
Math.pow(y - firstPoint.y, 2)
);
return distance < 20; // 20像素范围内视为接近
};
// 生成或更新动态多边形
const generatePolygon = (mousePointer) => {
// 确保有足够的点
if (!isDrawing || points.length < 1) return;
// 清除旧的活动线
if (activeLine) {
canvas.remove(activeLine);
activeLine = null;
}
// 清除旧的活动形状
if (activeShape) {
canvas.remove(activeShape);
activeShape = null;
}
// 创建动态线段 - 从最后一个点到当前鼠标位置
const lastPoint = points[points.length - 1];
activeLine = new fabric.Line(
[lastPoint.x, lastPoint.y, mousePointer.x, mousePointer.y],
{
stroke: linkLineColor,
strokeWidth: 2,
selectable: false
}
);
canvas.add(activeLine);
// 如果有至少2个点,创建动态多边形
if (points.length >= 2) {
// 创建包含所有点和当前鼠标位置的点数组
let polygonPoints = [...points];
// 如果鼠标接近第一个点,使用第一个点作为闭合点
if (isNearFirstPoint(mousePointer.x, mousePointer.y)) {
polygonPoints.push(firstPoint);
} else {
polygonPoints.push({ x: mousePointer.x, y: mousePointer.y });
}
// 创建动态多边形
activeShape = new fabric.Polygon(
polygonPoints.map(p => ({ x: p.x, y: p.y })),
{
fill: fillColor,
stroke: fillColor,
strokeWidth: 1,
selectable: false,
globalCompositeOperation: 'multiply' // 使用混合模式而不是透明度
}
);
// 添加到画布
canvas.add(activeShape);
// 确保背景图在最下层
activeShape.sendToBack();
bgImage.sendToBack();
}
canvas.renderAll();
};
// 完成多边形绘制
const completePolygon = () => {
if (points.length < 3) {
showMessage('至少需要3个点才能形成多边形', 'warning');
return;
}
// 清除动态元素
if (activeLine) {
canvas.remove(activeLine);
activeLine = null;
}
// 清除动态多边形
if (activeShape) {
canvas.remove(activeShape);
activeShape = null;
}
// 移除所有线段
lineObjects.forEach(line => {
if (line) canvas.remove(line);
});
// 创建最终的多边形
finalPolygon = new fabric.Polygon(points.map(p => ({ x: p.x, y: p.y })), {
fill: endColor,
stroke: endStrokeColor, // 线段颜色为rgb(169, 224, 36)
strokeWidth: 2,
selectable: true,
globalCompositeOperation: 'multiply' // 使用混合模式而不是透明度
});
// 移除所有临时点
pointObjects.forEach(point => {
if (point) canvas.remove(point);
});
// 添加多边形到画布
canvas.add(finalPolygon);
canvas.renderAll();
// 重置状态
points = [];
pointObjects = [];
lineObjects = [];
isDrawing = false;
firstPoint = null;
showMessage('多边形绘制完成', 'success');
};
// 监听鼠标按下事件
canvas.on('mouse:down', function(o) {
const pointer = canvas.getPointer(o.e);
// 检查是否点击在现有对象上
// 注意:这里可能导致问题,因为activeLine和activeShape也是对象
// 只有明确是polygon类型的最终多边形才应该阻止操作
if (o.target && o.target.type === 'polygon' && !isDrawing) {
// 如果点击了现有多边形且不在绘制模式,只进行选择
return;
}
// 如果是第一次点击,开始绘制
if (!isDrawing) {
// 初始化绘制状态
isDrawing = true;
points = [];
pointObjects = [];
lineObjects = [];
// 记录第一个点
firstPoint = { x: pointer.x, y: pointer.y };
points.push(firstPoint);
// 创建第一个点的可视标记(红色表示起点)
const firstPointObject = createPoint(pointer.x, pointer.y, true);
pointObjects.push(firstPointObject);
showMessage(`开始绘制${sidesCount.value}边形,请继续点击确定顶点位置`, 'info');
} else {
console.log('当前点数:', points.length, '最大点数:', sidesCount.value);
// 检查是否点击了第一个点(闭合多边形)
if (isNearFirstPoint(pointer.x, pointer.y)) {
// 如果已经有至少3个点且点击接近第一个点,闭合多边形
if (points.length >= 5) {
completePolygon();
} else {
showMessage(`需要${sidesCount.value}个点才能闭合多边形。`, 'warning');
}
} else {
// 只有在未达到最大点数时才添加新点
if (points.length < sidesCount.value) {
// 继续添加点
const newPoint = { x: pointer.x, y: pointer.y };
points.push(newPoint);
// 创建点的可视标记
const pointObject = createPoint(pointer.x, pointer.y);
pointObjects.push(pointObject);
// 添加永久连接线
if (points.length >= 2) {
const lastIndex = points.length - 1;
const line = createLine(
points[lastIndex - 1].x, points[lastIndex - 1].y,
points[lastIndex].x, points[lastIndex].y
);
lineObjects.push(line);
}
// if (points.length < sidesCount.value) {
// showMessage(`已添加${points.length}个点,继续点击添加更多点`, 'info');
// }
// 如果刚好达到最大点数,提示用户
if (points.length === sidesCount.value) {
showMessage(`已达到最大点数${sidesCount.value},请点击第一个点完成绘制`, 'warning');
}
} else {
// 已达到最大点数,点击无效,只能点击起点完成绘制
showMessage(`已达到最大点数${sidesCount.value},请点击第一个点完成绘制`, 'warning');
}
// 强制更新画布
canvas.renderAll();
}
}
});
// 监听鼠标移动事件
canvas.on('mouse:move', function(o) {
if (!isDrawing) return;
const pointer = canvas.getPointer(o.e);
// 生成动态多边形
generatePolygon(pointer);
});
// 取消绘制的快捷键(Esc键)
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && isDrawing) {
// 清除所有元素
if (activeLine) {
canvas.remove(activeLine);
}
if (activeShape) {
canvas.remove(activeShape);
}
pointObjects.forEach(point => {
if (point) canvas.remove(point);
});
lineObjects.forEach(line => {
if (line) canvas.remove(line);
});
// 重置状态
points = [];
pointObjects = [];
lineObjects = [];
activeLine = null;
activeShape = null;
isDrawing = false;
firstPoint = null;
canvas.renderAll();
showMessage('已取消绘制', 'info');
}
});
};
})
</script>
全部评论