Reading Image Sizes and Dimensions with Alpine.js
source link: https://www.raymondcamden.com/2022/12/08/reading-image-sizes-and-dimensions-with-alpinejs
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Reading Image Sizes and Dimensions with Alpine.js
It's been a few weeks since I've done this, but while looking at my new stats (https://raymondcamden.goatcounter.com/), I saw one of my old Vue.js posts getting some activity: Reading Image Sizes and Dimensions with Vue.js. In that blog post, I showed how to take a user-selected file and check the file size and dimensions of an image. As I've been slowly going through my Vue.js posts and creating Aline.js versions, I thought this would be a perfect fit.
I'm not going to repeat everything from the previous entry, but let me recap the highlights.
- Obviously, your client-side code can't go into the user's machine and read ad hoc files. What it can do is get information about a user selected file. This can be done with an
input
tag usingtype=file
. - Immediately after selecting the file, your code has access to the file size.
- To get the dimensions though, you need to do a bit of work. First, create a new
Image
object. - You need to set the source of the image to the contents of the file. This can be done by using a data URL.
- Once the image is loaded (remember, images have an
onload
event), you can then check the dimensions.
Ok, so given the above, let's build a quick demo. First, the HTML. I'm just going to have the input field and a place to print out details about the image.
<div x-data="app">
<input type="file" x-ref="myFile" x-on:change="selectedFile" accept="image/*"><br/>
<template x-if="imageLoaded">
<p>
Image size is <span x-text="image.size"></span><br/>
Image width and height is <span x-text="image.width"></span> / <span x-text="image.height"></span>
</p>
</template>
</div>
A few things to note here. Like Vue, we sometimes need to reach out to the DOM, and like Vue, this is done via refs
. You can see my setting x-ref="myFile"
to gain access to the input field directly. Also, note I'm using the change
event. This will fire when the user selects a file. Now let's look at the code.
document.addEventListener('alpine:init', () => {
Alpine.data('app', () => ({
imageLoaded:false,
image: {
size:null,
width:null,
height:null
},
selectedFile() {
this.imageLoaded = false;
let file = this.$refs.myFile.files[0];
if(!file || file.type.indexOf('image/') !== 0) return;
this.image.size = file.size;
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = evt => {
let img = new Image();
img.onload = () => {
this.image.width = img.width;
this.image.height = img.height;
this.imageLoaded = true;
}
img.src = evt.target.result;
}
reader.onerror = evt => {
console.error(evt);
}
}
}))
});
My Alpine app has two main variables, imageLoaded
and image
. The only real logic is in selectedFile
. This will use $refs
to grab the input field and the selected image. I then use a FileReader
object to read in the bits, set it to the image, and when onload
is fired, I can update my variables to the front-end displays. Given this source image for example:
If I select it, I'll see this:
You can test this yourself using the CodePen below:
Ok, so as I did in the previous post, let's consider a simple example that adds validation. Specifically - a max file size, a max width, and a max height. The HTML is mostly the same except now I show an error on a validation failure:
<div x-data="app">
<input type="file" x-ref="myFile" x-on:change="selectedFile" accept="image/*"><br/>
<template x-if="imageError">
<p class="imageError" x-text="imageError">
</p>
</template>
</div>
In the JavaScript, I added constants for my max values:
const MAX_SIZE = 100000;
const MAX_WIDTH = 500;
const MAX_HEIGHT = 300;
And here's the Alpine app itself:
document.addEventListener('alpine:init', () => {
Alpine.data('app', () => ({
imageError:'',
image: {
size:null,
width:null,
height:null
},
selectedFile() {
this.imageError = '';
let file = this.$refs.myFile.files[0];
if(!file || file.type.indexOf('image/') !== 0) return;
this.image.size = file.size;
if(this.image.size > MAX_SIZE) {
this.imageError = `The image file size (${this.image.size}) is too much (max is ${MAX_SIZE}).`;
return;
}
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = evt => {
let img = new Image();
img.onload = () => {
this.image.width = img.width;
this.image.height = img.height;
if(this.image.width > MAX_WIDTH) {
this.imageError = `The image width (${this.image.width}) is too much (max is ${MAX_WIDTH}).`;
return;
}
if(this.image.height > MAX_HEIGHT) {
this.imageError = `The image height (${this.image.height}) is too much (max is ${MAX_HEIGHT}).`;
return;
}
}
img.src = evt.target.result;
}
reader.onerror = evt => {
console.error(evt);
}
}
}))
});
For the most part, this is the same, with the only change being that now I check the various properties and set a new variable, imageError
, when something fails validation. You can test this below:
I'll repeat myself, which my readers know I like to do, but the more I use Alpine, the more it just clicks with me.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK