欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Vue3和Fabric.js實現(xiàn)交互式多邊形繪制功能

 更新時間:2025年05月18日 08:29:05   作者:jnsytgsyqjgm  
最近學(xué)習(xí)Fabric.js,正好想著利用這個機會去實現(xiàn)一個多邊形的繪制工具,該工具允許用戶在圖片上通過點擊創(chuàng)建多邊形區(qū)域,支持自定義邊數(shù),并提供友好的用戶交互體驗,這種工具通常用于圖像標(biāo)注、區(qū)域選擇和交互式地圖應(yīng)用等場景,需要的朋友可以參考下

需求概述

1.基礎(chǔ)畫布設(shè)置

  • 加載背景圖片

  • 創(chuàng)建與圖片尺寸匹配的Fabric.js畫布

2.多邊形繪制功能

  • 支持固定邊數(shù)多邊形繪制(默認5邊形)

  • 通過點擊添加頂點

  • 限制最大頂點數(shù),達到最大數(shù)后只能閉合

3.視覺反饋系統(tǒng)

  • 繪制頂點可視化(白色圓點,首個點為紅邊框)

  • 點與點之間的連接線(藍色)

  • 實時多邊形預(yù)覽(半透明填充)

4.智能交互

  • 起點閉合檢測(鼠標(biāo)靠近起點時自動吸附)

  • 距離計算(20像素范圍內(nèi)檢測接近)

  • 繪制完成時的狀態(tài)轉(zhuǎn)換

技術(shù)實現(xiàn)詳解

1. 基礎(chǔ)架構(gòu)

項目使用Vue 3的<script setup>

import { ref, onMounted, reactive } from 'vue'
import { fabric } from 'fabric'
import { ElMessage } from 'element-plus'    

2. 畫布初始化

在組件掛載時,通過以下步驟初始化畫布:

  • 加載背景圖片
  • 創(chuàng)建Fabric畫布,尺寸與圖片一致
  • 將圖片設(shè)置為背景
onMounted(() => {
  const imgElement = new Image();
  imgElement.src = '/test.jpg'; // 圖片路徑,注意使用公共路徑 src/public/test.jpg
  
  imgElement.onload = function() {
    const imgWidth = imgElement.width;
    const imgHeight = imgElement.height;
    
    const canvas = new fabric.Canvas(canvasRef.value, {
      width: imgWidth,
      height: imgHeight
    });
    
    // 設(shè)置背景圖片
    const bgImage = new fabric.Image(imgElement, {
      scaleX: 1,
      scaleY: 1,
      selectable: false,
    });
    
    canvas.setBackgroundImage(bgImage, canvas.renderAll.bind(canvas));
    
    // 后續(xù)代碼...
  };
});

3. 多邊形繪制核心邏輯

多邊形繪制實現(xiàn)了以下核心功能:

3.1 狀態(tài)管理

let isDrawing = false; // 繪制狀態(tài)
let points = []; // 多邊形頂點
let pointObjects = []; // 頂點可視化對象
let lineObjects = []; // 線段對象
let finalPolygon = null; // 最終多邊形
let activeLine = null; // 當(dāng)前活動線
let activeShape = null; // 動態(tài)多邊形

3.2 頂點可視化

使用Fabric.js的Circle對象實現(xiàn)頂點可視化,保證頂點精確定位:

const createPoint = (x, y, isFirst = false) => {
  const circle = new fabric.Circle({
    radius: 5,
    fill: 'white', // 白色背景
    stroke: isFirst ? 'red' : 'rgb(201, 201, 201)', // 第一個點紅色邊框,其余灰色
    strokeWidth: 1,
    selectable: false,
    originX: 'center',
    originY: 'center',
    left: x,
    top: y
  });
  
  canvas.add(circle);
  return circle;
};

注意這里的實現(xiàn)細節(jié):

  • 使用originX/originY為'center'確保圓心精確定位在點擊位置
  • left/top直接使用點擊坐標(biāo),不再額外減去半徑值

3.3 線段繪制

連接頂點的線段使用Fabric.js的Line對象實現(xiàn):

const createLine = (fromX, fromY, toX, toY) => {
  const line = new fabric.Line([fromX, fromY, toX, toY], {
    stroke: 'rgb(51, 164, 255)',
    strokeWidth: 2,
    selectable: false
  });
  
  canvas.add(line);
  return line;
};

3.4 動態(tài)多邊形生成

generatePolygon函數(shù)是實現(xiàn)實時預(yù)覽效果的核心,該函數(shù)會在鼠標(biāo)移動時被調(diào)用,創(chuàng)建從最后一個固定點到當(dāng)前鼠標(biāo)位置的動態(tài)線段和實時更新的多邊形:

const generatePolygon = (mousePointer) => {
  // 確保處于繪制狀態(tài)且至少有一個點
  if (!isDrawing || points.length < 1) return;
  
  // 清除舊的活動線
  if (activeLine) {
    canvas.remove(activeLine);
    activeLine = null;
  }
  
  // 清除舊的活動形狀
  if (activeShape) {
    canvas.remove(activeShape);
    activeShape = null;
  }
  
  // 創(chuàng)建動態(tài)線段 - 從最后一個點到當(dāng)前鼠標(biāo)位置
  const lastPoint = points[points.length - 1];
  activeLine = new fabric.Line(
    [lastPoint.x, lastPoint.y, mousePointer.x, mousePointer.y], 
    {
      stroke: 'rgb(51, 164, 255)',
      strokeWidth: 2,
      selectable: false
    }
  );
  canvas.add(activeLine);
  
  // 如果有至少2個點,創(chuàng)建動態(tài)多邊形
  if (points.length >= 2) {
    // 創(chuàng)建包含所有點和當(dāng)前鼠標(biāo)位置的點數(shù)組
    let polygonPoints = [...points];
    
    // 如果鼠標(biāo)接近第一個點,使用第一個點作為閉合點
    if (isNearFirstPoint(mousePointer.x, mousePointer.y)) {
      polygonPoints.push(firstPoint);
    } else {
      polygonPoints.push({ x: mousePointer.x, y: mousePointer.y });
    }
    
    // 創(chuàng)建動態(tài)多邊形
    activeShape = new fabric.Polygon(
      polygonPoints.map(p => ({ x: p.x, y: p.y })),
      {
        fill: 'rgb(232, 241, 249)',
        stroke: 'rgb(232, 241, 249)',
        strokeWidth: 1,
        selectable: false,
        globalCompositeOperation: 'multiply' // 使用混合模式而不是透明度
      }
    );
    
    // 添加到畫布
    canvas.add(activeShape);
    // 確保背景圖在最下層
    activeShape.sendToBack();
    bgImage.sendToBack();
  }
  
  canvas.renderAll();
};

動態(tài)多邊形生成技術(shù)細節(jié)分析:

1. 清除歷史狀態(tài): 每次鼠標(biāo)移動時,先清除之前的活動線和活動多邊形,避免重復(fù)渲染

2. 動態(tài)線段創(chuàng)建:

  • 使用Fabric.js的Line對象創(chuàng)建從最后固定點到鼠標(biāo)當(dāng)前位置的線段
  • 設(shè)置線段顏色為藍色(rgb(51, 164, 255)),提供明顯的視覺反饋

3. 多邊形預(yù)覽生成:

  • 當(dāng)已有至少2個點時,創(chuàng)建包含所有固定點和當(dāng)前鼠標(biāo)位置的臨時點數(shù)組
  • 通過isNearFirstPoint函數(shù)檢測鼠標(biāo)是否靠近起點,實現(xiàn)"吸附"效果
  • 如果鼠標(biāo)接近起點,使用起點坐標(biāo)替代鼠標(biāo)坐標(biāo),視覺上表示可以閉合

4. 多邊形樣式設(shè)置:

  • 使用淺藍色填充(rgb(232, 241, 249))和相同顏色的邊框
  • 使用globalCompositeOperation: 'multiply'實現(xiàn)半透明效果,使背景圖仍然可見
  • 多邊形不可選擇,確保不會干擾繪制流程

5. 層級管理:

  • 使用activeShape.sendToBack()確保多邊形在下層
  • 使用bgImage.sendToBack()確保背景圖始終在最底層
  • 這種層級管理確保了可視化元素的正確疊加順序

4. 事件處理

4.1 鼠標(biāo)點擊事件

鼠標(biāo)點擊事件處理是多邊形繪制的核心邏輯,處理啟動繪制、添加頂點和完成多邊形等關(guān)鍵操作:

canvas.on('mouse:down', function(o) {
  const pointer = canvas.getPointer(o.e);
  
  // 檢查是否點擊在現(xiàn)有對象上
  if (o.target && o.target.type === 'polygon' && !isDrawing) {
    // 如果點擊了現(xiàn)有多邊形且不在繪制模式,只進行選擇
    return;
  }
  
  // 如果是第一次點擊,開始繪制
  if (!isDrawing) {
    // 初始化繪制狀態(tài)
    isDrawing = true;
    points = [];
    pointObjects = [];
    lineObjects = [];
    
    // 記錄第一個點
    firstPoint = { x: pointer.x, y: pointer.y };
    points.push(firstPoint);
    
    // 創(chuàng)建第一個點的可視標(biāo)記(紅色表示起點)
    const firstPointObject = createPoint(pointer.x, pointer.y, true);
    pointObjects.push(firstPointObject);
    
    showMessage(`開始繪制${sidesCount.value}邊形,請繼續(xù)點擊確定頂點位置`, 'info');
  } else {
    console.log('當(dāng)前點數(shù):', points.length, '最大點數(shù):', sidesCount.value);
    
    // 檢查是否點擊了第一個點(閉合多邊形)
    if (isNearFirstPoint(pointer.x, pointer.y)) {
      // 如果已經(jīng)有至少3個點且點擊接近第一個點,閉合多邊形
      if (points.length >= 5) {
        completePolygon();
      } else {
        showMessage(`需要${sidesCount.value}個點才能閉合多邊形。`, 'warning');
      }
    } else {
      // 只有在未達到最大點數(shù)時才添加新點
      if (points.length < sidesCount.value) {
        // 繼續(xù)添加點
        const newPoint = { x: pointer.x, y: pointer.y };
        points.push(newPoint);
        
        // 創(chuàng)建點的可視標(biāo)記
        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);
        }
        
        // 如果剛好達到最大點數(shù),提示用戶
        if (points.length === sidesCount.value) {
          showMessage(`已達到最大點數(shù)${sidesCount.value},請點擊第一個點完成繪制`, 'warning');
        }
      } else {
        // 已達到最大點數(shù),點擊無效,只能點擊起點完成繪制
        showMessage(`已達到最大點數(shù)${sidesCount.value},請點擊第一個點完成繪制`, 'warning');
      }
      
      // 強制更新畫布
      canvas.renderAll();
    }
  }
});

鼠標(biāo)點擊事件技術(shù)細節(jié)分析:

1. 事件上下文解析:

  • 使用canvas.getPointer(o.e)獲取鼠標(biāo)在畫布上的準(zhǔn)確坐標(biāo)
  • 通過o.target檢測點擊是否落在現(xiàn)有對象上,防止在已有多邊形上開始新繪制

2. 繪制狀態(tài)初始化:

  • 首次點擊時,重置所有狀態(tài)變量(isDrawing, points, pointObjects, lineObjects)
  • 創(chuàng)建第一個點并用紅色標(biāo)記,區(qū)別于后續(xù)點
  • 通過消息提示告知用戶已開始繪制

3. 多種點擊場景處理:

  • 區(qū)分三種主要場景:首次點擊、點擊第一個點(閉合)、添加新點
  • 使用狀態(tài)變量isDrawing區(qū)分是否處于繪制模式

4. 閉合多邊形邏輯:

  • 使用isNearFirstPoint函數(shù)檢測點擊是否接近起點
  • 當(dāng)點擊接近第一個點且已有足夠的點(>=5)時,通過completePolygon()函數(shù)完成多邊形
  • 如果點不足,顯示警告消息指導(dǎo)用戶

5. 頂點數(shù)量管理:

  • 通過sidesCount.value控制多邊形的最大頂點數(shù)
  • 實現(xiàn)嚴(yán)格的頂點數(shù)限制,達到最大數(shù)后只允許閉合操作
  • 根據(jù)不同階段提供相應(yīng)的用戶提示

6. 可視化反饋:

  • 每個點都有可視化標(biāo)記(白色圓形帶有邊框)
  • 點與點之間有顏色鮮明的連接線
  • 每次操作后立即更新畫布(canvas.renderAll())

4.2 鼠標(biāo)移動事件

實時更新動態(tài)線段和多邊形形狀:

canvas.on('mouse:move', function(o) {
  if (!isDrawing) return;
  
  const pointer = canvas.getPointer(o.e);
  generatePolygon(pointer);
});

4.3 鍵盤事件

支持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);
    });
    
    // 重置狀態(tài)
    points = [];
    pointObjects = [];
    lineObjects = [];
    activeLine = null;
    activeShape = null;
    isDrawing = false;
    firstPoint = null;
    
    canvas.renderAll();
    showMessage('已取消繪制', 'info');
  }
});

5. 完成多邊形繪制

當(dāng)用戶點擊接近起點時,多邊形繪制完成:

const completePolygon = () => {
  if (points.length < 3) {
    showMessage('至少需要3個點才能形成多邊形', 'warning');
    return;
  }
  
  // 清除動態(tài)元素
  if (activeLine) {
    canvas.remove(activeLine);
    activeLine = null;
  }
  
  // 清除動態(tài)多邊形
  if (activeShape) {
    canvas.remove(activeShape);
    activeShape = null;
  }
  
  // 移除所有線段
  lineObjects.forEach(line => {
    if (line) canvas.remove(line);
  });
  
  // 創(chuàng)建最終的多邊形
  finalPolygon = new fabric.Polygon(points.map(p => ({ x: p.x, y: p.y })), {
    fill: 'rgb(227, 242, 202)',
    stroke: 'rgb(169, 224, 36)', // 線段顏色為rgb(169, 224, 36)
    strokeWidth: 2,
    selectable: true,
    globalCompositeOperation: 'multiply' // 使用混合模式而不是透明度
  });
  
  // 移除所有臨時點
  pointObjects.forEach(point => {
    if (point) canvas.remove(point);
  });
  
  // 添加多邊形到畫布
  canvas.add(finalPolygon);
  canvas.renderAll();
  
  // 重置狀態(tài)
  points = [];
  pointObjects = [];
  lineObjects = [];
  isDrawing = false;
  firstPoint = null;
  
  showMessage('多邊形繪制完成', 'success');
};

6. 用戶體驗優(yōu)化

6.1 消息提示

使用Element Plus的消息組件提供友好的用戶反饋:

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像素范圍內(nèi)視為接近
};

6.2 接近檢測

實現(xiàn)智能的"吸附"功能,方便用戶閉合多邊形:

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像素范圍內(nèi)視為接近
};

7. 完整代碼

<template>
  <div class="canvas-container">
    <!-- 使用單獨的canvas元素,圖片將作為背景 -->
    <canvas ref="canvasRef"></canvas>
  </div>
</template>

<script setup>
import { ref, onMounted, reactive } from 'vue'
import { fabric } from 'fabric'
import { ElMessage } from 'element-plus' // 導(dǎo)入Element Plus的消息組件

// 獲取 canvas 元素引用
const canvasRef = ref(null)

// 多邊形邊數(shù)
const sidesCount = ref(5) // 默認為5邊形

// 顯示提示信息的函數(shù),使用Element Plus
const showMessage = (message, type = 'info') => {
  ElMessage({
    message,
    type,
    duration: 3000,
    showClose: true
  });
}

onMounted(() => {
  // 創(chuàng)建圖片對象
  const imgElement = new Image();
  imgElement.src = '/test.jpg'; // 圖片路徑,注意使用公共路徑
  
  imgElement.onload = function() {
    // 獲取圖片寬高
    const imgWidth = imgElement.width;
    const imgHeight = imgElement.height;
    
    // 初始化 Fabric 畫布,尺寸與圖片一致
    const canvas = new fabric.Canvas(canvasRef.value, {
      width: imgWidth,
      height: imgHeight
    });
    
    // 創(chuàng)建背景圖片
    const bgImage = new fabric.Image(imgElement, {
      scaleX: 1,
      scaleY: 1,
      selectable: false, // 設(shè)置背景圖片不可選
    });
    
    // 將圖片設(shè)置為背景
    canvas.setBackgroundImage(bgImage, canvas.renderAll.bind(canvas));
    
    // 定義變量
    let isDrawing = false; // 是否正在繪制多邊形
    let points = []; // 存儲多邊形的點
    let pointObjects = []; // 存儲點的可視化對象
    let lineObjects = []; // 存儲永久線段對象
    let finalPolygon = null; // 最終多邊形
    let firstPoint = null; // 第一個點(用于閉合多邊形)
    
    // 動態(tài)元素 - 隨鼠標(biāo)移動更新
    let activeLine = null; // 當(dāng)前鼠標(biāo)位置的活動線
    let activeShape = null; // 當(dāng)前動態(tài)多邊形
    
    // 創(chuàng)建點的函數(shù)(用于可視化)
    const createPoint = (x, y, isFirst = false) => {
      const circle = new fabric.Circle({
        radius: 5,
        fill: 'white',
        stroke: isFirst ? 'red' : 'rgb(201, 201, 201)',
        strokeWidth: 1,
        selectable: false,
        originX: 'center',
        originY: 'center',
        left: x,
        top: y
      });
      
      canvas.add(circle);
      return circle;
    };
    
    // 創(chuàng)建連接線的函數(shù)
    const createLine = (fromX, fromY, toX, toY) => {
      const line = new fabric.Line([fromX, fromY, toX, toY], {
        stroke: 'rgb(51, 164, 255)',
        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像素范圍內(nèi)視為接近
    };
    
    // 生成或更新動態(tài)多邊形
    const generatePolygon = (mousePointer) => {
      // 確保有足夠的點
      if (!isDrawing || points.length < 1) return;
      
      // 清除舊的活動線
      if (activeLine) {
        canvas.remove(activeLine);
        activeLine = null;
      }
      
      // 清除舊的活動形狀
      if (activeShape) {
        canvas.remove(activeShape);
        activeShape = null;
      }
      
      // 創(chuàng)建動態(tài)線段 - 從最后一個點到當(dāng)前鼠標(biāo)位置
      const lastPoint = points[points.length - 1];
      activeLine = new fabric.Line(
        [lastPoint.x, lastPoint.y, mousePointer.x, mousePointer.y], 
        {
          stroke: 'rgb(51, 164, 255)',
          strokeWidth: 2,
          selectable: false
        }
      );
      canvas.add(activeLine);
      
      // 如果有至少2個點,創(chuàng)建動態(tài)多邊形
      if (points.length >= 2) {
        // 創(chuàng)建包含所有點和當(dāng)前鼠標(biāo)位置的點數(shù)組
        let polygonPoints = [...points];
        
        // 如果鼠標(biāo)接近第一個點,使用第一個點作為閉合點
        if (isNearFirstPoint(mousePointer.x, mousePointer.y)) {
          polygonPoints.push(firstPoint);
        } else {
          polygonPoints.push({ x: mousePointer.x, y: mousePointer.y });
        }
        
        // 創(chuàng)建動態(tài)多邊形
        activeShape = new fabric.Polygon(
          polygonPoints.map(p => ({ x: p.x, y: p.y })),
          {
            fill: 'rgb(232, 241, 249)',
            stroke: 'rgb(232, 241, 249)',
            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;
      }
      
      // 清除動態(tài)元素
      if (activeLine) {
        canvas.remove(activeLine);
        activeLine = null;
      }
      // 清除動態(tài)多邊形
      if (activeShape) {
        canvas.remove(activeShape);
        activeShape = null;
      }
      
      // 移除所有線段
      lineObjects.forEach(line => {
        if (line) canvas.remove(line);
      });
      
      // 創(chuàng)建最終的多邊形
      finalPolygon = new fabric.Polygon(points.map(p => ({ x: p.x, y: p.y })), {
        fill: 'rgb(227, 242, 202)',
        stroke: 'rgb(169, 224, 36)', // 線段顏色為rgb(169, 224, 36)
        strokeWidth: 2,
        selectable: true,
        globalCompositeOperation: 'multiply' // 使用混合模式而不是透明度
      });
      
      // 移除所有臨時點
      pointObjects.forEach(point => {
        if (point) canvas.remove(point);
      });
      
      // 添加多邊形到畫布
      canvas.add(finalPolygon);
      canvas.renderAll();
      
      // 重置狀態(tài)
      points = [];
      pointObjects = [];
      lineObjects = [];
      isDrawing = false;
      firstPoint = null;
      
      showMessage('多邊形繪制完成', 'success');
    };
    
    // 監(jiān)聽鼠標(biāo)按下事件
    canvas.on('mouse:down', function(o) {
      const pointer = canvas.getPointer(o.e);
      
      // 檢查是否點擊在現(xiàn)有對象上
      // 注意:這里可能導(dǎo)致問題,因為activeLine和activeShape也是對象
      // 只有明確是polygon類型的最終多邊形才應(yīng)該阻止操作
      if (o.target && o.target.type === 'polygon' && !isDrawing) {
        // 如果點擊了現(xiàn)有多邊形且不在繪制模式,只進行選擇
        return;
      }
      
      // 如果是第一次點擊,開始繪制
      if (!isDrawing) {
        // 初始化繪制狀態(tài)
        isDrawing = true;
        points = [];
        pointObjects = [];
        lineObjects = [];
        
        // 記錄第一個點
        firstPoint = { x: pointer.x, y: pointer.y };
        points.push(firstPoint);
        
        // 創(chuàng)建第一個點的可視標(biāo)記(紅色表示起點)
        const firstPointObject = createPoint(pointer.x, pointer.y, true);
        pointObjects.push(firstPointObject);
        
        showMessage(`開始繪制${sidesCount.value}邊形,請繼續(xù)點擊確定頂點位置`, 'info');
      } else {
        console.log('當(dāng)前點數(shù):', points.length, '最大點數(shù):', sidesCount.value);
        
        // 檢查是否點擊了第一個點(閉合多邊形)
        if (isNearFirstPoint(pointer.x, pointer.y)) {
          // 如果已經(jīng)有至少3個點且點擊接近第一個點,閉合多邊形
          if (points.length >= 5) {
            completePolygon();
          } else {
            showMessage(`需要${sidesCount.value}個點才能閉合多邊形。`, 'warning');
          }
        } else {
          // 只有在未達到最大點數(shù)時才添加新點
          if (points.length < sidesCount.value) {
            // 繼續(xù)添加點
            const newPoint = { x: pointer.x, y: pointer.y };
            points.push(newPoint);
            
            // 創(chuàng)建點的可視標(biāo)記
            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}個點,繼續(xù)點擊添加更多點`, 'info');
            // }
            
            // 如果剛好達到最大點數(shù),提示用戶
            if (points.length === sidesCount.value) {
              showMessage(`已達到最大點數(shù)${sidesCount.value},請點擊第一個點完成繪制`, 'warning');
            }
          } else {
            // 已達到最大點數(shù),點擊無效,只能點擊起點完成繪制
            showMessage(`已達到最大點數(shù)${sidesCount.value},請點擊第一個點完成繪制`, 'warning');
          }
          
          // 強制更新畫布
          canvas.renderAll();
        }
      }
    });
    
    // 監(jiān)聽鼠標(biāo)移動事件
    canvas.on('mouse:move', function(o) {
      if (!isDrawing) return;
      
      const pointer = canvas.getPointer(o.e);
      
      // 生成動態(tài)多邊形
      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);
        });
        
        // 重置狀態(tài)
        points = [];
        pointObjects = [];
        lineObjects = [];
        activeLine = null;
        activeShape = null;
        isDrawing = false;
        firstPoint = null;
        
        canvas.renderAll();
        showMessage('已取消繪制', 'info');
      }
    });
  };
})
</script>

<style scoped>
.canvas-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  margin-top: 20px;
  position: relative;
}
</style>

以上就是基于Vue3和Fabric.js實現(xiàn)交互式多邊形繪制功能的詳細內(nèi)容,更多關(guān)于Vue3 Fabric.js交互式多邊形繪制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • vue跳轉(zhuǎn)頁面打開新窗口,并攜帶與接收參數(shù)方式

    vue跳轉(zhuǎn)頁面打開新窗口,并攜帶與接收參數(shù)方式

    這篇文章主要介紹了vue跳轉(zhuǎn)頁面打開新窗口,并攜帶與接收參數(shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 詳解Vue CLI 3.0腳手架如何mock數(shù)據(jù)

    詳解Vue CLI 3.0腳手架如何mock數(shù)據(jù)

    這篇文章主要介紹了詳解Vue CLI 3.0腳手架如何mock數(shù)據(jù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11
  • 詳解Vue的watch中的immediate與watch是什么意思

    詳解Vue的watch中的immediate與watch是什么意思

    這篇文章主要介紹了詳解Vue的watch中的immediate與watch是什么意思,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • vue相同路由跳轉(zhuǎn)強制刷新該路由組件操作

    vue相同路由跳轉(zhuǎn)強制刷新該路由組件操作

    這篇文章主要介紹了vue相同路由跳轉(zhuǎn)強制刷新該路由組件操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • vue之Element-Ui輸入框顯示與隱藏方式

    vue之Element-Ui輸入框顯示與隱藏方式

    這篇文章主要介紹了vue之Element-Ui輸入框顯示與隱藏方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Vue中使用js-cookie詳情

    Vue中使用js-cookie詳情

    這篇文章主要介紹了Vue中使用js-cookie詳情,文章圍繞js-cookie的相關(guān)資料展開詳細內(nèi)容具有一定的的參考價值,需要的小伙伴可以參考一下
    2022-03-03
  • cesium開發(fā)之如何在vue項目中使用cesium,使用離線地圖資源

    cesium開發(fā)之如何在vue項目中使用cesium,使用離線地圖資源

    這篇文章主要介紹了cesium開發(fā)之如何在vue項目中使用cesium,使用離線地圖資源問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • 關(guān)于vue-router路由的傳參方式params query

    關(guān)于vue-router路由的傳參方式params query

    這篇文章主要介紹了關(guān)于vue-router路由的傳參方式params query,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue2老項目中node-sass更換dart-sass的操作方法

    vue2老項目中node-sass更換dart-sass的操作方法

    這篇文章主要介紹了vue2老項目中node-sass更換dart-sass的操作方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2024-07-07
  • Vue3中動態(tài)修改樣式與級聯(lián)樣式優(yōu)先順序圖文詳解

    Vue3中動態(tài)修改樣式與級聯(lián)樣式優(yōu)先順序圖文詳解

    在項目中,我們時常會遇到動態(tài)的去綁定操作切換不同的CSS樣式,下面這篇文章主要給大家介紹了關(guān)于Vue3中動態(tài)修改樣式與級聯(lián)樣式優(yōu)先順序的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-04-04

最新評論