Search code examples
ember.jsfabricjs

Fabric JS Tint Filter when applied resizes image Ember project


I have implemented Fabric in a Emberjs project, tinting a placed image causes the image to reduce in the lager markings. **Update, it may be a CORS issue as adding a user image can be tinted and keeps its dimensions correctly.

component handlebars

<div id="select-image-dimensions" class="placeholder canvas-size" {{did-insert this.renderWallCanvas}}>
</div>

component javascript

import Component from '@glimmer/component';
import ENV from '../config/environment';
import AWS from 'aws-sdk';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { fabric } from 'fabric';
import Picker from 'vanilla-picker';

export default class WallToDecorateComponent extends Component {
  @service('captured-image') capturedImage;
  @tracked canvasToDecorate = null;
  @tracked isDown = false;
  @tracked currentColor = null;

  @action
  renderWallCanvas() {
    var canvasAspect, imgAspectLandscape, imgAspectPortrait, left, top, scaleFactor, rect;
    let elemSize = document.querySelector('#select-image-dimensions');

    if(elemSize) {
      rect = elemSize.getBoundingClientRect();
      var canvas = document.createElement('canvas');
      canvas.id = 'fabric-canvas-to-decorate';
      canvas.width = rect.width;
      canvas.height = rect.height;
      elemSize.appendChild(canvas);
    }
    canvasAspect = rect.width / rect.height;
    imgAspectLandscape = this.capturedImage.wallImage.width / this.capturedImage.wallImage.height;
    imgAspectPortrait =  this.capturedImage.wallImage.height / this.capturedImage.wallImage.width;

    if (this.capturedImage.wallImage.width > this.capturedImage.wallImage.height) {

      if (canvasAspect >= imgAspectLandscape) {
          scaleFactor = rect.width / this.capturedImage.wallImage.width;
          left = 0;
          top = -((this.capturedImage.wallImage.height * scaleFactor) - rect.height) / 2;
      } else {
          scaleFactor = rect.height / this.capturedImage.wallImage.height;
          top = 0;
          left = -((this.capturedImage.wallImage.width * scaleFactor) - rect.width) / 2;
      }
    } else {

      if (canvasAspect >= imgAspectPortrait) {
          scaleFactor = rect.height / this.capturedImage.wallImage.height;
          left = -((this.capturedImage.wallImage.width * scaleFactor) - rect.width) / 2;
          top = -((this.capturedImage.wallImage.height * scaleFactor) - rect.height) / 2;
      } else {
          scaleFactor = rect.width / this.capturedImage.wallImage.width;
          top = 0;
          left = -((this.capturedImage.wallImage.width * scaleFactor) - rect.width) / 2;
      }
    }

    var canvas = new fabric.Canvas('fabric-canvas-to-decorate');
    canvas.setBackgroundImage(this.capturedImage.wallImage.src, canvas.renderAll.bind(canvas), {
        backgroundImageOpacity: 1,
        backgroundImageStretch: false,

        top: top,
        left: left,
        originX: 'left',
        originY: 'top',
        scaleX: scaleFactor,
        scaleY: scaleFactor
    });
    this.canvasToDecorate = canvas;
  }

  @action
  addImageTemplateToCanvas(event) {
    let targetElement = document.getElementById(event.target.id);
    var imgInstance = new fabric.Image(targetElement, {
      left: 100,
      top: 100,
      angle: 0,
      opacity: 1
    });
    this.canvasToDecorate.add(imgInstance);
  }

...    
  @action
  setColor() {
    if (!this.canvasToDecorate.getActiveObject()) {
      return;
    }

    var canvas2dBackend = new fabric.Canvas2dFilterBackend()
    fabric.filterBackend = canvas2dBackend;
    fabric.filterBackend = fabric.initFilterBackend();

    var colorButton = document.querySelector('#select-color');
    var obj = this.canvasToDecorate.getActiveObject();

    var filter = new fabric.Image.filters.BlendColor({
      color: colorButton.style.background,
      mode: 'tint',
      alpha: 0.5
    });

    obj.filters[16] = filter;
    obj.applyFilters();
    this.canvasToDecorate.renderAll();
  }

  @action
  selectColor() {
    var parent = document.querySelector('#select-color');
    var picker = new Picker(parent);

    picker.onDone = function(color) {
      parent.style.background = color.rgbaString;
    };
    this.currentColor = parent.style.background;
  }

When I select an image on the Fabric canvas to change the color, when the 'Tint' is applied the image resizes for images supplied from a model. Key section below.

  setColor() {
    if (!this.canvasToDecorate.getActiveObject()) {
      return;
    }

    var canvas2dBackend = new fabric.Canvas2dFilterBackend()
    fabric.filterBackend = canvas2dBackend;
    fabric.filterBackend = fabric.initFilterBackend();

    var colorButton = document.querySelector('#select-color');
    var obj = this.canvasToDecorate.getActiveObject();

    var filter = new fabric.Image.filters.BlendColor({
      color: colorButton.style.background,
      mode: 'tint',
      alpha: 0.5
    });

    obj.filters[16] = filter;
    obj.applyFilters();
    this.canvasToDecorate.renderAll();

The outline of the image doesn't change size but the actual image does except for user supplied images,

  @action
  addUserImageFile(){
    let preview = document.getElementById('imageToUpload');
    let file = document.getElementById('image-file-upload').files[0];
    const reader = new FileReader();

    if (file.size > 5242880 || !file.name.match(/.(jpg|jpeg|png|gif)$/i)) {
        return alert("The file must be an image (jpg|jpeg|png|gif) and less than " + (5242880/1024/1024) + "MB");
    } else {
      reader.addEventListener("load", function () {
        // convert image file to base64 string
        preview.src = reader.result;

      }, false);

      if (file) {
        reader.readAsDataURL(file);
      }
    }
  }

  @action
  async addImageCanvas() {
    let preview = document.getElementById('imageToUpload')
    var imgInstance = new fabric.Image(preview, {
      left: 100,
      top: 100,
      angle: 0,
      opacity: 1
    });
    this.canvasToDecorate.add(imgInstance);
  }

enter image description here enter image description here


Solution

  • Its an image CORs issue, Fabric does have functions to load images ignoring the image CORS, in my case the service holding the fabric canvas had to be assigned the variable canvas for the callback to find it.

      @action
      addImageTemplateToCanvas(event) {
    
        var canvas = <what ever your fabric canvas variable is, my case its a service>;
    
        fabric.util.loadImage(event.target.src,
            function (img) {
               var fab_image = new fabric.Image(img);
               canvas.add(fab_image);
               canvas.renderAll();
            },{left:100, top:100, angle: 0, opacity: 1});
      }