Control.polygonPositionHandler 处未定义(读为“x”)

问题描述 投票:0回答:1

我有一个代码,可以使用 javascript 和 Fabric JS 绘制和编辑多边形。我希望它能工作成角度并尝试将其转换,但其他功能不起作用,原始代码可以很好地添加和编辑。

顺便说一句,我正在使用 Fabric js 在画布上绘图,我也在使用 Angular 版本 12 >>

有人知道如何解决编辑多边形功能中的问题吗?谢谢你。

我将不胜感激任何可以提供帮助并指出问题的人。

#原代码

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>

<script src="https://unpkg.com/[email protected]/dist/fabric.js"></script>

    <button type="button" onclick="toggleDrawPolygon()">Draw Polygon</button>
    <button type="button" onclick="editPolygon()">Edit Polygon</button>
    <button type="button" onclick="resizePolygon()">Resize/Move Polygon</button>
    <button type="button" onclick="clearPolygon()">Clear</button>

    <canvas id="c"></canvas>


    <script type="text/javascript">
        let activeLine;
    let activeShape;
    let canvas;
    let lineArray = [];
    let pointArray = [];
    let drawMode = false;

    function initCanvas() {
        canvas = new fabric.Canvas('c');
        // canvas.backgroundColor = 'rgba(0,0,0,0.3)';
        canvas.setHeight(720);
        canvas.setWidth(1280);
        var imageUrl = "https://images.wallpaperscraft.com/image/road_asphalt_marking_130996_1280x720.jpg";

        // Define 
        canvas.setBackgroundImage(imageUrl, canvas.renderAll.bind(canvas), {
            // Optionally add an opacity lvl to the image
            backgroundImageOpacity: 0.5,
            // should the image be resized to fit the container?
            backgroundImageStretch: true
        });

        //fabric.Object.prototype.originX = 'center';
        //fabric.Object.prototype.originY = 'center';


        canvas.on('mouse:down', onMouseDown);
        canvas.on('mouse:up', onMouseUp);
        canvas.on('mouse:move', onMouseMove);
        canvas.on('object:moving', onObjectMove);
        // canvas.on('mouse:wheel', onMouseWheel);
    }

    function onMouseDown(options) {    
        if (drawMode) {
            if (options.target && options.target.id === pointArray[0].id) {
                // when click on the first point
                generatePolygon(pointArray);
            } else {
                addPoint(options);
            }
        }
        
        var evt = options.e;
        if (evt.altKey === true) {
            this.isDragging = true;
            this.selection = false;
            this.lastPosX = evt.clientX;
            this.lastPosY = evt.clientY;
        }
    }

    function onMouseUp(options) {
      this.isDragging = false;
      this.selection = true;
    }

    function onMouseMove(options) {
        if (this.isDragging) {
            var e = options.e;
            this.viewportTransform[4] += e.clientX - this.lastPosX;
            this.viewportTransform[5] += e.clientY - this.lastPosY;
            this.requestRenderAll();
            this.lastPosX = e.clientX;
            this.lastPosY = e.clientY;
        } 
        if (drawMode) {
            if (activeLine && activeLine.class === 'line') {
                const pointer = canvas.getPointer(options.e);
                activeLine.set({
                    x2: pointer.x,
                    y2: pointer.y
                });
                const points = activeShape.get('points');
                points[pointArray.length] = {
                    x: pointer.x,
                    y: pointer.y,
                };
                activeShape.set({
                    points
                });
            }
            canvas.renderAll();
        }
    }

    function onMouseWheel(options) {
      var delta = options.e.deltaY;
      var pointer = canvas.getPointer(options.e);
      var zoom = canvas.getZoom();  
      if (delta > 0) {
        zoom += 0.1;
      } else {
        zoom -= 0.1;
      }
      if (zoom > 20) zoom = 20;
      if (zoom < 0.1) zoom = 0.1;
      canvas.zoomToPoint({ x: options.e.offsetX, y: options.e.offsetY }, zoom);
      options.e.preventDefault();
      options.e.stopPropagation();
    }

    function onObjectMove(option) {
        const object = option.target;
        object._calcDimensions();
        object.setCoords();    
        canvas.renderAll();
    }

    function toggleDrawPolygon(event) {
        if (drawMode) {
            // stop draw mode
            activeLine = null;
            activeShape = null;
            lineArray = [];
            pointArray = [];
            canvas.selection = true;
            drawMode = false;
        } else {
            // start draw mode
            canvas.selection = false;
            drawMode = true;
        }
    }

    function addPoint(options) {
        const pointOption = {
            id: new Date().getTime(),
            radius: 5,
            fill: '#ffffff',
            stroke: '#333333',
            strokeWidth: 0.5,
            left: options.e.layerX / canvas.getZoom(),
            top: options.e.layerY / canvas.getZoom(),
            selectable: false,
            hasBorders: false,
            hasControls: false,
            originX: 'center',
            originY: 'center',
            objectCaching: false,
        };
        const point = new fabric.Circle(pointOption);

        if (pointArray.length === 0) {
            // fill first point with red color
            point.set({
                fill: 'red'
            });
        }

        const linePoints = [
            options.e.layerX / canvas.getZoom(),
            options.e.layerY / canvas.getZoom(),
            options.e.layerX / canvas.getZoom(),
            options.e.layerY / canvas.getZoom(),
        ];
        const lineOption = {
            strokeWidth: 2,
            fill: '#999999',
            stroke: '#999999',
            originX: 'center',
            originY: 'center',
            selectable: false,
            hasBorders: false,
            hasControls: false,
            evented: false,
            objectCaching: false,
        };
        const line = new fabric.Line(linePoints, lineOption);
        line.class = 'line';

        if (activeShape) {
            const pos = canvas.getPointer(options.e);
            const points = activeShape.get('points');
            points.push({
                x: pos.x,
                y: pos.y
            });
            const polygon = new fabric.Polygon(points, {
                stroke: '#333333',
                strokeWidth: 1,
                fill: '#cccccc',
                opacity: 0.3,
                selectable: false,
                hasBorders: false,
                hasControls: false,
                evented: false,
                objectCaching: false,
            });
            canvas.remove(activeShape);
            canvas.add(polygon);
            activeShape = polygon;
            canvas.renderAll();
        } else {
            const polyPoint = [{
                x: options.e.layerX / canvas.getZoom(),
                y: options.e.layerY / canvas.getZoom(),
            }, ];
            const polygon = new fabric.Polygon(polyPoint, {
                stroke: '#333333',
                strokeWidth: 1,
                fill: '#cccccc',
                opacity: 0.3,
                selectable: false,
                hasBorders: false,
                hasControls: false,
                evented: false,
                objectCaching: false,
            });
            activeShape = polygon;
            canvas.add(polygon);
        }

        activeLine = line;
        pointArray.push(point);
        lineArray.push(line);

        canvas.add(line);
        canvas.add(point);
    }

    function generatePolygon(pointArray) {
        const points = [];
        // collect points and remove them from canvas
        for (const point of pointArray) {
            points.push({
                x: point.left,
                y: point.top,
            });
            canvas.remove(point);
        }

        // remove lines from canvas
        for (const line of lineArray) {
            canvas.remove(line);
        }

        // remove selected Shape and Line 
        canvas.remove(activeShape).remove(activeLine);

        // create polygon from collected points
        console.log(points)
        const polygon = new fabric.Polygon(points, {
            id: new Date().getTime(),
            stroke: '#0084ff',
            fill: false,
            objectCaching: false,
            moveable: false,
            //selectable: false
        });
        canvas.add(polygon);

        toggleDrawPolygon();
        editPolygon();
    }

    /**
     * define a function that can locate the controls.
     * this function will be used both for drawing and for interaction.
     */
    function polygonPositionHandler(dim, finalMatrix, fabricObject) {
      var x = (fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x),
            y = (fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y);
        return fabric.util.transformPoint(
            { x: x, y: y },
      fabric.util.multiplyTransformMatrices(
        fabricObject.canvas.viewportTransform,
        fabricObject.calcTransformMatrix()
      )
        );
    }

    /**
     * define a function that will define what the control does
     * this function will be called on every mouse move after a control has been
     * clicked and is being dragged.
     * The function receive as argument the mouse event, the current trasnform object
     * and the current position in canvas coordinate
     * transform.target is a reference to the current object being transformed,
     */
    function actionHandler(eventData, transform, x, y) {
        var polygon = transform.target,
            currentControl = polygon.controls[polygon.__corner],
            mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center'),
        polygonBaseSize = polygon._getNonTransformedDimensions(),
                size = polygon._getTransformedDimensions(0, 0),
                finalPointPosition = {
                    x: mouseLocalPosition.x * polygonBaseSize.x / size.x + polygon.pathOffset.x,
                    y: mouseLocalPosition.y * polygonBaseSize.y / size.y + polygon.pathOffset.y
                };
        polygon.points[currentControl.pointIndex] = finalPointPosition;
        return true;
    }

    /**
     * define a function that can keep the polygon in the same position when we change its
     * width/height/top/left.
     */
  function anchorWrapper(anchorIndex, fn) {
    return function(eventData, transform, x, y) {
      var fabricObject = transform.target,
          absolutePoint = fabric.util.transformPoint({
              x: (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x),
              y: (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y),
          }, fabricObject.calcTransformMatrix()),
          actionPerformed = fn(eventData, transform, x, y),
          newDim = fabricObject._setPositionDimensions({}),
          polygonBaseSize = fabricObject._getNonTransformedDimensions(),
          newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x,
            newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
      fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
      return actionPerformed;
    }
  }

    function editPolygon() {
        let activeObject = canvas.getActiveObject();
        if (!activeObject) {
            activeObject = canvas.getObjects()[0];
            canvas.setActiveObject(activeObject);
        }

        activeObject.edit = true;
        activeObject.objectCaching = false;

        const lastControl = activeObject.points.length - 1;
        activeObject.cornerStyle = 'circle';
        activeObject.controls = activeObject.points.reduce((acc, point, index) => {
            acc['p' + index] = new fabric.Control({
                positionHandler: polygonPositionHandler,
                actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
                actionName: 'modifyPolygon',
                pointIndex: index,
            });
            return acc;
        }, {});

        activeObject.hasBorders = false;

        canvas.requestRenderAll();
    }

    function resizePolygon() {
        let activeObject = canvas.getActiveObject();
        if (!activeObject) {
            activeObject = canvas.getObjects()[0];
            canvas.setActiveObject(activeObject);
        }

        activeObject.edit = false;
        activeObject.objectCaching = false;
        activeObject.controls = fabric.Object.prototype.controls;
        activeObject.cornerStyle = 'rect';
        activeObject.hasBorders = true;

        canvas.requestRenderAll();
    }

    function clearPolygon(){
        canvas.remove(...canvas.getObjects());
    }

    initCanvas();
    </script>
</body>
</html>

#转换后的html代码

<div>
  <button type="button" (click)="toggleDrawPolygon()">Draw Polygon</button>
  <button type="button" (click)="editPolygon()">Edit Polygon</button>
  <button type="button" (click)="resizePolygon()">Resize/Move Polygon</button>
  <button type="button" (click)="clearPolygon()">Clear</button>
</div>

<canvas id="c"></canvas>

#converted ts code


import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
const fabric = require("fabric").fabric;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements  OnInit, AfterViewInit {
  
  activeLine: any;
  activeShape: any;
  canvas: any;
  lineArray: any[] = [];
  pointArray: any[] = [];
  drawMode = false;
  pointIndex: any;

  constructor() { }

  ngOnInit(): void {
  }

  ngAfterViewInit(): void {
    this.initCanvas();
  }

  initCanvas() {
    this.canvas = new fabric.Canvas('c');
    this.canvas.setHeight(720);
    this.canvas.setWidth(1280);
    const imageUrl = "https://wpmedia.roomsketcher.com/content/uploads/2021/12/14164614/RoomSketcher-House-Floor-Plans-2452430-800.jpg";

    this.canvas.setBackgroundImage(imageUrl, this.canvas.renderAll.bind(this.canvas), {
      backgroundImageOpacity: 0.5,
      backgroundImageStretch: true
    });

    this.canvas.on('mouse:down', (options: any) => this.onMouseDown(options));
    this.canvas.on('mouse:up', () => this.onMouseUp());
    this.canvas.on('mouse:move', (options: any) => this.onMouseMove(options));
    this.canvas.on('object:moving', (option: any) => this.onObjectMove(option));
  }

  onMouseDown(options: any) {
    if (this.drawMode) {
      if (options.target && options.target.id === this.pointArray[0].id) {
        this.generatePolygon(this.pointArray);
      } else {
        this.addPoint(options);
      }
    }

    const evt = options.e;
    if (evt.altKey === true) {
      this.canvas.isDragging = true;
      this.canvas.selection = false;
      this.canvas.lastPosX = evt.clientX;
      this.canvas.lastPosY = evt.clientY;
    }
  }

  onMouseUp() {
    this.canvas.isDragging = false;
    this.canvas.selection = true;
  }

  onMouseMove(options: any) {
    if (this.canvas.isDragging) {
      const e = options.e;
      this.canvas.viewportTransform[4] += e.clientX - this.canvas.lastPosX;
      this.canvas.viewportTransform[5] += e.clientY - this.canvas.lastPosY;
      this.canvas.requestRenderAll();
      this.canvas.lastPosX = e.clientX;
      this.canvas.lastPosY = e.clientY;
    }
    if (this.drawMode) {
      if (this.activeLine && this.activeLine.class === 'line') {
        const pointer = this.canvas.getPointer(options.e);
        this.activeLine.set({
          x2: pointer.x,
          y2: pointer.y
        });
        const points = this.activeShape.get('points');
        points[this.pointArray.length] = {
          x: pointer.x,
          y: pointer.y,
        };
        this.activeShape.set({
          points
        });
      }
      this.canvas.renderAll();
    }
  }

  onObjectMove(option: any) {
    const object = option.target;
    object._calcDimensions();
    object.setCoords();
    this.canvas.renderAll();
  }

  toggleDrawPolygon() {
    if (this.drawMode) {
      this.activeLine = null;
      this.activeShape = null;
      this.lineArray = [];
      this.pointArray = [];
      this.canvas.selection = true;
      this.drawMode = false;
    } else {
      this.canvas.selection = false;
      this.drawMode = true;
    }
  }

  addPoint(options: any) {
    const pointOption = {
      id: new Date().getTime(),
      radius: 5,
      fill: '#ffffff',
      stroke: '#333333',
      strokeWidth: 0.5,
      left: options.e.layerX / this.canvas.getZoom(),
      top: options.e.layerY / this.canvas.getZoom(),
      selectable: false,
      hasBorders: false,
      hasControls: false,
      originX: 'center',
      originY: 'center',
      objectCaching: false,
    };
    const point = new fabric.Circle(pointOption);

    if (this.pointArray.length === 0) {
      point.set({
        fill: 'red'
      });
    }

    const linePoints = [
      options.e.layerX / this.canvas.getZoom(),
      options.e.layerY / this.canvas.getZoom(),
      options.e.layerX / this.canvas.getZoom(),
      options.e.layerY / this.canvas.getZoom(),
    ];
    const lineOption = {
      strokeWidth: 2,
      fill: '#999999',
      stroke: '#999999',
      originX: 'center',
      originY: 'center',
      selectable: false,
      hasBorders: false,
      hasControls: false,
      evented: false,
      objectCaching: false,
    };
    const line = new fabric.Line(linePoints, lineOption);

// Set a custom property 'customClass' on the line object
      (line as any).customClass = 'line';


    if (this.activeShape) {
      const pos = this.canvas.getPointer(options.e);
      const points = this.activeShape.get('points');
      points.push({
        x: pos.x,
        y: pos.y
      });
      const polygon = new fabric.Polygon(points, {
        stroke: '#333333',
        strokeWidth: 1,
        fill: '#cccccc',
        opacity: 0.3,
        selectable: false,
        hasBorders: false,
        hasControls: false,
        evented: false,
        objectCaching: false,
      });
      this.canvas.remove(this.activeShape);
      this.canvas.add(polygon);
      this.activeShape = polygon;
      this.canvas.renderAll();
    } else {
      const polyPoint = [{
        x: options.e.layerX / this.canvas.getZoom(),
        y: options.e.layerY / this.canvas.getZoom(),
      }];
      const polygon = new fabric.Polygon(polyPoint, {
        stroke: '#333333',
        strokeWidth: 1,
        fill: '#cccccc',
        opacity: 0.3,
        selectable: false,
        hasBorders: false,
        hasControls: false,
        evented: false,
        objectCaching: false,
      });
      this.activeShape = polygon;
      this.canvas.add(polygon);
    }

    this.activeLine = line;
    this.pointArray.push(point);
    this.lineArray.push(line);

    this.canvas.add(line);
    this.canvas.add(point);
  }

  generatePolygon(pointArray: any) {
    const points = [];
    for (const point of pointArray) {
      points.push({
        x: point.left,
        y: point.top,
      });
      this.canvas.remove(point);
    }
  
    for (const line of this.lineArray) {
      this.canvas.remove(line);
    }
  
    this.canvas.remove(this.activeShape).remove(this.activeLine);
  
    const polygon = new fabric.Polygon(points, {
      stroke: '#0084ff',
      fill: 'black',
    });
    this.canvas.add(polygon);
  
    this.toggleDrawPolygon();
    this.editPolygon();
  }


  polygonPositionHandler(dim : any, finalMatrix : any, fabricObject : any) {
    var x = (fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x),
          y = (fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y);
      return fabric.util.transformPoint(
          { x: x, y: y },
    fabric.util.multiplyTransformMatrices(
      fabricObject.canvas.viewportTransform,
      fabricObject.calcTransformMatrix()
    )
      );
  }

  actionHandler(eventData: any, transform: any, x: any, y: any) {
    const polygon = transform.target;
    const currentControl = polygon.controls[polygon.__corner];
    const mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center');
    const polygonBaseSize = polygon._getNonTransformedDimensions();
    const size = polygon._getTransformedDimensions(0, 0);
    const finalPointPosition = {
      x: mouseLocalPosition.x * polygonBaseSize.x / size.x + polygon.pathOffset.x,
      y: mouseLocalPosition.y * polygonBaseSize.y / size.y + polygon.pathOffset.y
    };
    polygon.points[currentControl.pointIndex] = finalPointPosition;
    return true;
  }

  anchorWrapper(anchorIndex: any, fn: any) {
    return (eventData: any, transform: any, x: any, y: any) => {
      const fabricObject = transform.target;
      const point = new fabric.Point(
        fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x,
        fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y
      );
      const absolutePoint = fabric.util.transformPoint(point, fabricObject.calcTransformMatrix());
      const actionPerformed = fn(eventData, transform, x, y);
      const newDim = fabricObject._setPositionDimensions({});
      const polygonBaseSize = fabricObject._getNonTransformedDimensions();
      const newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x;
      const newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
      fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
      return actionPerformed;
    };
  }

 

  editPolygon() {
    let activeObject =  this.canvas.getActiveObject();
    if (!activeObject) {
        activeObject =  this.canvas.getObjects()[0];
        this.canvas.setActiveObject(activeObject);
    }

    activeObject.edit = true;
    activeObject.objectCaching = false;

    const lastControl = activeObject.points.length - 1;
    activeObject.cornerStyle = 'circle';
    activeObject.controls = activeObject.points.reduce((acc:any,index:any) => {
        acc['p' + index] = new fabric.Control({
            positionHandler: this.polygonPositionHandler,
            actionHandler: this.anchorWrapper(index > 0 ? index - 1 : lastControl, this.actionHandler),
            actionName: 'modifyPolygon',
            pointIndex: index,
        });
        return acc;
    }, {});

    activeObject.hasBorders = false;
    this.canvas.requestRenderAll();
}

  resizePolygon() {
    let activeObject = this.canvas.getActiveObject();
    if (!activeObject) {
      activeObject = this.canvas.getObjects()[0];
      this.canvas.setActiveObject(activeObject);
    }

    activeObject.edit = false;
    activeObject.objectCaching = false;
    activeObject.controls = fabric.Object.prototype.controls;
    activeObject.cornerStyle = 'rect';
    activeObject.hasBorders = true;

    this.canvas.requestRenderAll();
  }

  clearPolygon() {
    this.canvas.remove(...this.canvas.getObjects());
  }
}
javascript angular typescript fabricjs
1个回答
0
投票

this.pointIndex
中的
polygonPositionHandler
并不是指当前控件的属性。您应该能够使用
const currentControl = polygon.controls[polygon.__corner];
获取当前控制,如
actionHandler

中所定义

这里更新了

polygonPositionHandler

  polygonPositionHandler(dim : any, finalMatrix : any, fabricObject : any) {
      const currentControl = polygon.controls[polygon.__corner];
      var x = (fabricObject.points[currentControl.pointIndex].x - fabricObject.pathOffset.x),
          y = (fabricObject.points[currentControl.pointIndex].y - fabricObject.pathOffset.y);
      return fabric.util.transformPoint(
          { x: x, y: y },
    fabric.util.multiplyTransformMatrices(
      fabricObject.canvas.viewportTransform,
      fabricObject.calcTransformMatrix()
    )
      );
  }
© www.soinside.com 2019 - 2024. All rights reserved.