Users are now interacting with videos more and more every day. In this article, we explore how to add action points within and after our video has finished playing.
Before we begin, we are going to set up the content we are going to use. This is a video and an image we will use as a banner in the video. We will store these files in Cloudinary a media management platform with a powerful set of APIs and SDKs. If you do not have an account, feel free to create one here.
Proceed to the media library and create a folder called nuxtjs-in-video-cta
and add the following files:
Here is a screenshot of the resulting folder:
The final project can be viewed on Codesandbox.
You can find the full source code on my Github repository.
To build our app, we will be using Nuxt.Js, a Vue.Js framework. To get started, ensure you have either yarn or npm v5.2+/v6.1+ installed. Open the terminal in your preferred working directory and run the following command
yarn create nuxt-app nuxtjs-in-video-cta
# OR
npx create-nuxt-app nuxtjs-in-video-cta
# OR
npm init nuxt-app nuxtjs-in-video-cta
Code language: PHP (php)
This will trigger a set of prompts. Here are our recommended defaults:
Project name: nuxtjs-in-video-cta
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
You may now enter and run the app. The output should be visible on https://localhost:3000
cd nuxtjs-in-video-cta
yarn dev
# OR
npm run dev
Code language: PHP (php)
To be able to display custom CTAs, we need to be able to detect and react when the video player state changes. Cloudinary’s video player allows us to receive such events. That is why we will be using it.
First and foremost, we will be adding the video player’s assets to our nuxt.config.js
file. This is how simple installation is.
// nuxt.config.js
export default {
head: {
...
link: [
...
{ rel: 'stylesheet', href: 'https://unpkg.com/cloudinary-video-player@1.5.9/dist/cld-video-player.min.css' }
],
script: [
{ src: 'https://unpkg.com/cloudinary-core@latest/cloudinary-core-shrinkwrap.min.js' },
{ src: 'https://unpkg.com/cloudinary-video-player@1.5.9/dist/cld-video-player.min.js' },
],
},
};
Code language: JavaScript (javascript)
To be able to reference our account from any file, we need to store our Cloudinary cloud name. We will store it as an environmental variable in the global .env
file located at the root of the project.
touch .env
Code language: CSS (css)
To get your cloud name, refer to your Dashboard under the Account Details
section. Insert it in your .env
file.
<!-- .env -->
NUXT_ENV_CLOUDINARY_CLOUD_NAME=<cloudinary-cloud-name-value>
Code language: HTML, XML (xml)
With the above setup, we may now create our video player. This is done first by creating a normal video
element and adding the cld-video-player cld-video-player-skin-dark
classes.
<template>
<video
id="video-player"
controls
autoplay
muted
class="cld-video-player cld-video-player-skin-dark w-full h-full"
>
</video>
</template>
Code language: HTML, XML (xml)
We will now create a Cloudinary instance in our mounted
lifecycle hook and use it to initialize the video player and configure the video source.
// pages/index.vue
<script>
export default {
name: 'IndexPage',
data(){
return {
cld:null,
player:null,
video: "nuxtjs-in-video-cta/pexels-kelly-lacy-9722139.mp4"
}
},
mounted(){
this.cld = cloudinary.Cloudinary.new({
cloud_name: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
secure: true
});
this.player = this.cld.videoPlayer(
'video-player'
);
this.player.source(this.video);
}
};
</script>
Code language: HTML, XML (xml)
We should now be able to view the video on our webpage.
In order to create a call to action based on the percentage played, we need to first receive and store the percentage in the page state. To get this data from the video player, we simply add the percentsplayed
analytics event to the configuration together with the playedEventTimes
we want to be notified of. We will then add an event listener for the percentsplayed
event.
// pages/index.vue
<script>
export default {
data(){
return {
...
percentage:null
}
},
mounted(){
...
this.player = this.cld.videoPlayer(
'video-player',
{
playedEventTimes: [10,20,30,40,50,60,70,80,90],
analytics: {
events: [
'play',
'pause',
'`',
'start',
'ended',
]
}
}
);
this.player.source(this.video);
this.player.on('percentsplayed', (event) => {
this.percentage = event.eventData.percent;
});
}
};
</script>
Code language: HTML, XML (xml)
We can now render an element such as a button based on the percentage
. To ensure that the element is placed above the video player, we need to position it absolutely in a relatively positioned parent.
<!-- pages/index.vue -->
<template>
<div>
...
<div class="w-2/3 h-96 my-10 mx-auto relative">
<video
id="video-player"
controls
autoplay
muted
class="cld-video-player cld-video-player-skin-dark w-full h-full"
>
</video>
<a
v-if="percentage > 20 && percentage < 70"
href="https://www.pexels.com/video/aerial-view-on-city-during-dawn-9722139/"
target="_blank"
class="inline-flex items-center px-4 py-2 opacity-50 hover:opacity-70 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-white bg-transparent hover:text-black hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 absolute top-10 right-10"
>
View on Pexels
</a>
</div>
</div>
</template>
Code language: HTML, XML (xml)
Now we should see the button when the percentage is greater than 20 but less than 70.
The next call to action we want to display is one that is visible when the video is either paused or it has ended. We do this by simply adding the events to the event. analytics
setting in the video player initialization. We then add event listeners to store the video state in the page state.
// pages/index.vue
<script>
export default {
name: 'IndexPage',
data(){
return {
cld:null,
player:null,
video: "nuxtjs-in-video-cta/pexels-kelly-lacy-9722139.mp4",
percentage:null,
paused:null,
ended:null
}
},
mounted(){
this.cld = cloudinary.Cloudinary.new({
cloud_name: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
secure: true
});
this.player = this.cld.videoPlayer(
'video-player',
{
playedEventTimes: [10,20,30,40,50,60,70,80,90],
analytics: {
events: [
'play',
'pause',
'percentsplayed',
'start',
'ended',
]
}
}
);
this.player.source(this.video);
this.player.on('percentsplayed', (event) => {
this.percentage = event.eventData.percent;
});
this.player.on('play', () => {
this.paused = false;
this.ended = false;
});
this.player.on('pause', () => {
this.paused = true;
});
this.player.on('start', () => {
this.paused = false;
this.ended = false;
});
this.player.on('ended', () => {
this.ended = true;
});
}
};
</script>
Code language: HTML, XML (xml)
Now we can simply show the CTA when paused
or ended
is true.
<!-- pages/index.vue -->
<template>
<div>
<div class="w-2/3 h-96 my-10 mx-auto relative">
<video
id="video-player"
controls
autoplay
muted
class="cld-video-player cld-video-player-skin-dark w-full h-full"
>
</video>
<a
v-if="percentage > 20 && percentage < 70"
href="https://www.pexels.com/video/aerial-view-on-city-during-dawn-9722139/"
target="_blank"
class="inline-flex items-center px-4 py-2 opacity-50 hover:opacity-70 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-white bg-transparent hover:text-black hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 absolute top-10 right-10"
>
View on Pexels
</a>
<img
v-if="paused || ended"
src="https://res.cloudinary.com/hackit-africa/image/upload/v1639495990/nuxtjs-in-video-cta/free-video-856479.jpg"
class="absolute w-96 top-16 left-72"
/>
</div>
</div>
</template>
Code language: HTML, XML (xml)
With the above setup, we should see the image when the video is paused.
To read more about the Video player events and its comprehensive API, feel free to review the extensive documentation.