An aspect ratio of an image is the ratio of an image’s width to its height. It is commonly two numbers separated by a colon such as 16:9. As we increasingly have different devices being created some with varying display sizes and viewing angles, aspect ratios help us create optimally viewable videos. This situation has been exacerbated by different software demanding their own aspect ratios such as social media websites.
In this tutorial, we review how we can dynamically render the same video in varying aspect ratios.
The final project can be viewed on Codesandbox.
You can find the full source code on my Github repository.
A basic understanding of HTML, CSS, and JavaScript is required to be able to follow along with this tutorial. Knowledge of Vue.Js and Nuxt.Js is not required to understand but would be beneficial.
Nuxt.Js is an intuitive Vue framework that boasts of being easy to learn and easy to use. To quickly get started with Nuxt.Js, ensure you have Yarn package manager installed or Node package manager greater than version v5.2+ or v6.1. This will ensure that npx is installed by default. This enables us to use the create-nuxt-app utility.
To get started, open the terminal in your working directory of preference.
yarn create nuxt-app nuxtjs-video-aspect-ratio-editor
# OR
npx create-nuxt-app nuxtjs-video-aspect-ratio-editor
# OR
npm init nuxt-app nuxtjs-video-aspect-ratio-editor
Code language: PHP (php)
This will result in a series of questions. Here are our recommended defaults:
Project name: nuxtjs-video-aspect-ratio-editor
Programming language: JavaScript
Package manager: Yarn
UI framework: Tailwind CSS
Nuxt.js modules: N/A
Linting tools: N/A
Testing frameworks: None
Rendering mode: Universal (SSR / SSG)
Deployment target: Server (Node.js hosting)
Development tools: N/A
What is your Github username:
<your-github-username>
Version control system: Git
Once the setup is complete, you may now enter the directory and run the project:
cd nuxtjs-video-aspect-ratio-editor
yarn dev
# OR
npm run dev
Code language: PHP (php)
Cloudinary is a media management platform that enables us to make the most of our media assets through its comprehensive service, APIs and SDKs. We are going to install the recommended Nuxt.Js plugin, @nuxtjs/cloudinary.
Open the terminal in your project folder to add the dependency:
yarn add @nuxtjs/cloudinary
# or
npm install @nuxtjs/cloudinary
Code language: CSS (css)
Once installation is complete, add the module in the modules
section of the nuxt.config.js
file:
// nuxt.config.js
export default {
...
modules: [
'@nuxtjs/cloudinary'
]
...
}
Code language: JavaScript (javascript)
We will then configure our Cloudinary instance by adding a cloudinary
section to the nuxt.config.js
file:
// nuxt.config.js
export default {
...
cloudinary: {
cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
useComponent: true,
secure: true
}
}
Code language: JavaScript (javascript)
In the above piece of code, we refer to an environmental variable (NUXT_ENV_CLOUDINARY_CLOUD_NAME
). Environmental variables as values that are not dependent on our code itself but on the environment in the code that is executed. They are also useful for sensitive credentials we do not want to be exposed to in our code repositories. Let us first create the .env
file which will contain our environmental variables:
touch .env
Code language: CSS (css)
We will then set our environmental variables in the file
NUXT_ENV_CLOUDINARY_CLOUD_NAME=<secret-cloud-name>
Code language: HTML, XML (xml)
Before uploading the video file, we first need to create a form for the user to select the file for upload:
<!-- pages/index.vue -->
<form action="#" method="POST" @submit.prevent="submit" >
<div>
<label for="email">Video</label>
<div>
<input
id="file"
name="file"
type="file"
required
@change="handleFile"
/>
</div>
</div>
<div>
<p v-if="uploading" >
Uploading...
</p>
<button v-else type="submit" >
Upload
</button>
</div>
</form>
Code language: HTML, XML (xml)
On file selection, the handleFile
method will be called whereas the submit
method will be called once the form is submitted.
// pages/index.vue
export default {
data() {
return {
uploading: false,
uploadVideo: null,
cloudinaryVideo: null,
...
};
},
methods: {
async handleFile(e) {
this.uploadVideo = e.target.files[0];
},
async readData(f) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(f);
});
},
async submit() {
this.uploading = true;
const videoData = await this.readData(this.uploadVideo);
this.cloudinaryVideo = await this.$cloudinary.upload(videoData, {
upload_preset: "default-preset",
folder: "nuxtjs-video-aspect-ratio-editor",
});
this.uploading = false;
...
},
...
},
};
Code language: JavaScript (javascript)
The handleFile
method simply stores the selected file in our local state. The submit
method uses the readData
method to obtain the file data from the selected file. It then uploads this file data to Cloudinary specifying the upload present and the upload folder.
An upload preset is a pre-determined set of options to be applied to a file upload on Cloudinary. To create an upload preset, proceed to the create upload preset page. We recommend using the following defaults:
Name: default-preset
Unique filename: true
Delivery type: upload
Access mode: public
Once the file upload is complete is when we now proceed to generate the videos with varying aspect ratios.
We first need to determine which aspect ratios we want to convert to. Let’s add them into a state variable.
// pages/index.vue
export default {
data(){
return {
...
aspectRatios: [
{ value: "7680x4320", label: "7680 x 4320 (8K UHDTV)" },
{ value: "5120x2880", label: "5120 x 2880 (5K, iMac with retina screen)" },
{ value: "3840x2160", label: "3840 × 2160 (4K UHDTV)" },
{ value: "2048x1536", label: "2048 x 1536 (iPad with retina screen)" },
{ value: "1920x1200", label: "1920 x 1200 (WUXGA)" },
{ value: "1920x1080", label: "1920 x 1080 (HD TV, iPhone 6 plus)" },
{ value: "1334x750", label: "1334 x 750 (iPhone 6)" },
{ value: "1200x630", label: "1200 x 630 (Facebook)" },
{ value: "1136x640", label: "1136 x 640 (iPhone 5 screen)" },
{ value: "1024x768", label: "1024 x 768 (iPad)" },
{ value: "1024x512", label: "1024 x 512 (Twitter)" },
{ value: "960x640", label: "960 x 640 (iPhone 4 screen)" },
{ value: "800x600", label: "800 x 600" },
{ value: "728x90", label: "728 x 90 (Common web banner ad size)" },
{ value: "720x576", label: "720 x 576 (PAL)" },
{ value: "640x480", label: "640 x 480 (VGA)" },
{ value: "576x486", label: "576 x 486 (NTSC)" },
{ value: "320x480", label: "320 x 480 (HVGA)" },
],
...
};
}
}
Code language: JavaScript (javascript)
The above are the most commonly used aspect ratios. We will not proceed to generate the converted videos once the Cloudinary upload is done:
// pages/index.vue
export default {
data() {
return {
...
videos: [],
};
},
methods: {
...
async submit() {
...
this.generateVideos();
},
generateVideos() {
this.videos = this.aspectRatios.map((ratio) => ({
ratio,
url: this.$cloudinary.video.url(this.cloudinaryVideo.public_id, {
width: parseInt(ratio.value.split("x")[0]),
height: parseInt(ratio.value.split("x")[1]),
crop: "crop",
}),
element: this.$cloudinary.video.element(this.cloudinaryVideo.public_id, {
width: parseInt(ratio.value.split("x")[0]),
height: parseInt(ratio.value.split("x")[1]),
crop: "crop",
controls: true,
})
.toHtml(),
}));
},
},
};
Code language: JavaScript (javascript)
The above piece of code triggers the generate videos method once the upload is complete. This method iterates through each of the aspect ratios generating transformed URLS and transformed HTML video elements. This is by making use of the
$cloudinary.video.URL and the $cloudinary.video.element
methods passing in the width and the height. The output is stored in the video
state variable. We will not iterate this state variable to render the video and the download link to our users:
<!-- pages/index.vue -->
<ul role="list" >
<li v-for="(video, index) in videos" :key="index">
<div>
<div v-html="video.element"></div>
<div>
<div>
<h3>{{ video.ratio.label }}</h3>
<a target="_blank" :href="video.url" >
Download
</a>
</div>
</div>
</div>
</li>
</ul>
Code language: HTML, XML (xml)
Using the above tutorial, we have been able to dynamically change video aspect ratios in our applications. There are additional options we can specify to better customize our experience. Feel free to read more on the video transformations documentation page.