Introduction
For any product manager or app developer, it is very easy to record the screen of your latest release. It is also very easy to record yourself as you walk through the application. It is however not easy to combine these two videos together let alone in a presentable manner.
In this tutorial, we demonstrate how to create a demo walkthrough generator for easy updates of your product walkthroughs.
Codesandbox
The completed project is available on Codesandbox.
Github
You can clone the full source code on my Github repository.
Prerequisites
To be able to follow through with this tutorial, entry-level knowledge of HTML, CSS, and JavaScript is required. Knowledge of VueJs is recommended but not required.
Setup
NuxtJs Installation
NuxtJs is a VueJS framework that provides us with a modular, performant, and enjoyable experience.
To set up our project, run the following command in your preferred working directory:
1yarn create nuxt-app nuxtjs-mobile-demo-walkthrough-generator23# OR45npx create-nuxt-app nuxtjs-mobile-demo-walkthrough-generator67# OR89npm init nuxt-app nuxtjs-mobile-demo-walkthrough-generator
The commands above will trigger a set of questions required to help the utility set up the project. Here are our recommended defaults:
Project name: nuxtjs-mobile-demo-walkthrough-generator
Programming language: JavaScript
Package manager: Yarn
UI Framework: Tailwind CSS
Nuxt.js modules: N/A
Linting tools: N/A
Testing framework: None
Rendering mode: Universal (SSR/SSG)
Deployment target: Server (Node.js hosting)
Deployment tools: N/A
What is your Github
<your-github-username>
Version control system: Git
You may now navigate to the project and launch it
1cd nuxtjs-mobile-demo-walkthrough-generator234yarn dev56# OR78npm run dev
@nuxtjs/cloudinary installation
@nuxtjs/cloudinary is the recommended Cloudinary plugin for NuxtJs. Cloudinary is a media management platform that allows us to unleash the potential of our media assets by providing a powerful API service.
We first need to add @nuxtjs/cloudinary
as a dependency in our project
1yarn add @nuxtjs/cloudinary23# OR45npm install @nuxtjs/cloudinary
Add @nuxtjs/cloudinary
as a module in the modules
project of the nuxt.config.js
file:
1// nuxt.config.js23export default {45...67modules: [89'@nuxtjs/cloudinary'1011]1213...1415}
We will finally add a cloudinary
section in the nuxt.config.js
to set configurations for the module:
1// nuxt.config.js23export default {45...67cloudinary: {89cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,1011secure: true,1213useComponent: true1415}1617...1819}
As visible in the codebase above, we are making use of environmental variables. These are sensitive details that we do not want in our codebase. To set up environmental variables locally, we will create a .env
file
1touch .env
We will add the Cloudinary cloud name here. If you do not have one, create an account on https://cloudinary.com/. The cloud name will be visible in the console section.
1<!-- .env -->23NUXT_ENV_CLOUDINARY_CLOUD_NAME=<secret-cloudinary-cloud-name>
Template file placement
To generate the final complete file, we are going to make use of a video template. This is a 30-minute video that has no audio and has the background and phone template we require. This will limit our application to generate videos of max 30 minutes. Grab this video here. We recommend creating a folder called nuxtjs-mobile-demo-walkthrough-generator
and saving this video with the name iphone_se_template.mp4
. This will lead to the video having the public_id nuxtjs-mobile-demo-walkthrough-generator/iphone_se_template.mp4
.
Uploading video files
We are first going to create a simple form for selecting the two video files. One is the mobile app video, the other is an instructional video. We are going to place this form in a Form.vue
component file.
1<!-- components/Form.vue -->23<form action="#" method="POST">45<div>67<div>89<div>1011<div>1213<label for="mobile_app_video">1415Mobile app video1617</label>1819<div>2021<input id="mobile_app_video" name="mobile_app_video" required="" type="file" />2223</div>2425</div>2627</div>2829<div>3031<div>3233<label for="instructor_video">3435Instructor video3637</label>3839<div>4041<input id="instructor_video" name="instructor_video" required="" type="file" />4243</div>4445</div>4647</div>4849</div>5051<div>5253<button type="submit">5455Save5657</button>5859<p v-if="uploadingMobileAppVideo || uploadingInstructorVideo">6061Upload in progress6263</p>6465</div>6667</div>6869</form>
As indicated in the above code, the submission of the form will be handled by the submit
method. Let's see what this method contains:
1<script>23// components/Form.vue45export default {67data() {89return {1011uploadingMobileAppVideo: false,1213uploadingInstructorVideo: false,1415};1617},1819methods: {2021submit(e) {2223this.uploadMobileAppVideo(e);2425this.uploadInstructorVideo(e);2627},28293031async uploadMobileAppVideo(e) {3233this.uploadingMobileAppVideo = true;34353637// Mobile app video file3839let file = e.target.mobile_app_video.files[0];40414243/* Read data */4445const data = await this.readData(file);46474849/* upload the converted data */5051const resp = await this.$cloudinary.upload(data, {5253upload_preset: "default-preset",5455folder: `nuxtjs-mobile-demo-walkthrough-generator`,5657});58596061this.$emit("MobileAppVideoUploaded", resp.public_id);62636465this.uploadingMobileAppVideo = false;6667},68697071async uploadInstructorVideo(e) {7273this.uploadingInstructorVideo = true;74757677// Instructor video file7879let file = e.target.instructor_video.files[0];80818283/* Read data */8485const data = await this.readData(file);86878889/* upload the converted data */9091const resp = await this.$cloudinary.upload(data, {9293upload_preset: "default-preset",9495folder: `nuxtjs-mobile-demo-walkthrough-generator`,9697});9899100101this.$emit("InstructorVideoUploaded", resp.public_id);102103104105this.uploadingInstructorVideo = false;106107},108109readData(f) {110111return new Promise((resolve) => {112113const reader = new FileReader();114115reader.onloadend = () => resolve(reader.result);116117reader.readAsDataURL(f);118119});120121},122123},124125};126127</script>
In the above code, we read the files submitted and upload them to the nuxtjs-mobile-demo-walkthrough-generator
folder. We also specify an upload preset. These are a set of rules to be used for all uploads. To create an upload reset, proceed to the settings
section in your Cloudinary account. Proceed to the upload
section. We recommend the following settings:
Unique filename: true
Delivery type: upload
Access mode: public
The above code will upload the files and emit two events. MobileAppVideoUploaded
and InstructorVideoUploaded
. In the next section, we receive these two events and generate the final video.
Video generation
We will now inject the Form.vue
component in the pages/index.vue
file and listen for the events:
1<!-- pages/index.vue -->23<template>45...67<Form89@InstructorVideoUploaded="instructorVideoUploaded"1011@MobileAppVideoUploaded="mobileAppVideoUploaded"1213/>1415...1617</template>
We will receive the public_id
and assign it to local variables. When overlaying videos in subfolders, you need to replace all \
with :
as indicated here.
1// pages/index.vue23<script>45export default {67data() {89return {1011instructorVideo: null,1213mobileAppVideo: null,1415};1617},1819methods: {2021instructorVideoUploaded(public_id) {2223this.instructorVideo = public_id.replace("/", ":");2425},2627mobileAppVideoUploaded(public_id) {2829this.mobileAppVideo = public_id.replace("/", ":");3031},3233},3435};3637</script>
Once the public_id
s have been stored in the page state, we will proceed to overlay the two videos on top of the initial video template. To remove the sound from the mobile app video, we add the audioCodec="none"
setting.
1<template>23...45<cld-video67public-id="nuxtjs-mobile-demo-walkthrough-generator/iphone_se_template"89width="1000"1011crop="scale"1213quality="auto"1415controls="true"1617autoplay="true"1819class="m-auto"2021v-if="mobileAppVideo && instructorVideo"2223>2425<cld-transformation2627:overlay="`video:${mobileAppVideo}`"2829width="405"3031height="750"3233gravity="center"3435x="-385"3637y="0"3839audioCodec="none"4041/>4243<cld-transformation4445:overlay="`video:${instructorVideo}`"4647width="300"4849height="300"5051gravity="south_east"5253radius="150"5455x="100"5657y="100"5859crop="fill"6061/>6263</cld-video>6465...6667</template>
Conclusion
With the above code, we have successfully created a mobile demo walkthrough generator. When the product changes, we simply upload the new videos and retrieve the newly compiled video. This just shows the potential of what is possible.