how can I save a croppie-cropped image, the same way as I save an image-file?

Published

In a vue / laravel application I have a window where user can edit the profile.

I want to replace the <input type="file"…> tag with a vue component that crops the image.

The current, working, script is:

<template>
<div>
    <h2>Cards 1</h2>
    
        <div class="form-group"   > 
            <input type="text" class="form-control" :placeholder="card.title" v-model="card.title">
        </div>

        <div class="form-group">
            <textarea  class="form-control" :placeholder="card.description" v-model="card.description">
            </textarea>
        </div>
 
        <img class="img-circle" style="width:150px"  v-if="card.picture" v-bind:src="card.picture" alt="Card Image">
      

        <input type="file" v-on:change="onFileChange" ref="fileUpload" id="file_picture_input">

        
        <button @click="editCard(currentSpelerCard)" class="btn btn-warning m-1" style="width:100px;color:black">  Save Card  </button>



</div>
</template>

<script>
import { mapState, mapActions } from 'vuex';

export default {
    mounted(){ 
        this.showSpelerCards();
    },
    computed: {
        ...mapState([
        'currentSpeler' ,'currentSpelerCard', 'currentGame'
        ]), 
    },
    data() {
        return{
            cards:[],
            card:{
                id:'',
                title:'Titel',
                description:'Beschrijving',
                picture:'',
            },
            url:'',
            edit:false,
            selectedFile:'',
            cardExists:false,
            successMessage:'',
            errorMessage:'',

            croppieImage: '',
            cropped: null,
        }

    },

    methods: {

        ...mapActions([  'getGames', 'addGame', 'fetchSpelerCards'  ]),

        showCard(){
            if (!this.cardExists) {
                this.addCard();
            }       
        },

        showSpelerCards(){
            console.log('speler= '+ this.currentSpeler.speler.id);
                this.$store.dispatch('fetchSpelerCards', this.currentSpeler.speler.id )
                .then(res => { 
                    this.cardExists = true;
                    this.card = this.currentSpelerCard;
                    this.successMessage = res;
                    console.log(res);
                })
                .catch(err => {
                    this.errorMessage = err;
                    this.addCard();
                });
            
        },

      
        addCard(){
            // de informatie voor de card staat inthis.$card, nu omzetten naar FormData om file toe te voegen aan de request zodat api die kan opslaan, api gaat ook naam bedenken voor de card.
            //  via fetchCards() komt die naam dadelijk terug in card.picture
            const formData = new FormData();
            formData.append('title',this.card.title);
            formData.append('description',this.card.description);
            formData.append('imagefile',this.selectedFile);
            formData.append('speler_id', this.currentSpeler.speler.id);
            formData.append('game_id', this.currentGame.id);
 
            if(this.edit === false){  // eerste keer dat kaart wordt getoond is edit ===false, als er dan geen kaart is, wordt er een toegevoegd
                //add
                fetch(`api/cards`, {
                    method: 'post',
                    body:formData,
                    headers:{
                    }
                })
                .then(res => console.log(res))
                .then(data => {
                    this.card.title='';
                    this.card.description='';
                    this.card.picture='';
                    this.$refs.fileUpload.value=null;
                    this.url='';
                    this.showSpelerCards();
                })
                .catch(err => console.log(err));
            } else{   // als je hier belandt via editCard functie, dan is de editCard ===true, en kom je hier terecht
                //update
                fetch(`api/cards/${this.card.id}?_method=PUT`, {
                    method: 'post',
                    body:formData,
                    headers:{

                    }
                })

                .then(res => console.log(res))
                .then(data => {
                    this.card.title='';
                    this.card.description='';
                    this.url='';
                    this.$refs.fileUpload.value=null;
                    this.showSpelerCards();
                })
                .catch(err => console.log(err));
            }
        },
        editCard(card){
            this.edit=true;
            this.card.id=card.id;
            this.card.title=card.title;
            this.card.description=card.description;
            this.addCard();
        },

         onFileChange(e){ 
                
                this.selectedFile = e.target.files[0];
                console.log(this.selectedFile);
                console.log(URL.createObjectURL(this.selectedFile));
                
                if (this.selectedFile.size > 2048 * 1024) {
                    this.$refs.fileUpload.value=null;
                    e.preventDefault();
                    alert('File too big (> 1MB)');
                    return;
                }
                this.url = URL.createObjectURL(this.selectedFile);               
            },
        },
}
</script>

Now I want to add an component shown below.

<template>
    <div class="Image-Upload-wrapper Image-upload"> 
        <p> 
            This is image upload wrapper component
        </p>

        <div id="croppie"> </div>     
        <div class="upload-wrapper">
            <button class="btn btn-primary btn-sm" v-on:click="setImage">
                 Set image   
            </button>
        </div>   

    </div>
</template>



<script>
//  uitleg over Croppie::https://www.youtube.com/watch?v=kvNozA8M1HM
import Croppie from 'croppie';

export default {
    props:[
        'imgUrl'
    ],

    mounted(){
        this.image = this.imgUrl   // als component is mounted, this.image definieren, en daartmee croppie in..
        this.setUpCroppie()
    },
    data(){
        return{
           image:null,
           croppie:null,
        }
    },
    methods:{
        setUpCroppie(){
           let el = document.getElementById('croppie');
           this.croppie = new Croppie(el, {
               viewport:{width:200,height:200,type:'circle'},
               boundary:{ width:220,height:220},
               showZoomer:true,
               enableOrientation: true
           });
           this.croppie.bind({       
               url:this.image
           });
        },
        setImage(){
            this.croppie.result({
                type:'canvas',
                size:'viewport'
            }).then(response =>{
               console.log(response);  // works fine, console shows the raw imagedata
                this.$emit('updateImage', response);
            })
        }
    },
  
}
</script>

I have it implemented in such a way that the component emits the cropped image back to the parent,
which looks slightly different now (note the $$$ at the place of added component):


<template>
<div>
    <h2>Cards 1</h2>
    
        <div class="form-group"   > 
            <input type="text" class="form-control" :placeholder="card.title" v-model="card.title">
        </div>

        <div class="form-group">
            <textarea  class="form-control" :placeholder="card.description" v-model="card.description">
            </textarea>
        </div>
 
        <img class="img-circle" style="width:150px"  v-if="card.picture" v-bind:src="card.picture" alt="Card Image">
        <!-- <button @click="deleteCard(currentSpelerCard.id)" class="btn btn-danger m-1" style="width:100px;color:white">  Delete  </button> -->
      
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

        <div id="preview" v-if="url" >
            <h4> Preview</h4>
            <ImageUpload  :imgUrl="url"  @updateImage="onFileChange"></ImageUpload> 
        </div>
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
        <input type="file" v-on:change="onFileChange" ref="fileUpload" id="file_picture_input">

        
        <button @click="editCard(currentSpelerCard)" class="btn btn-warning m-1" style="width:100px;color:black">  Save Card  </button>


<!--    <p class="alert alert-danger" v-if="errorMessage !== ''"> {{errorMessage}} </p>
        <p class="alert alert-success" v-if="successMessage !== ''"> {{successMessage}} </p> -->

    
</div>
</template>

<script>
import { mapState, mapActions } from 'vuex';

export default {
    mounted(){
        console.log('component mounted');  
        this.showSpelerCards();
    },
    computed: {
        ...mapState([
        'currentSpeler' ,'currentSpelerCard', 'currentGame'
        ]), 
    },
    data() {
        return{
            cards:[],
            card:{
                id:'',
                title:'Titel',
                description:'Beschrijving',
                picture:'',
            },
            url:'',
            //card_id:'',
            edit:false,
            selectedFile:'',
            cardExists:false,
            successMessage:'',
            errorMessage:'',

            croppieImage: '',
            cropped: null,
        }

    },
    created(){
    
    },
    methods: {

        ...mapActions([  'getGames', 'addGame', 'fetchSpelerCards'  ]),

        showCard(){
            if (!this.cardExists) {
                this.addCard();
            }
            
        },

        showSpelerCards(){
            console.log('speler= '+ this.currentSpeler.speler.id);
                this.$store.dispatch('fetchSpelerCards', this.currentSpeler.speler.id )
                .then(res => { 
                    this.cardExists = true;
                    this.card = this.currentSpelerCard;
                    this.successMessage = res;
                    console.log(res);
                })
                .catch(err => {
                    this.errorMessage = err;
                   // this.cardExists = false;
                    this.addCard();
                });
            
        },

      
        addCard(){
            // de informatie voor de card staat inthis.$card, nu omzetten naar FormData om file toe te voegen aan de request zodat api die kan opslaan, api gaat ook naam bedenken voor de card.
            //  via fetchCards() komt die naam dadelijk terug in card.picture
            const formData = new FormData();
            formData.append('title',this.card.title);
            formData.append('description',this.card.description);
            formData.append('imagefile',this.selectedFile);
            formData.append('speler_id', this.currentSpeler.speler.id);
            formData.append('game_id', this.currentGame.id);

           
            if(this.edit === false){  // eerste keer dat kaart wordt getoond is edit ===false, als er dan geen kaart is, wordt er een toegevoegd
                //add
                fetch(`api/cards`, {
                    method: 'post',
                    body:formData,
                    headers:{
                    }
                })
                .then(res => console.log(res))
                .then(data => {
                    this.card.title='';
                    this.card.description='';
                    this.card.picture='';
                    this.$refs.fileUpload.value=null;
                    this.url='';
                    this.showSpelerCards();
                })
                .catch(err => console.log(err));
            } else{   // als je hier belandt via editCard functie, dan is de editCard ===true, en kom je hier terecht
                //update
                fetch(`api/cards/${this.card.id}?_method=PUT`, {
                    method: 'post',
                    body:formData,
                    headers:{
                    }
                })
                .then(res => console.log(res))
                .then(data => {
                    this.card.title='';
                    this.card.description='';
                   this.url='';
                    this.$refs.fileUpload.value=null;
                    this.showSpelerCards();
                })
                .catch(err => console.log(err));
            }
        },
        editCard(card){
            this.edit=true;
            this.card.id=card.id;
            this.card.title=card.title;
            this.card.description=card.description;
            this.addCard();
        },

         onFileChange(e){ 
                
            this.selectedFile = e.target.files[0];
            console.log(this.selectedFile);
            console.log(URL.createObjectURL(this.selectedFile));
            
            if (this.selectedFile.size > 2048 * 1024) {
                this.$refs.fileUpload.value=null;
                e.preventDefault();
                alert('File too big (> 1MB)');
                return;
            }
            this.url = URL.createObjectURL(this.selectedFile);
            
        },
}
</script>

The raw image data is written to the console, as is programmed in the onFileChnage function.

I just cannot get it processedd the way I succesfully process the uploaded files.

Does anyone jave more experience with the croppie formats, and ways to extract the image file?

I tried

this.url = e.blob

without success..

Source: Laravel

Published
Categorised as croppie, laravel, vue.js Tagged , ,

Answers

Leave a Reply

Still Have Questions?


Our dedicated development team is here for you!

We can help you find answers to your question for as low as 5$.

Contact Us
faq