javascript - 如何使用 FabricJS 绘制 1/16 英寸乘 1/16 英寸的网格

标签 javascript css html canvas fabricjs

我正在将 FabricJS 集成到我的 Angular 应用程序中。我已经设法放下 Canvas 并自动在 Canvas 中添加一个图形。

我的目标是使图形为 8 英寸 x 8 英寸,并添加一个具有动态网格高度和宽度的网格(比方说 1/16 英寸 x 1/16 英寸)。

这是我的问题: 1. 添加图形时,不能设置图形的高和宽,单位是英寸。我只能用一个数字。我如何为此使用不同的单位?我对 Canvas 使用了 cssOnly: true 但我似乎无法弄清楚这个数字。

 addFigure(figure) {
    let add: any;
    switch (figure) {
      case 'rectangle':
        add = new fabric.Rect({
          width: 500, height: 500, left: 150, top: 10, angle: 0, opacity: .3,
          fill: '#3f51b5'
        });
        break;
      case 'square':
        add = new fabric.Rect({
          width: 200, height: 200, left: 0, top: 0, angle: 0, opacity: .15,
          fill: '#4caf50'
        });
        break;
      case 'triangle':
        add = new fabric.Triangle({
          width: 100, height: 100, left: 10, top: 10, fill: '#2196f3'
        });
        break;
      case 'circle':
        add = new fabric.Circle({
          radius: 50, left: 10, top: 10, fill: '#ff5722'
        });
        break;
    }
    this.extend(add, this.randomId());
    console.log(add);
    this.canvas.add(add);
    // this.selectItemAfterAdded(add);
  }
  1. 图形高度显示为 200,但看起来不正确。这里以上图为正方形为例。请注意,200 宽度是正确的,但高度似乎不正确,因为 Canvas 是 6 英寸 x 6 英寸。高度延伸通过 Canvas 的底部。

enter image description here

  1. 注意上面的网格。我有同样的问题。这是我从 Adding grid over Fabric.js canvas 中提取的网格代码.我遇到的问题是我不知道如何使这个网格均匀地 1/16 英寸 1/16 英寸,因为这是一个 8 英寸 x 8 英寸的 Canvas 。

    onst gridGroup = new fabric.Group([], {left: 0, top: 0});

    const gridSize = 16;
    const gridWidth = 400;
    const gridHeight = 400;
    const lineOption = {stroke: 'rgba(0,0,0,.4)', strokeWidth: 1, selectable: false, strokeDashArray: [3, 3]};
    
    for (let i = Math.ceil(gridWidth / gridSize); i--;) {
      this.canvas.add( new fabric.Line([gridSize * i, 0, gridSize * i, gridHeight], lineOption) );
    }
    
    for (let i = Math.ceil(gridHeight / gridSize); i--;) {
      this.canvas.add( new fabric.Line([0, gridSize * i, gridWidth, gridSize * i], lineOption) );
    }
    
    this.canvas.add(gridGroup);
    

这是供审查的整个组件。我在设置中做错了什么吗?

 // private canvas: any;
  uploading = false;
  uploaded = false;
  pdfUrl = '';
  canvas: any;
  props: any = {
    canvasFill: '#ffffff',
    canvasImage: '',
    id: null,
    opacity: null,
    fill: null,
    fontSize: null,
    lineHeight: null,
    charSpacing: null,
    fontWeight: null,
    fontStyle: null,
    textAlign: null,
    fontFamily: null,
    TextDecoration: ''
  };

  textString: string;
  url = '';
  size: any = {
    width: '6in',
    height: '6in'
  };

  json: any;
  globalEditor = false;
  textEditor = false;
  imageEditor = false;
  figureEditor = false;
  selected: any;

  constructor(
    private router: Router,
    private userService: UserService,
    private fileService: FileService
  ) { }

  ngOnInit() {
    this.getActive();
    // this.activateCanvas();

    //setup front side canvas
    this.canvas = new fabric.Canvas('canvas', {
      hoverCursor: 'pointer',
      selection: true,
      selectionBorderColor: 'blue'
    });

    this.canvas.on({
      'object:moving': (e) => { },
      'object:modified': (e) => { },
      'object:selected': (e) => {

        const selectedObject = e.target;
        this.selected = selectedObject
        selectedObject.hasRotatingPoint = true;
        selectedObject.transparentCorners = false;
        // selectedObject.cornerColor = 'rgba(255, 87, 34, 0.7)';
        this.resetPanels();

        if (selectedObject.type !== 'group' && selectedObject) {

          this.getId();
          this.getOpacity();

          switch (selectedObject.type) {
            case 'rect':
            case 'circle':
            case 'triangle':
              this.figureEditor = true;
              this.getFill();
              break;
            case 'i-text':
              this.textEditor = true;
              this.getLineHeight();
              this.getCharSpacing();
              this.getBold();
              this.getFontStyle();
              this.getFill();
              this.getTextDecoration();
              this.getTextAlign();
              this.getFontFamily();
              break;
            case 'image':
              break;
          }
        }
      },
      'selection:cleared': (e) => {
        this.selected = null;
        this.resetPanels();
      }
    });

    this.canvas.setWidth(this.size.width, {cssOnly: true});
    this.canvas.setHeight(this.size.height, {cssOnly: true});

    this.addFigure('square');

    const gridGroup = new fabric.Group([], {left: 0, top: 0});

    const gridSize = 16;
    const gridWidth = 400;
    const gridHeight = 400;
    const lineOption = {stroke: 'rgba(0,0,0,.4)', strokeWidth: 1, selectable: false, strokeDashArray: [3, 3]};

    for (let i = Math.ceil(gridWidth / gridSize); i--;) {
      this.canvas.add( new fabric.Line([gridSize * i, 0, gridSize * i, gridHeight], lineOption) );
    }

    for (let i = Math.ceil(gridHeight / gridSize); i--;) {
      this.canvas.add( new fabric.Line([0, gridSize * i, gridWidth, gridSize * i], lineOption) );
    }

    this.canvas.add(gridGroup);

    // for ( let x = 1; x < ( this.canvas.width / gridsize); x++) {
    //   this.canvas.add(new fabric.Line([cellWidth * x, 0, cellWidth * x, 800],
    //     { stroke: '#000000', strokeWidth: 1, selectable: false}));

    //   this.canvas.add(new fabric.Line([0, cellWidth * x, 800, cellWidth * x],
    //     { stroke: '#000000', strokeWidth: 1, selectable: false}));
    // }

    // get references to the html canvas element & its context
    // this.canvas.on('mouse:down', (e) => {
    // let canvasElement: any = document.getElementById('canvas');
    // console.log(canvasElement)
    // });
  }


  /*------------------------Block elements------------------------*/

  //Block "Size"
  changeSize(event: any) {
    this.canvas.setWidth(this.size.width);
    this.canvas.setHeight(this.size.height);
  }

  //Block "Add text"
  addText() {
    let textString = this.textString;
    let text = new fabric.IText(textString, {
      left: 10,
      top: 10,
      fontFamily: 'helvetica',
      angle: 0,
      fill: '#000000',
      scaleX: 0.5,
      scaleY: 0.5,
      fontWeight: '',
      hasRotatingPoint: true
    });
    this.extend(text, this.randomId());
    this.canvas.add(text);
    this.selectItemAfterAdded(text);
    this.textString = '';
  }

  //Block "Add images"
  getImgPolaroid(event: any) {
    let el = event.target;
    fabric.Image.fromURL(el.src, (image) => {
      image.set({
        left: 10,
        top: 10,
        angle: 0,
        padding: 10,
        cornersize: 10,
        hasRotatingPoint: true,
        peloas: 12
      });
      // image.setWidth(150);
      // image.setHeight(150);
      this.extend(image, this.randomId());
      this.canvas.add(image);
      this.selectItemAfterAdded(image);
    });
  }

  //Block "Upload Image"
  addImageOnCanvas(url) {
    if (url) {
      fabric.Image.fromURL(url, (image) => {
        image.set({
          left: 10,
          top: 10,
          angle: 0,
          // scaleX: 200,
          // scaleY: 200,
          padding: 10,
          cornersize: 10,
          hasRotatingPoint: true
        });
        image.scale(0.2);
        // image.setWidth(200);
        // image.height = 200;
        // image.width = 200;
        this.extend(image, this.randomId());
        this.canvas.add(image);
        this.selectItemAfterAdded(image);
      });
    }
  }

  readUrl(event) {
    if (event.target.files && event.target.files[0]) {
      const reader = new FileReader();
      reader.onload = (e) => {
        this.url = e.target['result'];
      };
      reader.readAsDataURL(event.target.files[0]);
    }
  }

  removeWhite(url) {
    this.url = '';
  }


  //Block "Add figure"
  addFigure(figure) {
    let add: any;
    switch (figure) {
      case 'rectangle':
        add = new fabric.Rect({
          width: 500, height: 500, left: 150, top: 10, angle: 0, opacity: .3,
          fill: '#3f51b5'
        });
        break;
      case 'square':
        add = new fabric.Rect({
          width: 200, height: 200, left: 0, top: 0, angle: 0, opacity: .4,
          fill: '#4caf50'
        });
        break;
      case 'triangle':
        add = new fabric.Triangle({
          width: 100, height: 100, left: 10, top: 10, fill: '#2196f3'
        });
        break;
      case 'circle':
        add = new fabric.Circle({
          radius: 50, left: 10, top: 10, fill: '#ff5722'
        });
        break;
    }
    this.extend(add, this.randomId());
    console.log(add);
    this.canvas.add(add);
    // this.selectItemAfterAdded(add);
  }

  /*Canvas*/

  cleanSelect() {
    this.canvas.discardActiveObject();
  }

  selectItemAfterAdded(obj) {
    this.canvas.discardActiveObject();
    this.canvas.setActiveObject(obj);
  }

  setCanvasFill() {
    if (!this.props.canvasImage) {
      this.canvas.backgroundColor = this.props.canvasFill;
      this.canvas.renderAll();
    }
  }

  extend(obj, id) {
    obj.toObject = (function (toObject) {
      return function () {
        return fabric.util.object.extend(toObject.call(this), {
          id: id
        });
      };
    })(obj.toObject);
  }

  setCanvasImage() {
    let self = this;
    if (this.props.canvasImage) {
      this.canvas.setBackgroundColor({ source: this.props.canvasImage, repeat: 'repeat' }, function () {
        // self.props.canvasFill = '';
        self.canvas.renderAll();
      });
    }
  }

  randomId() {
    return Math.floor(Math.random() * 999999) + 1;
  }

  /*------------------------Global actions for element------------------------*/

  getActiveStyle(styleName, object) {
    object = object || this.canvas.getActiveObject();
    if (!object) {return '';}

    return (object.getSelectionStyles && object.isEditing)
      ? (object.getSelectionStyles()[styleName] || '')
      : (object[styleName] || '');
  }


  setActiveStyle(styleName, value, object) {
    object = object || this.canvas.getActiveObject();
    if (!object) {return '';}

    if (object.setSelectionStyles && object.isEditing) {
      let style = {};
      style[styleName] = value;
      object.setSelectionStyles(style);
      object.setCoords();
    } else {
      object.set(styleName, value);
    }

    object.setCoords();
    this.canvas.renderAll();
  }


  getActiveProp(name) {
    let object = this.canvas.getActiveObject();
    if (!object) {return '';}

    return object[name] || '';
  }

  setActiveProp(name, value) {
    const object = this.canvas.getActiveObject();
    if (!object) {return ''};
    object.set(name, value).setCoords();
    this.canvas.renderAll();
  }

  clone() {
    const activeObject = this.canvas.getActiveObject(),
      activeGroup = this.canvas.getActiveGroup();

    if (activeObject) {
      let clone;
      switch (activeObject.type) {
        case 'rect':
          clone = new fabric.Rect(activeObject.toObject());
          break;
        case 'circle':
          clone = new fabric.Circle(activeObject.toObject());
          break;
        case 'triangle':
          clone = new fabric.Triangle(activeObject.toObject());
          break;
        case 'i-text':
          clone = new fabric.IText('', activeObject.toObject());
          break;
        case 'image':
          clone = fabric.util.object.clone(activeObject);
          break;
      }
      if (clone) {
        clone.set({ left: 10, top: 10 });
        this.canvas.add(clone);
        this.selectItemAfterAdded(clone);
      }
    }
  }

  getId() {
    this.props.id = this.canvas.getActiveObject().toObject().id;
  }

  setId() {
    const val = this.props.id;
    const complete = this.canvas.getActiveObject().toObject();
    console.log(complete);
    this.canvas.getActiveObject().toObject = () => {
      complete.id = val;
      return complete;
    };
  }

  getOpacity() {
    this.props.opacity = this.getActiveStyle('opacity', null) * 100;
  }

  setOpacity() {
    this.setActiveStyle('opacity', +this.props.opacity / 100, null);
  }

  getFill() {
    this.props.fill = this.getActiveStyle('fill', null);
  }

  setFill() {
    this.setActiveStyle('fill', this.props.fill, null);
  }

  getLineHeight() {
    this.props.lineHeight = this.getActiveStyle('lineHeight', null);
  }

  setLineHeight() {
    this.setActiveStyle('lineHeight', parseFloat(this.props.lineHeight), null);
  }

  getCharSpacing() {
    this.props.charSpacing = this.getActiveStyle('charSpacing', null);
  }

  setCharSpacing() {
    this.setActiveStyle('charSpacing', this.props.charSpacing, null);
  }

  getFontSize() {
    this.props.fontSize = this.getActiveStyle('fontSize', null);
  }

  setFontSize() {
    this.setActiveStyle('fontSize', +this.props.fontSize, null);
  }

  getBold() {
    this.props.fontWeight = this.getActiveStyle('fontWeight', null);
  }

  setBold() {
    this.props.fontWeight = !this.props.fontWeight;
    this.setActiveStyle('fontWeight', this.props.fontWeight ? 'bold' : '', null);
  }

  getFontStyle() {
    this.props.fontStyle = this.getActiveStyle('fontStyle', null);
  }

  setFontStyle() {
    this.props.fontStyle = !this.props.fontStyle;
    this.setActiveStyle('fontStyle', this.props.fontStyle ? 'italic' : '', null);
  }


  getTextDecoration() {
    this.props.TextDecoration = this.getActiveStyle('textDecoration', null);
  }

  setTextDecoration(value) {
    let iclass = this.props.TextDecoration;
    if (iclass.includes(value)) {
      iclass = iclass.replace(RegExp(value, 'g'), '');
    } else {
      iclass += ` ${value}`
    }
    this.props.TextDecoration = iclass;
    this.setActiveStyle('textDecoration', this.props.TextDecoration, null);
  }

  hasTextDecoration(value) {
    return this.props.TextDecoration.includes(value);
  }


  getTextAlign() {
    this.props.textAlign = this.getActiveProp('textAlign');
  }

  setTextAlign(value) {
    this.props.textAlign = value;
    this.setActiveProp('textAlign', this.props.textAlign);
  }

  getFontFamily() {
    this.props.fontFamily = this.getActiveProp('fontFamily');
  }

  setFontFamily() {
    this.setActiveProp('fontFamily', this.props.fontFamily);
  }

  /*System*/


  removeSelected() {
    let activeObject = this.canvas.getActiveObject(),
      activeGroup = this.canvas.getActiveGroup();

    if (activeObject) {
      this.canvas.remove(activeObject);
      // this.textString = '';
    } else if (activeGroup) {
      let objectsInGroup = activeGroup.getObjects();
      this.canvas.discardActiveGroup();
      let self = this;
      objectsInGroup.forEach(function (object) {
        self.canvas.remove(object);
      });
    }
  }

  bringToFront() {
    let activeObject = this.canvas.getActiveObject(),
      activeGroup = this.canvas.getActiveGroup();

    if (activeObject) {
      activeObject.bringToFront();
      // activeObject.opacity = 1;
    }
    else if (activeGroup) {
      let objectsInGroup = activeGroup.getObjects();
      this.canvas.discardActiveGroup();
      objectsInGroup.forEach((object) => {
        object.bringToFront();
      });
    }
  }

  sendToBack() {
    const activeObject = this.canvas.getActiveObject(),
      activeGroup = this.canvas.getActiveGroup();

    if (activeObject) {
      activeObject.sendToBack();
      // activeObject.opacity = 1;
    } else if (activeGroup) {
      const objectsInGroup = activeGroup.getObjects();
      this.canvas.discardActiveGroup();
      objectsInGroup.forEach((object) => {
        object.sendToBack();
      });
    }
  }

  confirmClear() {
    if (confirm('Are you sure?')) {
      this.canvas.clear();
    }
  }

  rasterize() {
    if (!fabric.Canvas.supports('toDataURL')) {
      alert('This browser doesn\'t provide means to serialize canvas to an image');
    } else {
      console.log(this.canvas.toDataURL('png'))
      //window.open(this.canvas.toDataURL('png'));
      let image = new Image();
      image.src = this.canvas.toDataURL('png')
      let w = window.open('');
      w.document.write(image.outerHTML);
    }
  }

  rasterizeSVG() {
    console.log(this.canvas.toSVG())
    // window.open(
    //   'data:image/svg+xml;utf8,' +
    //   encodeURIComponent(this.canvas.toSVG()));
    // console.log(this.canvas.toSVG())
    // let image = new Image();
    // image.src = this.canvas.toSVG()
    const w = window.open('');
    w.document.write(this.canvas.toSVG());
  }


  saveCanvasToJSON() {
    const json = JSON.stringify(this.canvas);
    localStorage.setItem('Kanvas', json);
    console.log('json');
    console.log(json);

  }

  loadCanvasFromJSON() {
    const CANVAS = localStorage.getItem('Kanvas');
    console.log('CANVAS');
    console.log(CANVAS);

    // and load everything from the same json
    this.canvas.loadFromJSON(CANVAS, () => {
      console.log('CANVAS untar');
      console.log(CANVAS);

      // making sure to render canvas at the end
      this.canvas.renderAll();

      // and checking if object's "name" is preserved
      console.log('this.canvas.item(0).name');
      console.log(this.canvas);
    });

  }

  rasterizeJSON() {
    this.json = JSON.stringify(this.canvas, null, 2);
  }

  resetPanels() {
    this.textEditor = false;
    this.imageEditor = false;
    this.figureEditor = false;
  }

总而言之,我希望在 8 英寸 x 8 英寸的 Canvas 上使用 1/16 英寸 x 1/16 英寸(可能是动态的)网格,并在此 Canvas 内添加一个也是偶数正方形的图形。

最佳答案

对于遇到同样问题的任何人。我传递的 cssOnly bool 参数选项似乎不是将 Canvas 实际缩放到英寸的解决方案。

我是这样解决的。

1) 我现在有一个输入允许用户根据此 url 上的信息手动输入他们的 PPI - http://dpi.lv/

2) 我保存那个 PPI

3) 我创建了一个 calcPixels(inches: number) 方法,它使用 pixels = inches * ppi 来计算所需的像素数

4) 我在整个应用程序中针对所有宽度、高度、网格线等使用该方法。

这让我可以缩放 7 英寸 x 7 英寸的网格线。它的自动化程度较低,但如果您保存 PPI,用户只需输入一次!

新结果

enter image description here

关于javascript - 如何使用 FabricJS 绘制 1/16 英寸乘 1/16 英寸的网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48677184/

相关文章:

javascript - 使用 uikit 扩展下拉菜单

javascript - 在 JavaScript 中传递事件触发器的索引

JavaScript 函数不会加载 html 页面

html - 使用 css 的全屏网页布局问题

javascript - 根据相邻元素的大小或值隐藏 flexbox 元素

javascript - 如何在 Bootstrap 中通过触摸(或鼠标)在幻灯片中启用滑动图像?

javascript - 使包装器 div 围绕动态响应图像收缩

javascript - 在 HTML 页面中添加选择时区的选项

javascript - 使用 Node.js 比较两个对象

html - 输入转换