mercredi 28 avril 2021

Fabric JS Polyline points update issue

I'm having issue with the update of Polyline points.

I want to be able to move my arrow, and also to change two points of it.

When I move my arrow (left, top), the points doesn't update. So I update them manually, and the issue comes here.

For me, maybe I'm wrong, my point calculation is good, but fabric displays me the opposite.

I've made a jsfiddle which reproduces the issue: https://jsfiddle.net/7k6oyuL4/1/


    const calcNewAngle = ({ x1, y1, x2, y2 }) => {
      const width = Math.abs(Math.max(x1, x2) - Math.min(x1, x2));
      const height = Math.abs(Math.max(y1, y2) - Math.min(y1, y2));
      let theta;
      if (x1 < x2 && y1 < y2) {
        theta = Math.atan(height / width);
      } else if (x1 < x2 && y1 >= y2) {
        theta = Math.atan(-height / width);
      } else if (x1 >= x2 && y1 < y2) {
        theta = Math.abs(Math.atan(height / width) - Math.PI);
      } else {
        theta = Math.abs(Math.atan(-height / width) - Math.PI);
      }
      return theta;
    };
    
    const setArrowPoints = ({x1, y1, x2, y2}) => {
      const angle = calcNewAngle({x1, y1, x2, y2});
      const headlen = 8;
      return [
        {
          x: x1,
          y: y1
        },
        {
          x: x2,
          y: y2
        },
        {
          x: x2 - headlen * Math.cos(angle - Math.PI / 2),
          y: y2 - headlen * Math.sin(angle - Math.PI / 2)
        },
        {
          x: x2 + headlen * Math.cos(angle),
          y: y2 + headlen * Math.sin(angle)
        },
        {
          x: x2 - headlen * Math.cos(angle + Math.PI / 2),
          y: y2 - headlen * Math.sin(angle + Math.PI / 2)
        },
        {
          x: x2,
          y: y2
        }
      ];
    }
    
    const getX1Y1X2Y2 = points => {
        return {
        x1: points[0].x,
        y1: points[0].y,
        x2: points[1].x,
        y2: points[1].y
      }
    }
    
    const updateArrow = (e, canvas, arrow, index) => {
        const mousePos = canvas.getPointer(e);
      const {points} = arrow;
      points[index].x = mousePos.x;
      points[index].y = mousePos.y;
      arrow.set('points', setArrowPoints(getX1Y1X2Y2(points)))
    }
    
    const createControl = ({canvas, index, arrow}) => {
        const left = arrow.points[index].x;
      const top = arrow.points[index].y;
      const control = new fabric.Circle({
        index,
        left, 
        top,
        radius: 5,
        strokeWidth: 1,
        hasBorders: false,
        stroke: 'rgba(0,0,255,1)',
        fill: 'rgba(255,255,255,1)',
        originX: 'center',
        originY: 'center'
      });
      control.setControlsVisibility({
        bl: false,
        br: false,
        mb: false,
        ml: false,
        mr: false,
        mt: false,
        tl: false,
        tr: false,
        mtr: false
      });
      control.on('moving', e => updateArrow(e, canvas, arrow, index));
      return control;
    }
    
    const handleMoving = (e, canvas, controls) => {
      const {target} = e.transform;
      if (!target.alreadyLogged) {
        target.alreadyLogged = true;
        console.log('handleMoving', target.points);
      controls.forEach(control => control.set('visible', false))
      }
    }
    
    const handleMoved = (e, canvas, controls) => {
      console.log('###### handleMoved ######')
      const {target, transform} = e;
      const {left: originLeft, top: originTop} = transform.original;
      const {left: newLeft, top: newTop, points} = target;
      const leftDiff = newLeft - originLeft;
      const topDiff = newTop - originTop;
      console.log({originLeft, newLeft, leftDiff, originTop, newTop, topDiff});
      console.log('pointsBeforeManualUpdate', getX1Y1X2Y2(points))
      const x1 = points[0].x + leftDiff;
      const y1 = points[0].y + topDiff;
      const x2 = points[1].x + leftDiff;
      const y2 = points[1].y + topDiff;
      target.set('points', setArrowPoints({x1, y1, x2, y2}))
      console.log('pointsAfterManualUpdate', getX1Y1X2Y2(target.points))
      controls.forEach(control => {
        const {index} = control;
        const leftControl = target.points[index].x;
        const topControl = target.points[index].y;
        control.set({
            visible: true,
            left: leftControl, 
          top: topControl
        })
      })
        target.alreadyLogged = false;
    }
      
    const createArrow = ({canvas, points}) => {
      const arrowPoints = setArrowPoints(points);
      const arrow = new fabric.Polyline(arrowPoints, {
        strokeWidth: 2,
        stroke: 'rgba(0,0,0,1)',
        fill: 'rgba(0,0,0,1)',
        perPixelTargetFind: true
      });
      arrow.setControlsVisibility({
        bl: false,
        br: false,
        mb: false,
        ml: false,
        mr: false,
        mt: false,
        tl: false,
        tr: false,
        mtr: false
      });
        
      const control1 = createControl({canvas, index: 0, arrow})
      const control2 = createControl({canvas, index: 1, arrow})
      
      arrow.on('moving', e => handleMoving(e, canvas, [control1, control2]))
      arrow.on('moved', e => handleMoved(e, canvas, [control1, control2]))
    
      canvas.add(arrow)
      canvas.add(control1)
      canvas.add(control2)
      return arrow;
    }
    
    (function() {
      const canvas = this.__canvas = new fabric.Canvas('c', { width: 1500, height: 1000 });
        const arrowObj = createArrow({canvas, points: {x1: 50, y1: 50, x2: 200, y2: 200}})
    })();

Thanks in advance for your help!




Aucun commentaire:

Enregistrer un commentaire