DEV Community

Yuqing Ma
Yuqing Ma

Posted on

Supplementary Notes for Vue-Advanced-Cropper

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
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.
`
:src="img"
@change="change"
/>

<br> import { Cropper } from &#39;vue-advanced-cropper&#39;;<br> import &#39;vue-advanced-cropper/dist/style.css&#39;;</p> <p>export default {<br> components: {<br> Cropper,<br> },<br> data() {<br> return {<br> img: &#39;<a href="https://images.pexels.com/photos/4323307/pexels-photo-4323307.jpeg">https://images.pexels.com/photos/4323307/pexels-photo-4323307.jpeg</a>&#39;,<br> };<br> },<br> methods: {<br> change({ coordinates, canvas }) {<br> console.log(coordinates, canvas);<br> },<br> },<br> };<br>
To achieve the result of static cropper, you only need to alternate the <cropper /> section in the minimal working example with the following code.
: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
: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.
: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.

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"
/>

<br> import { CircleStencil, Cropper } from &#39;vue-advanced-cropper&#39;;<br> import &#39;vue-advanced-cropper/dist/style.css&#39;;</p> <p>export default {<br> components: {<br> Cropper,CircleStencil<br> },<br> data() {<br> return {<br> img: &#39;<a href="https://images.pexels.com/photos/4323307/pexels-photo-4323307.jpeg">https://images.pexels.com/photos/4323307/pexels-photo-4323307.jpeg</a>&#39;,<br> };<br> },<br> methods: {<br> change({ coordinates, canvas }) {<br> console.log(coordinates, canvas);<br> },<br> },<br> };<br>
Here is the example with circle edition of cropper with upload ability.

Load image

<br> import { CircleStencil, Cropper } from &#39;vue-advanced-cropper&#39;;<br> import &#39;vue-advanced-cropper/dist/style.css&#39;;</p> <p>// This function is used to detect the actual image type, <br> function getMimeType(file, fallback = null) {<br> const byteArray = (new Uint8Array(file)).subarray(0, 4);<br> let header = &#39;&#39;;<br> for (let i = 0; i &lt; byteArray.length; i++) {<br> header += byteArray[i].toString(16);<br> }<br> switch (header) {<br> case &quot;89504e47&quot;:<br> return &quot;image/png&quot;;<br> case &quot;47494638&quot;:<br> return &quot;image/gif&quot;;<br> case &quot;ffd8ffe0&quot;:<br> case &quot;ffd8ffe1&quot;:<br> case &quot;ffd8ffe2&quot;:<br> case &quot;ffd8ffe3&quot;:<br> case &quot;ffd8ffe8&quot;:<br> return &quot;image/jpeg&quot;;<br> default:<br> return fallback;<br> }<br> }</p> <p>export default {<br> components: {<br> Cropper,<br> CircleStencil<br> },<br> data() {<br> return {<br> image: {<br> src: null,<br> type: null<br> }<br> };<br> },<br> methods: {<br> crop() {<br> const { canvas } = this.$refs.cropper.getResult();<br> canvas.toBlob((blob) =&gt; {<br> // Do something with blob: upload to a server, download and etc.<br> }, this.image.type);<br> },<br> reset() {<br> this.image = {<br> src: null,<br> type: null<br> }<br> },<br> loadImage(event) {<br> // Reference to the DOM input element<br> const { files } = event.target;<br> // Ensure that you have a file before attempting to read it<br> if (files &amp;&amp; files[0]) {<br> // 1. Revoke the object URL, to allow the garbage collector to destroy the uploaded before file<br> if (this.image.src) {<br> URL.revokeObjectURL(this.image.src)<br> }<br> // 2. Create the blob link to the file to optimize performance:<br> const blob = URL.createObjectURL(files[0]);<br> // 3. The steps below are designated to determine a file mime type to use it during the <br> // getting of a cropped image from the canvas. You can replace it them by the following string, <br> // but the type will be derived from the extension and it can lead to an incorrect result:<br> //<br> // this.image = {<br> // src: blob;<br> // type: files[0].type<br> // }<br> // Create a new FileReader to read this image binary data<br> const reader = new FileReader();<br> // Define a callback function to run, when FileReader finishes its job<br> reader.onload = (e) =&gt; {<br> // Note: arrow function used here, so that &quot;this.image&quot; refers to the image of Vue component<br> this.image = {<br> // Set the image source (it will look like blob:<a href="http://example.com/2c5270a5-18b5-406e-a4fb-07427f5e7b94">http://example.com/2c5270a5-18b5-406e-a4fb-07427f5e7b94</a>)<br> src: blob,<br> // Determine the image type to preserve it during the extracting the image from canvas:<br> type: getMimeType(e.target.result, files[0].type),<br> };<br> };<br> // Start the reader job - read file as a data url (base64 format)<br> reader.readAsArrayBuffer(files[0]);<br> }<br> },<br> },<br> destroyed() {<br> // Revoke the object URL, to allow the garbage collector to destroy the uploaded before file<br> if (this.image.src) {<br> URL.revokeObjectURL(this.image.src)<br> }<br> }<br> };<br> `
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

Top comments (0)