Edit Images With Vue And Pintura
In this quick tutorial we’ll build a Vue image editor using Pintura a powerful JavaScript image and video editor SDK.
We could build a complete image editor ourselves but that would literally take months, it would turn this quick tutorial into a not-so-quick tutorial.
To get started we’ll set up a basic Vue Composition API based template and then add Pintura.
<script setup>
// Coming up next...
</script>
<template>
<!-- File selection -->
<input type="file" />
<!-- Editor output -->
<img src="" alt="" />
</template>
While this tutorial is based on the Vue 3 Composition API, Pintura works just as well with Vue 2 or the Vue 3 template API.
Receiving User Selected Files
Let’s first write the logic get the selected image file from the file input.
We’ll need to get the files, select the first file, and then make sure it’s an image.
<script setup>
function handleFile(e) {
// Get files from the file input
const { files } = e.target;
// Only need the first file
const [file] = files;
// We're only interested in images, also, if no file selected we can't continue
if (!file || !/image/.test(file.type)) return;
// We're ready to edit an image...
}
</script>
<template>
<input type="file" @change="handleFile" />
<img src="" alt="" />
</template>
Editing Images With Vue
Now that we’re sure we’ve selected an image file we can add Pintura and edit the image.
We’ll import the openDefaultEditor
factory, this creates and opens a feature complete image editor instance in a modal.
<script setup>
// Import the default Pintura modal editor
import { openDefaultEditor } from '@pqina/pintura';
function handleFile(e) {
const { files } = e.target;
const [file] = files;
if (!file || !/image/.test(file.type)) return;
// Open Pintura in a modal
const editor = openDefaultEditor({
src: file,
});
// Fires when we're done editing the image
editor.on('process', ({ dest }) => {
// Next up is showing the resulting image...
});
}
</script>
<template>
<input type="file" @change="handleFile" />
<img src="" alt="" />
</template>
Showing The Output Image
When we’re done editing an image the resulting image is returned in the callback assigned to the 'process'
event. The dest
property contains the output image file.
By defining a reactive
property we can set the src
attribute of the image by updating the src
property of the outputImage
object.
<script setup>
import { openDefaultEditor } from '@pqina/pintura';
// The Vue reactive property helper
import { reactive } from 'vue';
// The source of the image we want to display
const outputImage = reactive({ src: '' });
function handleFile(e) {
const { files } = e.target;
const [file] = files;
if (!file || !/image/.test(file.type)) return;
const editor = openDefaultEditor({
src: file,
});
editor.on('process', ({ dest }) => {
// Show the resulting image file
outputImage.src = URL.createObjectURL(dest);
});
}
</script>
<template>
<input type="file" @change="handleFile" />
<img :src="outputImage.src" alt="" />
</template>
Uploading The Resulting Image
We can use Pintura to upload the edited image as well. We set the store
property on the imageWriter
, this tells Pintura to POST the resulting image to the specified URL. Pintura will show a progress indicator while it’s busy uploading.
<script setup>
import { openDefaultEditor } from '@pqina/pintura';
import { reactive } from 'vue';
const outputImage = reactive({ src: '' });
function handleFile(e) {
const { files } = e.target;
const [file] = files;
if (!/image/.test(file.type)) return;
const editor = openDefaultEditor({
src: file,
// Here we pass the `store` property to the `imageWriter`, it'll
// POST our image as FormData to the defined end point at `/upload/`
imageWriter: {
store: '/upload/',
},
});
editor.on('process', ({ dest }) => {
outputImage.src = URL.createObjectURL(dest);
});
}
</script>
<template>
<input type="file" @change="handleFile" />
<img :src="outputImage.src" alt="" />
</template>
Conditionally Loading Pintura
Pintura weighs in at about half the size of a 5¼ inch floppy (about 200KB
gzipped), while super small for an image editor, that’s still quite a lot of data for slower connections.
To improve page performance we can use tree-shaking and only import the internal Pintura modules we need, but on top of that we can wait with loading Pintura until the user selects an image to edit. It’s at that time we need the editor image editing.
Let’s make this tiny change now, we’ll use a dynamic import
to load the module.
<script setup>
import { reactive } from 'vue';
const outputImage = reactive({ src: '' });
async function handleFile(e) {
const { files } = e.target;
const [file] = files;
if (!file || !/image/.test(file.type)) return;
// Dynamically load Pintura when the user has selected a valid image file
const { openDefaultEditor } = await import('@pqina/pintura');
const editor = openDefaultEditor({
src: file,
imageWriter: {
store: '/upload/',
},
});
editor.on('process', ({ dest }) => {
outputImage.src = URL.createObjectURL(dest);
});
}
</script>
<template>
<input type="file" @change="handleFile" />
<img :src="outputImage.src" alt="" />
</template>
Conclusion
With only a couple lines of code we’ve added a fully featured image editing solution to our project and we didn’t even use the Vue Pintura Components. While integrating Pintura we’ve learned about reactive Vue objects and dynamic loading of JavaScript modules.
We’ve got everything we need to add image editing functionality to a Vue project, making it possible to crop profile pictures, build photo collages, annotate images, and much more.