Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

collaborative canvas with angular and firestore

I am trying to create a real time collaborative canvas on ionic/angular+fabric.js+firestore. I have the canvas, I serialize it to json, store it (and update it from different users) but I do not get to refresh every user's canvases. How can I accomplish this?

First of all, I get connected to the collection:

this.imageCollection = database.collection<MyImage>("images");
this.drawing = this.imageCollection.doc(this.chatId).valueChanges();
this.writeCanvas();

Then, I have this function that is called on every "mouse up":

loadOnCanvas(){
    var json = JSON.stringify(this.f_canvas);
    this.us.addCanvastoDB(json, this.chatId);
    this. writeCanvas();
   }

And the following function is where I am trying to write to the shared canvas (that is not actually shared) because it doesn't get updated with different users' drawings. This function is called at the begining of the class (ngOnInit) and after each loadOnCanvas (see above):

writeCanvas(){
    let that = this;
    this.drawing.pipe(take(1)).subscribe(data=>{
      that.read_json = data[0]['canvas'];
      if(that.read_json){
        this.f_canvas.clear();
        console.log("data loaded", that.read_json);
      that.f_canvas.loadFromJSON(that.read_json, that.f_canvas.renderAll.bind(that.f_canvas));
      }
    });

The data from the firestore is saved in the database when I start drawing but if another user (or same user after having left the app) enters again an empty canvas is displayed and the data from the database is not loaded.

This is the function that stores the data in the database. It is in a service:

addCanvastoDB(canvas: any, chatId:string) {
    //Create an ID for document
    const id = this.database.createId();
    console.log("chat id: ", chatId);
    //Set document id with value in database
    this.imageCollection
      .doc(chatId)
      .set({canvas: canvas, chatid: chatId},
        { merge: true })
      .then(resp => {
        console.log("resp", resp);
      })
      .catch(error => {
        console.log("error " + error);
      });
  }

And this is all the relevant code from my component:

    export class DrawComponent implements OnInit {
  @Input() chatId: string;
  @Input() typeOfImg: string;
  @Input() userId: string;

  @Output() passEntry: EventEmitter<any> = new EventEmitter();

 @ViewChild('my_canvas', {static: false}) my_canvas: ElementRef;

  images: Observable<MyImage[]>;
  private imageCollection: AngularFirestoreCollection<MyImage>;

  private f_canvas:any;
  private read_json: any;
  drawing: any;

  constructor(
     public popoverCtrl: PopoverController,
    public cs: ChatService,
    public us: UploadService,
    private modalController: ModalController,
    private database: AngularFirestore,
    public events: Events,

  ) {
    this.availableColours = ["#dd0000", "#000000", "#00dd00", "#ffdd00"];
    //Set collection where our documents/ images info will save
    this.imageCollection = database.collection("images");  
  }

  ngOnInit() {
    this.f_canvas = new fabric.Canvas('my_canvas', {selection: true});
    this.f_canvas.setHeight(window.innerHeight);
    this.f_canvas.setWidth(window.innerWidth);
    this.f_canvas.selection = true;
    this.currentColour = '#000000';
    this.f_canvas.freeDrawingBrush.width = 3; // size of the drawing brush
    this.tool_selected = "selection_tool";
    this.background_visible = false;
    this.load_background();
    let that = this;
    this.f_canvas.on("mouse:down", function(o) {
      that.mousedown(o);
    });
    this.f_canvas.on("mouse:move", function(e) {
      that.mousemove(e);
    });
    this.f_canvas.on("mouse:up", function(e) {
      that.mouseup(e);
    });
    console.log("chatid en draw", this.chatId);
    this.drawing = this.imageCollection.doc(this.chatId).valueChanges();
  }
  ionViewDidLoad() {
    this. writeCanvas();
  }
  ngAfterViewInit(){

  }
  //save canvas in firestore
  loadOnCanvas(){
    var json = JSON.stringify(this.f_canvas);
    console.log("chat id sent to store canvas");
    this.us.addCanvastoDB(json, this.chatId);
    this. writeCanvas();
   }
//draw the canvas with the data from firestore  
   writeCanvas(){
    let that = this;
    console.log("writing canvas");
      this.drawing.pipe(tap(data=>{
        console.log(data);
        if(data[0]['canvas']){
          that.read_json = data[0]['canvas'];
          if(that.read_json){
            this.f_canvas.clear();
            console.log("data loaded", that.read_json);
          that.f_canvas.loadFromJSON(that.read_json, that.f_canvas.renderAll.bind(that.f_canvas));
          }
        }
      }));
   }

Any ideas would be appreciated. Thanks a lot in advance!!!

like image 359
marianovelamazan Avatar asked May 16 '26 15:05

marianovelamazan


1 Answers

I do not know exactly why but I got this working when I changed the way I was reading from the database. Instead of using tap I am using take(1). The new block is like this:

this.drawing.pipe(take(1)).subscribe(data=>{
  //OLD WAY: this.drawing.pipe(tap(data=>{

    if(data.canvas){
      that.read_json = data.canvas;
      if(that.read_json){
        this.f_canvas.clear();
      that.f_canvas.loadFromJSON(that.read_json, that.f_canvas.renderAll.bind(that.f_canvas));
      }
    }
  });

Thanks a lot for the help anyway!

like image 192
marianovelamazan Avatar answered May 19 '26 04:05

marianovelamazan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!