Supplementary Notes for Vue-Advanced-Cropper
Published: 2022-12-16 20:47:00

Introduction:
There are many images cropper packages on NPM platform. I have tried some of them for my projects and found out the most suitable and compatible image processing tool for Vue Cli 3.  It offers the website (https://advanced-cropper.github.io/vue-advanced-cropper/ )  for demonstrations and documentations. 
At the same time, there are some details to take care about. Also this article is beginer friendly for a simple setup. 
Setup
Instead of using  npm install -S vue-advanced-cropper. It requires following command for Vue 3.
_npm install -S vue-advanced-cropper@next_
Principal Concepts:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwusg7cez6vy4kdzfuvq.png)
It is composed of three main parts: boundaries, visibleArea and coordinates. 

Boundaries is the image area inside the cropper section. By default, it is equal to an image fitted to a cropper.

The visibleArea is the part of the image that a user see.  It has same aspect ratio as boundaries. 

Coordinates is the cropped coordinates of an image( left,top,width,height) inside the the visibleArea which is defined by coordinates.
**Minimal Working Example
**
It is necessary to implement and start with the minimal working example.
`<template>
	<cropper
		:src="img"
		@change="change"
	/>
</template>

<script>
import { Cropper } from 'vue-advanced-cropper';
import 'vue-advanced-cropper/dist/style.css';

export default {
	components: {
		Cropper,
	},
	data() {
		return {
			img: 'https://images.pexels.com/photos/4323307/pexels-photo-4323307.jpeg',
		};
	},
	methods: {
		change({ coordinates, canvas }) {
			console.log(coordinates, canvas);
		},
	},
};
</script>`
To achieve the result of static cropper, you only need to alternate the <cropper /> section in the minimal working example with the following code.
`<cropper
	:src="img"
	:stencil-props="{
		handlers: {},
		movable: false,
		scalable: false,
		aspectRatio: 1,
	}"
	:resize-image="{
		adjustStencil: false
	}"
	image-restriction="stencil"
/>`
For the fixed stencil size, change the <cropper/> with  
`<cropper
	:src="img"
	:stencil-props="{
		handlers: {},
		movable: false,
		scalable: false,
	}"
	:stencil-size="{
		width: 280,
		height: 280
	}"
	image-restriction="stencil"
/>`
If you need to move and resize its stencil, classic hybrid method is recommended.
`<cropper
	:src="img"
	:auto-zoom="true"
 />`
There are only two default stencil, RectangleStencil, which shows above, and  CircleStencil. I created a simple circleStencil example based from the instruction. 
`<template>
<cropper
src="https://images.pexels.com/photos/1254140/pexels-photo-1254140.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
:stencil-component="$options.components.CircleStencil"
/>
</template>

<script>
import { CircleStencil, Cropper } from 'vue-advanced-cropper';
import 'vue-advanced-cropper/dist/style.css';

export default {
components: {
Cropper,CircleStencil
},
data() {
return {
img: 'https://images.pexels.com/photos/4323307/pexels-photo-4323307.jpeg',
};
},
methods: {
change({ coordinates, canvas }) {
console.log(coordinates, canvas);
},
},
};
</script>`
Here is the example with circle edition of cropper with upload ability. 
`<template>
<div class="upload-example">
<cropper
ref="cropper"
class="upload-example-cropper"
:src="image.src"
:stencil-component="$options.components.CircleStencil"
/>
<div class="button-wrapper">
<button class="button" @click="$refs.file.click()">
<input type="file" ref="file" @change="loadImage($event)" accept="image/*">
Load image
</button>
</div>
</div>

</template>

<script>
import { CircleStencil, Cropper } from 'vue-advanced-cropper';
import 'vue-advanced-cropper/dist/style.css';

// This function is used to detect the actual image type, 
function getMimeType(file, fallback = null) {
const byteArray = (new Uint8Array(file)).subarray(0, 4);
let header = '';
for (let i = 0; i < byteArray.length; i++) {
header += byteArray[i].toString(16);
}
switch (header) {
case "89504e47":
return "image/png";
case "47494638":
return "image/gif";
case "ffd8ffe0":
case "ffd8ffe1":
case "ffd8ffe2":
case "ffd8ffe3":
case "ffd8ffe8":
return "image/jpeg";
default:
return fallback;
}
}

export default {
components: {
Cropper,
CircleStencil
},
data() {
return {
image: {
src: null,
type: null
}
};
},
methods: {
crop() {
const { canvas } = this.$refs.cropper.getResult();
canvas.toBlob((blob) => {
// Do something with blob: upload to a server, download and etc.
}, this.image.type);
},
reset() {
this.image = {
src: null,
type: null
}
},
loadImage(event) {
// Reference to the DOM input element
const { files } = event.target;
// Ensure that you have a file before attempting to read it
if (files && files[0]) {
// 1. Revoke the object URL, to allow the garbage collector to destroy the uploaded before file
if (this.image.src) {
URL.revokeObjectURL(this.image.src)
}
// 2. Create the blob link to the file to optimize performance:
const blob = URL.createObjectURL(files[0]);
// 3. The steps below are designated to determine a file mime type to use it during the 
// getting of a cropped image from the canvas. You can replace it them by the following string, 
// but the type will be derived from the extension and it can lead to an incorrect result:
//
// this.image = {
// src: blob;
// type: files[0].type
// }
// Create a new FileReader to read this image binary data
const reader = new FileReader();
// Define a callback function to run, when FileReader finishes its job
reader.onload = (e) => {
// Note: arrow function used here, so that "this.image" refers to the image of Vue component
this.image = {
// Set the image source (it will look like blob:http://example.com/2c5270a5-18b5-406e-a4fb-07427f5e7b94)
src: blob,
// Determine the image type to preserve it during the extracting the image from canvas:
type: getMimeType(e.target.result, files[0].type),
};
};
// Start the reader job - read file as a data url (base64 format)
reader.readAsArrayBuffer(files[0]);
}
},
},
destroyed() {
// Revoke the object URL, to allow the garbage collector to destroy the uploaded before file
if (this.image.src) {
URL.revokeObjectURL(this.image.src)
}
}
};
</script>`
Remaind: toBlob is very important because that toBlob() will only do the first bullet synchronously, but will do the conversion to image format in a non blocking manner