When we were in the design stages of our website, and we were still trying to figure out a way to explain our product, I created 2 animated videos, that illustrated edgemesh's capabilities and the difference between a traditional network and our mesh network. The issue however became, how to seamlessly display this content on our page. Stacking it side by didn't make sense, because we wanted to display the full resolution of the video, and stacking the videos one on top of the other made the information lose a bit of context, and in general just took up too much space. The only other option left, was overlaying the videos seamlessly directly one on top of the other, and being able to somehow interactively mouse between them.
-
Preparing your content
-
Overlaying 2 video elements
-
Using the onMouseMove event to determine mouse position
-
Figuring out how we are going to animate
-
Puting it all together, and making it performant
-
Making it work on mobile
The first thing you will want to do is prepare your video content, before anything else. I personally recommend, for compatability reasons, to get your video content into at least 3 formats.
.webm
.mp4
.gif
For performance reasons, I will typically take my source footage and I will first convet it to a .webm
. This format was developed by Google to be extremely lightweight, and create high quality and low file size videos very well, but is not compatabile with all browsers. More information about the .webp
format can be found here on its official Google developer page.
Typically I will use the .webm
as my primary source, and fallback to .mp4
if the browser does not support .webm
-- the .gif
only needs to be generated if you plan on mobile compatability (more on that later.)
You can use a service like Cloudconvert to do your video conversions, and it supports all the format that I have mentioned.
We will start off by creating a simple class extending the generic react component.
Inside of our component, we will define some props (basically just the paths to our 2 videos) and we will render both <video/>
containers.
Please note, I use the HTML5 <video/>
tag because it allows us to easily create fallbacks, for unsupported formats.
We will also start defining some styles above our class.
For this example, I use aphrodite inline style library. You can use regular CSS, or another library, but this is what I will be using for all of the examples.
https://gist.github.com/dde37e23b79df4ea0b165fd685163a9d
Okay, so we have our two videos overlayed one on top of the other, and our mouse position relative to the image, now what? Let's take a moment to visualize what we are trying to achieve. Basically we want to crop the top image based on our mouse/touch position to reveal the image underneath.
First we will start off by applying the mouses X position, which we figured out in the previous step, and apply it as a translational X
value to the Video A Container
(that's the video that is absolutely positioned on top of the other video.) This should move the Video A Container
and the Video A
inside the countainer with the mouse cursor to reveal the Video B
underneath.
We can then take the mouse position, and reverse it by multiplying it by -1 to, get a direct opposite direction. We then take this opposite direction value and plug it in to the translation of Video A
inside of the Video A Container
making sure that the Video A Container
has style property overflow: hidden
causing it to clip Video A
as it travels outside of its bounds.
When we combine both animations, they end up sort of cancelling each other out, and we get the desired effect, hooray! Now let's put it all together and make it work on mobile as well!
Now that we have a good idea of where all the pieces will be moving, we can begin putting it all together. Now there are numerous ways we can do the actual animation using CSS properties, but I recommened using transform: translateX()
for several reasons. Transform will always give a smoother experience on mobile, and will keep up better with lower end CPUs. Transitioning the left
property will lead to extreme stuttering in animation on mobile devices.
https://gist.github.com/0e4c3c0f2fe13a7b86db9c3e41b6bcc4
You may have noticed that I plug the transform values in to the style
prop instead of the className
prop, it's because I don't want aphrodite
to create a new stylesheet on every single state update. Various css-in-js
libraries handle this in different ways, and for something like Radium you would stick everything in to the style
prop.
Through out this tutorial, we have done our best to keep mobile in mind. Our mouse movement handler can detect mobile touches
for instance, and we are using transform: translateX()
instead of left
which should create a smooth animation even on most mobile devices. However, we seem to run into a small problem when attempting to render this component on mobile, which becomes apparent immediately when you try.
** MOST MOBILE DEVICES WILL ONLY RENDER ONE VIDEO AT A TIME **
Yup, and even worse, video autoplay is disabled in most mobile browsers due to battery saving measures. Even though now browser vendors are starting to see the light a little bit and even the new Apple OS allows for muted video to autoplay on a browser without a user gesture. there is still only one classic way to make your component bulletproof on most mobile devices.
By converting our video to .gif
we can get around the pesky <video/>
limitations on most mobile browsers. You can use a library like is.js to do a simple check to detect when the user is on a mobile browsers, and simply switch out your <video/>
tags with <picture/>
or <img/>
tags.