Skip to content

RESOURCES / BLOG

Floating Video Player

To provide our customers with the best video user experience, we want to provide them amazing accessibility features such as a video player which is fixed when opened but follows the user as they scroll the webpage. These are called floating video players. Floating videos are a common sight whether on YouTube, Facebook, or Twitter. In this tutorial, we explore how we can easily create such a floating video player in a Nuxt.Js app.

The final project can be viewed on Codesandbox.

You can find the full source code on my Github repository.

Nuxt.Js is a Vue.Js framework that boasts of improving developer productivity by being simple and powerful. We will use the create-nuxt-app utility to set up our project. Ensure you have yarn or npm v5.2+/v6.1+ installed. Navigate to your preferred working directory and open the terminal.


yarn create nuxt-app nuxtjs-floating-video-player

# OR

npx create-nuxt-app nuxtjs-floating-video-player

# OR

npm init nuxt-app nuxtjs-floating-video-player

Code language: PHP (php)

This will result in a series of prompts to customize your installation. Here are our recommended defaults:

Project name: nuxtjs-floating-video-player

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, feel free to enter and run the project. Once running, it will be available on http://localhost:3000


cd nuxtjs-floating-video-player


yarn dev

# OR

npm run dev

Code language: PHP (php)

Instead of using the default HTML player, we will be using Cloudinary’s video player. This is because it is bundled with many available customizations and features. Cloudinary is a platform that allows delivering the best media experience to our customers through their comprehensive SDKs and APIs.

We install their video player by adding the CDN files into our project. A content delivery network ensures that files are delivered fast and efficiently to our project. In this case, we will use files hosted by the UNPKG CDN. To add assets globally in our app, we’ll add them in the head section of the nuxt.config.js file.


// 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)

We can also use Cloudinary to host the videos we will be playing. We will be doing so by creating a folder called nuxtjs-floating-video-player in our media library. If you do not have an account, feel free to register one here. Add this video file to the nuxtjs-floating-video-player with the pixels-lisa-9963201 name. This means it will have nuxtjs-floating-video-player/pexels-lisa-9963201 as it’s public_id. This is what we’ll use to refer to the video in our video player.

We first add a <video> tag to our pages/index.vue file like we normally would. We add cld-video-player to ensure it is styled as a Cloudinary video player and additionally we change the theme using the cld-video-player-skin-dark class. To ensure we can reference the video player in our code, we add the floating-player id.


<!-- pages/index.vue -->

<template>

<div>

<video

id="floating-player"

autoplay

class="cld-video-player cld-video-player-skin-dark w-1/2 mx-auto h-96"

>

</video>

</div>

</template>

Code language: HTML, XML (xml)

We initialize the player in the mounted lifecycle hook as well as configure which video will be played.


// pages/index.vue

<script>

export default {

mounted(){

const cld  =  cloudinary.Cloudinary.new({ cloud_name:  process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME, secure:true  });

var player  =  cld.videoPlayer(

'floating-player',

{

controls:true,

floatingWhenNotVisible:  'left',

}

);

}

}

</script>

Code language: HTML, XML (xml)

In the above code, we reference the NUXT_ENV_CLOUDINARY_CLOUD_NAME environmental variable. These are variables we store outside our code for various reasons, in this case, code portability. To set your own cloud name, create the .env file in your project root.


touch .env

Code language: CSS (css)

You may obtain your cloud name in your account dashboard. You may add it to the .env file:


NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloud-name>

Code language: HTML, XML (xml)

We should now have a functional video player similar to the one visible below.

Video player screenshot

A challenge we have now is that we cannot scroll the page due to the lack of content. Thus we can’t know if our video player floats or not. To fix it, we will create a components/DummyContent.vue file and populate it will dummy content.


<!-- components/DummyContent.vue -->

<template>

<div class="leading-10">

<div class="hidden lg:block lg:absolute lg:inset-y-0 lg:h-full lg:w-full">

<div class="relative h-full text-lg max-w-prose mx-auto"  aria-hidden="true">

<svg class="absolute top-12 left-full transform translate-x-32"  width="404"  height="384"  fill="none"  viewBox="0 0 404 384">

<defs>

<pattern  id="74b3fd99-0a6f-4271-bef2-e80eeafdf357"  x="0"  y="0"  width="20"  height="20"  patternUnits="userSpaceOnUse">

<rect  x="0"  y="0"  width="4"  height="4"  class="text-gray-200"  fill="currentColor"  />

</pattern>

</defs>

<rect  width="404"  height="384"  fill="url(#74b3fd99-0a6f-4271-bef2-e80eeafdf357)"  />

</svg>

<svg  class="absolute top-1/2 right-full transform -translate-y-1/2 -translate-x-32"  width="404"  height="384"  fill="none"  viewBox="0 0 404 384">

<defs>

<pattern  id="f210dbf6-a58d-4871-961e-36d5016a0f49"  x="0"  y="0"  width="20"  height="20"  patternUnits="userSpaceOnUse">

<rect  x="0"  y="0"  width="4"  height="4"  class="text-gray-200"  fill="currentColor"  />

</pattern>

</defs>

<rect  width="404"  height="384"  fill="url(#f210dbf6-a58d-4871-961e-36d5016a0f49)"  />

</svg>

<svg  class="absolute bottom-12 left-full transform translate-x-32"  width="404"  height="384"  fill="none"  viewBox="0 0 404 384">

<defs>

<pattern  id="d3eb07ae-5182-43e6-857d-35c643af9034"  x="0"  y="0"  width="20"  height="20"  patternUnits="userSpaceOnUse">

<rect  x="0"  y="0"  width="4"  height="4"  class="text-gray-200"  fill="currentColor"  />

</pattern>

</defs>

<rect  width="404"  height="384"  fill="url(#d3eb07ae-5182-43e6-857d-35c643af9034)"  />

</svg>

</div>

</div>

<div  class="relative px-4 sm:px-6 lg:px-8">

<div  class="mt-6 prose prose-indigo prose-lg text-gray-500 mx-auto">

<p>Faucibus commodo massa rhoncus, volutpat. <strong>Dignissim</strong> sed <strong>eget risus enim</strong>. Mattis mauris semper sed amet vitae sed turpis id. Id dolor praesent donec est. Odio penatibus risus viverra tellus varius sit neque erat velit. Faucibus commodo massa rhoncus, volutpat. Dignissim sed eget risus enim. <a href="#">Mattis mauris semper</a> sed amet vitae sed turpis id.</p>

<ul  role="list">

<li>Quis elit egestas venenatis mattis dignissim.</li>

<li>Cras cras lobortis vitae vivamus ultricies facilisis tempus.</li>

<li>Orci in sit morbi dignissim metus diam arcu pretium.</li>

</ul>

<p>Quis semper vulputate aliquam venenatis egestas sagittis quisque orci. Donec commodo sit viverra aliquam porttitor ultrices gravida eu. Tincidunt leo, elementum mattis elementum ut nisl, justo, amet, mattis. Nunc purus, diam commodo tincidunt turpis. Amet, duis sed elit interdum dignissim.</p>

<h2>From beginner to expert in 30 days</h2>

<p>Id orci tellus laoreet id ac. Dolor, aenean leo, ac etiam consequat in. Convallis arcu ipsum urna nibh. Pharetra, euismod vitae interdum mauris enim, consequat vulputate nibh. Maecenas pellentesque id sed tellus mauris, ultrices mauris. Tincidunt enim cursus ridiculus mi. Pellentesque nam sed nullam sed diam turpis ipsum eu a sed convallis diam.</p>

<blockquote>

<p>Sagittis scelerisque nulla cursus in enim consectetur quam. Dictum urna sed consectetur neque tristique pellentesque. Blandit amet, sed aenean erat arcu morbi.</p>

</blockquote>

<p>Faucibus commodo massa rhoncus, volutpat. Dignissim sed eget risus enim. Mattis mauris semper sed amet vitae sed turpis id. Id dolor praesent donec est. Odio penatibus risus viverra tellus varius sit neque erat velit.</p>

<h2  class="m-10 text-center font-bold">Everything you need to get up and running</h2>

<p>Purus morbi dignissim senectus mattis <a href="#">adipiscing</a>. Amet, massa quam varius orci dapibus volutpat cras. In amet eu ridiculus leo sodales cursus tristique. Tincidunt sed tempus ut viverra ridiculus non molestie. Gravida quis fringilla amet eget dui tempor dignissim. Facilisis auctor venenatis varius nunc, congue erat ac. Cras fermentum convallis quam.</p>

<p>Faucibus commodo massa rhoncus, volutpat. Dignissim sed eget risus enim. Mattis mauris semper sed amet vitae sed turpis id. Id dolor praesent donec est. Odio penatibus risus viverra tellus varius sit neque erat velit.</p>

</div>

<div  class="mt-6 prose prose-indigo prose-lg text-gray-500 mx-auto">

<p>Faucibus commodo massa rhoncus, volutpat. <strong>Dignissim</strong> sed <strong>eget risus enim</strong>. Mattis mauris semper sed amet vitae sed turpis id. Id dolor praesent donec est. Odio penatibus risus viverra tellus varius sit neque erat velit. Faucibus commodo massa rhoncus, volutpat. Dignissim sed eget risus enim. <a href="#">Mattis mauris semper</a> sed amet vitae sed turpis id.</p>

<ul  role="list">

<li>Quis elit egestas venenatis mattis dignissim.</li>

<li>Cras cras lobortis vitae vivamus ultricies facilisis tempus.</li>

<li>Orci in sit morbi dignissim metus diam arcu pretium.</li>

</ul>

<p>Quis semper vulputate aliquam venenatis egestas sagittis quisque orci. Donec commodo sit viverra aliquam porttitor ultrices gravida eu. Tincidunt leo, elementum mattis elementum ut nisl, justo, amet, mattis. Nunc purus, diam commodo tincidunt turpis. Amet, duis sed elit interdum dignissim.</p>

<h2>From beginner to expert in 30 days</h2>

<p>Id orci tellus laoreet id ac. Dolor, aenean leo, ac etiam consequat in. Convallis arcu ipsum urna nibh. Pharetra, euismod vitae interdum mauris enim, consequat vulputate nibh. Maecenas pellentesque id sed tellus mauris, ultrices mauris. Tincidunt enim cursus ridiculus mi. Pellentesque nam sed nullam sed diam turpis ipsum eu a sed convallis diam.</p>

<blockquote>

<p>Sagittis scelerisque nulla cursus in enim consectetur quam. Dictum urna sed consectetur neque tristique pellentesque. Blandit amet, sed aenean erat arcu morbi.</p>

</blockquote>

<p>Faucibus commodo massa rhoncus, volutpat. Dignissim sed eget risus enim. Mattis mauris semper sed amet vitae sed turpis id. Id dolor praesent donec est. Odio penatibus risus viverra tellus varius sit neque erat velit.</p>

<h2>Everything you need to get up and running</h2>

<p>Purus morbi dignissim senectus mattis <a href="#">adipiscing</a>. Amet, massa quam varius orci dapibus volutpat cras. In amet eu ridiculus leo sodales cursus tristique. Tincidunt sed tempus ut viverra ridiculus non molestie. Gravida quis fringilla amet eget dui tempor dignissim. Facilisis auctor venenatis varius nunc, congue erat ac. Cras fermentum convallis quam.</p>

<p>Faucibus commodo massa rhoncus, volutpat. Dignissim sed eget risus enim. Mattis mauris semper sed amet vitae sed turpis id. Id dolor praesent donec est. Odio penatibus risus viverra tellus varius sit neque erat velit.</p>

</div>

</div>

</div>

</template>

Code language: HTML, XML (xml)

We will then add this component below our video player.


<!-- pages/index.vue -->

<template>

<div>

...

<div class="w-4/5 mx-auto relative py-16 bg-white overflow-hidden">

<dummy-content />

</div>

</div>

</template>

Code language: HTML, XML (xml)

Don’t forget to import the component for it to resolve:


// pages/index.vue

<script>

import DummyContent from "../components/DummyContent.vue"

  

export default {

components:{

DummyContent

},

...

}

</script>

  

Code language: HTML, XML (xml)

We should now be able to scroll with the video player disappearing once it’s out of the viewable space.

Dummy content

To make our video player float as we scroll, we need to add the floatingWhenNotVisible property to the videoPlayer initialization together with the direction we want our player to float to. In our case, we’ll set the player to float to the left.


// pages/index.vue

<script>

...

mounted(){

const  cld  =  cloudinary.Cloudinary.new({ cloud_name:  process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME, secure:true  });

var  player  =  cld.videoPlayer(

'floating-player',

{

controls:true,

floatingWhenNotVisible:  'left',

}

);

player.source('nuxtjs-floating-video-player/pexels-lisa-9963201.mp4');

}

}

</script>

Code language: HTML, XML (xml)

Now we should have a floating video player just like the one in the screenshot below.

Floating player screenshot

Feel free to read more about the Cloudinary video player on the official docs.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free