Search code examples
javascriptvue.jscropperjs

Cannot get the final cropped version with cropperjs in my main vue component


In my laravel-vue application I have created a ImageCropper component using cropperjs. And then in my parent vue component, I am dynamically creating the ImageCropper component upon change event of file uploading of user. I am sending the image url to the child component as props from the master component. The ImageCropper is being created perfectly and the cropping mechanism works perfectly too. Only issue is I can only track the cropped image and the changes on that in the ImageCropper component, but can't track or update the data in my master component, from which I need to submit the data in Database. My code is below:

This is the ImageCropper component:

<template>
    <div class="card">
        <img ref="image" :src="imageSource" style="width: 100%;">
        <img ref="destinationImage" :src="destination" class="img-fluid">

    </div>
</template>
<script>
    import Cropper from 'cropperjs';
    export default {
        name: 'ImageCropper',
        props: {
            imageSource: String,
        },
        data() {
            return {
                cropper: {},
                destination: {},
                image: {},
            }
        },
        mounted() {
            this.image = this.$refs.image;

            this.cropper = new Cropper(this.image, {
                zoomable: false,
                scalable: false,
                aspectRatio: 16 / 9,
                viewMode: 1,
                background: false,
                crop: () => {
                    const canvas = this.cropper.getCroppedCanvas();
                    this.destination = canvas.toDataURL("image/png");

                },

            });

        },




    }

</script>

From this component I can track the final image and the updates of that by 'this.destination'. This is my Master Component:

<template>
    <div>
        <div>
            <input @change="changePhoto($event)" name="photo" type="file">
        </div>
        <div ref="imageCropperArea"></div>
    </div>
</template>
<script>
    import ImageCropper from './../../ImageCropper.vue';
    export default {
        name: "New",
        data() {
            return {
                temp_photo: '',
                final_photo: '',

            }
        },
        components: {
            ImageCropper
        },
        methods: {
            createImageCropping(event) {
                var ComponentClass = Vue.extend(ImageCropper)
                var instance = new ComponentClass({
                    propsData: {
                        imageSource: this.form.temp_photo
                    }
                })
                instance.$mount()
                this.$refs.imageCropperArea.appendChild(instance.$el)

            },
            changePhoto(event) {
                let file = event.target.files[0];


                let reader = new FileReader();
                reader.onload = event => {
                    this.form.temp_photo = event.target.result
                    this.createImageCropping(event)

                };
                reader.readAsDataURL(file);


            },
        }

    }

</script>

All I need is to update my 'final_photo' variable as the cropping goes on. I was looking into vuex to pass the data using vue store when it changes, but as I am new in using vuex, couldn't configure vuex in the ImageCropper component. I could access the store.js from the master component but not from the ImageComponent. Also wasn't sure if that was the right path to achieve my goal here.


Solution

  • You can emit the event to send any data back to parent component. See Custom Events for more information.

    In your case, you can do something like:

    ...
      mounted () {
        ...
        this.cropper = new Cropper(this.image, {
          ...
          crop: () => {
            const canvas = this.cropper.getCroppedCanvas()
            this.$emit('crop', canvas.toDataURL('image/png'))
          }
        })
      }
    ...
    

    And

    ...
      createImageCropping (event) {
        ...
        instance.$on('crop', dataUrl => {
          // now you can dispatch an action to update state of store
        })
        ...
      }
    ...
    

    I'm not sure why you are create the ImageCropper instance like that but mostly you have no need to do that. You can just use the component or dynamic component. See more about Dynamic & Async Components.

    So you can do something like:

    <template>
      <div>
        ...
        <image-cropper
          v-if='!!imageSource'
          :image-source="imageSource"
          @crop="dispatchSomeAction"/>
      </div>
    </template>
    

    And in your changePhoto method after your reader finish just set imageSource:

    ...
      changePhoto (event) {
        const file = event.target.files[0]
        const reader = new FileReader()
        reader.onload = event => {
          this.imageSource = event.target.result
        }
        reader.readAsDataURL(file);
      }
    ...