0

I'm trying to make a download button and want the images to be downloadable. However, I'm facing trouble as two of my function toBlob and save functions are being told to be undefined.

What am I doing wrong here? I'm using Vue.

 methods: {
    async downloadImage(val) {
      const blob = await toBlob(src);
      save(blob, 'image.jpg');
    },

    toBlob(src) {
      new Promise((res) => {
        const img = document.createElement("img");
        const c = document.createElement("canvas");
        const ctx = c.getContext("2d");
        img.onload = ({ target }) => {
          c.width = target.naturalWidth;
          c.height = target.naturalHeight;
          ctx.drawImage(target, 0, 0);
          c.toBlob((b) => res(b), "image/jpeg", 0.5);
        };
        img.crossOrigin = "";
        img.src = src;
      });
    
      save = (blob, name = "image.jpg") => {
       const a = document.createElement("a");
       a.href = URL.createObjectURL(blob);
       a.target = "_blank";
       a.download = name;
       a.click();
      };
   }, 
 }
fortunee
  • 3,852
  • 2
  • 17
  • 29
reactRookie
  • 189
  • 1
  • 9

2 Answers2

1

You can't reference fields defined in an object from that object. I.e., you can't do:

let x = {
  y: 0,
  z: y + 1,
};

The simplest alternative is to define the reused (or all) fields outside the object:

let y = 0;
let x = {
  y, 
  z: y + 1
};

OR

let y = 0;
let z = y + 1;
let x = {y, z};

You also have some minor syntax errors. E.g., you can't have {x: 3;}.

For your example, this would look like:

let downloadImage = async val => {
    const blob = await toBlob(src);
    save(blob, 'image.jpg');
};

let toBlob = async src => {
    new Promise((res) => {
        const img = document.createElement("img");
        const c = document.createElement("canvas");
        const ctx = c.getContext("2d");
        img.onload = ({target}) => {
            c.width = target.naturalWidth;
            c.height = target.naturalHeight;
            ctx.drawImage(target, 0, 0);
            c.toBlob((b) => res(b), "image/jpeg", 0.5);
        };
        img.crossOrigin = "";
        img.src = src;
    })
};

let save = (blob, name = "image.jpg") => {
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.target = "_blank";
    a.download = name;
    a.click();
};

let obj = {methods: {downloadImage, toBlob, save}}
junvar
  • 11,151
  • 2
  • 30
  • 46
  • But you can reference the object method/property with the `this` keyword. Add that too – fortunee Mar 24 '21 at 15:38
  • @fortunee, if you mean e.g. `let x = {t: this, t2: () => this}`, then both `x.t` & `x.t2()` will return `window` or the outer scoped object if e.g. definied inside a class, but *not* `x`. – junvar Mar 24 '21 at 15:42
  • No I mean `let x = { y: 0, z: this.y + 1, };` should reference the `this.y` in that object – fortunee Mar 24 '21 at 15:49
  • @fortunee, that's wrong, as in my previous comment, that'll reference `window.y` or whatever the outerscope object is (which isn't always `window`. – junvar Mar 24 '21 at 15:54
  • @juniver that's when you reference it on a property then this is the outer scope, but inside a method like the `downloadImage` then it should reference the `this` of the `method `object. Consider this example https://replit.com/@MrBoggyice/AnchoredFarawayConfigfiles#obj.js – fortunee Mar 24 '21 at 16:10
1
  • To call other methods in the Vue instance you use this.

  • And you should also be returning the Promise from toBlob

methods: {
    async downloadImage(val) {
      const blob = await this.toBlob(src);
      this.save(blob, 'image.jpg');
    },
    toBlob(src) {
      return new Promise((res) => {
        const img = document.createElement("img");
        const c = document.createElement("canvas");
        const ctx = c.getContext("2d");
        img.onload = ({
          target
        }) => {
          c.width = target.naturalWidth;
          c.height = target.naturalHeight;
          ctx.drawImage(target, 0, 0);
          c.toBlob((b) => res(b), "image/jpeg", 0.5);
        };
        img.crossOrigin = "";
        img.src = src;
      });
    }
    save(blob, name = "image.jpg") {
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.target = "_blank";
      a.download = name;
      a.click();
    }
  }
}
Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106