0

Is it possible to manipulate images inside of vuejs using vanilla js? I've successfully been able to upload the pic and send it directly to my s3 bucket using vue but I can't for the life of me figure out how to transform an image WITHIN vuejs and then display it...document.querySelector does not work...I've been trying for days but I can't get the images to resize inside of vue...it only works as regular vanilla js but I can't transfer it to vue. Below is my attempt...I'm desperate...any help is greatly appreciated!

HTML CODE

<div id="imageAPP">
 <div v-if="!image">
    <h2>Select an image</h2> 
    <input type="file" id="upload" @change="imageFileChange">
 </div>

 <div v-else>
    <br>
    <br>
    {{fileName}}
    <br>
    <br>
    <img :src="image" ref="output"/>
    <button v-if="!uploadIMAGE" @click="removeImage">Remove image</button>
    <button v-if="!uploadIMAGE" @click="uploadImage">Upload image</button>
 </div>

 <br>  
 <br>
 <h2 v-if="uploadIMAGE">Success! Image uploaded to bucket.</h2

Vue Code

new Vue({
          el: "#imageAPP", 
          data: {
            image: '',
            uploadIMAGE: ''
          },
          methods: {
            imageFileChange (e) {
                let files = e.target.files || e.dataTransfer.files      
                if (!files.length) return
                this.fileName = files[0].name;
                this.createImage(files[0]) 
            },
            createImage (file) {
                let reader = new FileReader()                  
                reader.readAsDataURL(file)                  
                reader.onload = (e) => {     
                  this.image = e.target.result;  
                }
             },

                reader.onload = function(event){
                  this.image = event.target.result;
                  const imgElement = document.createElement("img");
                  imgElement.src = this.image;

                  document.querySelector("#input").src = event.target.result;

                  imgElement.onload = function(e){
                    const canvas = document.createElement("canvas");
                    const MAX_WIDTH = 260;
                    const MAX_HEIGHT = 194;

                    canvas.width = MAX_WIDTH;
                    canvas.height = MAX_HEIGHT;

                    const ctx = canvas.getContext("2d");
                    ctx.drawImage(e.target, 0, 0, canvas.width, canvas.height);
                    const srcEndcoded = ctx.canvas.toDataURL(e.target, "image/jpeg");}
                    console.log(e.target);
                    document.querySelector("#output2").src = srcEndcoded2;
            },

            
            removeImage: function (e) {
                console.log('Remove clicked')
                this.image = ''
            },

            
            uploadImage: async function (e) {
                console.log('Upload clicked')
                const response = await axios({
                  method: 'GET',
                  url: API_ENDPOINT_IMAGE
                })
                console.log('Response: ', response.data)
                console.log('Uploading: ', this.image)
                let binary = atob(this.image.split(',')[1])
                let array = []
                for (var i = 0; i < binary.length; i++) {
                  array.push(binary.charCodeAt(i))
                }
                let blobData = new Blob([new Uint8Array(array)], {type: 'image/jpeg'})
                console.log('Uploading to: ', response.data.uploadIMAGE)
                const result = await fetch(response.data.uploadIMAGE, {
                  method: 'PUT',
                  body: blobData
                })
                console.log('Result: ', result);
                this.uploadIMAGE = response.data.uploadIMAGE.split('?')[0];
            }

            
          }
      })
      

1 Answers1

1

You were almost there, I made some change to your createImage method (and as a bonus added a way to keep the aspect ratio of the resized image).

new Vue({
      el: "#imageAPP", 
      data: {
        image: '',
        uploadIMAGE: ''
      },
      methods: {
        imageFileChange (e) {
            let files = e.target.files || e.dataTransfer.files      
            if (!files.length) return
            this.fileName = files[0].name;
            this.createImage(files[0]) 
        },     
        async createImage (file) { // <= this must be asynchronous
            const reader = new FileReader();

            // Wait for the data url to be loaded from the file
            const dataURL = await new Promise(resolve => {
                reader.onload = (e) => resolve(e.target.result);
                reader.readAsDataURL(file);
            });

            // Wait for the image to be loaded
            const img = new Image();
            await new Promise(resolve => {
                img.onload = resolve;
                img.src = dataURL;
            });

            // Resize the image with a canvas
            const canvas = document.createElement("canvas");
            const ctx = canvas.getContext("2d");
            
            // This is an addition to keep the aspect ratio of the image so it won't get distorted
            // See : https://stackoverflow.com/q/3971841/12594730
            const [maxWidth, maxHeight] = [260, 194];
            const [imgWidth, imgHeight] = [
                img.naturalWidth,
                img.naturalHeight
            ];
            const ratio = Math.min(maxWidth / imgWidth, maxHeight / imgHeight);

            canvas.width = imgWidth * ratio;
            canvas.height = imgHeight * ratio;

            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            this.image = canvas.toDataURL('image/jpeg', 0.9);
    
            // Display your resized image
            document.querySelector("#output2").src = this.image;
        },
        removeImage() {
            console.log('Remove clicked')
            this.image = '';
        },
        async uploadImage(e) {
            console.log('Upload clicked')
            const response = await axios({
              method: 'GET',
              url: API_ENDPOINT_IMAGE
            })
            console.log('Response: ', response.data)
            console.log('Uploading: ', this.image)
            let binary = atob(this.image.split(',')[1])
            let array = []
            for (var i = 0; i < binary.length; i++) {
              array.push(binary.charCodeAt(i))
            }
            let blobData = new Blob([new Uint8Array(array)], {type: 'image/jpeg'})
            console.log('Uploading to: ', response.data.uploadIMAGE)
            const result = await fetch(response.data.uploadIMAGE, {
              method: 'PUT',
              body: blobData
            })
            console.log('Result: ', result);
            this.uploadIMAGE = response.data.uploadIMAGE.split('?')[0];
        }
    }
})
Fennec
  • 1,535
  • 11
  • 26
  • This was beyond fantastic!! I actually need for the images to be that exact size but that was an easy manipulation!! You saved me!! Now I have to figure out how to take user input data to send to lambda to save as the filename! –  Jan 27 '21 at 15:17