Jump to content

how-to: vanilla-tilt.js card effect in Emby Web App


chef

Recommended Posts

I have added this very cool JS library to embys web app home screen. It makes the  movie cards move is a unique way

 

 

You can see examples here at the  vanilla-tilt.js website. https://micku7zu.github.io/vanilla-tilt.js/

 

 

 

 

Here is how you can add the same effect to Emby web app

 

Here is how to enable the cool tilt effects on Emby's Poster Cards:

1. Copy the javascript from the URL into notepad:   https://raw.githubusercontent.com/micku7zu/vanilla-tilt.js/master/dist/vanilla-tilt.js

2. Save notepad to your desktop:
File Name: vanilla-tilt.js

Save as Type: All File (*)

 

3.Open: *AppData*\Roaming\Emby-Server\system\dashboard-ui\scripts

4. Copy and paste the vanilla-tilt.js file into the 'scripts' folder.

 

5. Copy the following code into a new notepad and save as 'mutation.js'

This will watch the emby environment for changes to the UI.

(function(win) {
    'use strict';
   
    var listeners = [],
    doc = win.document,
    MutationObserver = win.MutationObserver || win.WebKitMutationObserver,
    observer;
   
    function ready(selector, fn) {
        // Store the selector and callback to be monitored
        listeners.push({
            selector: selector,
            fn: fn
        });
        if (!observer) {
            // Watch for changes in the document
            observer = new MutationObserver(check);
            observer.observe(doc.documentElement, {
                childList: true,
                subtree: true
            });
        }
        // Check if the element is currently in the DOM
        check();
    }
       
    function check() {
        // Check the DOM for elements matching a stored selector
        for (var i = 0, len = listeners.length, listener, elements; i < len; i++) {
            listener = listeners[i];
            // Query for elements matching the specified selector
            elements = doc.querySelectorAll(listener.selector);
            for (var j = 0, jLen = elements.length, element; j < jLen; j++) {
                element = elements[j];
                // Make sure the callback isn't invoked with the
                // same element more than once
                if (!element.ready) {
                    element.ready = true;
                    // Invoke the callback with the element
                    listener.fn.call(element, element);
                }
            }
        }
    }

    // Expose `ready`
    win.ready = ready;
           
})(this); 

6. Save the 'mutation.js' file to the Emby scripts folder.

 

7. In notepad (or your favorite IDE) Open: *AppData*\Roaming\Emby-Server\system\dashboard-ui\index.html

Between the <body> tags you'll see where Emby is loading scripts into the DOM:

<body>

    <div class="backdropContainer"></div>
    <div class="backgroundContainer"></div>
    <div class="mainDrawer hide"></div>
    <div class="skinHeader"></div>
    <div class="mainAnimatedPages skinBody"></div>
    <div class="mainDrawerHandle"></div>
    <script src="scripts/apploader.js" defer></script>

</body> 

6. Add vanilla-tilt.js script and mutation.js to Emby:

<body>

    <div class="backdropContainer"></div>
    <div class="backgroundContainer"></div>
    <div class="mainDrawer hide"></div>
    <div class="skinHeader"></div>
    <div class="mainAnimatedPages skinBody"></div>
    <div class="mainDrawerHandle"></div>
    <script src="scripts/apploader.js" defer></script>
    <script src="scripts/vanilla-tilt.js"></script>
    <script src="scripts/mutation.js"></script>

</body> 

7. Add this to the script entries:

   <script>
        mutations();


        function mutations() {
            //Doesn't have to return promise, but eventually with all the animations from mojs and tilt, we may need to move on before it's ready...
            return new Promise(() => {

                ready('.cardScalable', function(element) {
                    // Ignore cards on the Emby dashboard
                    if (!document.querySelector('body').classList.contains('dashboardDocument')) {
                        VanillaTilt.init(element);
                        // give card overlays a nice 3D look over the tilting card
                        element.setAttribute("style", "transform-style: preserve-3d; transform: perspective(1000px);");
                    }
                });

                ready('.cardOverlayContainer', function(element) {
                    // Ignore cards on the Emby dashboard
                    if (!document.querySelector('body').classList.contains('dashboardDocument')) {
                        element.setAttribute("style", "transform: translateZ(20px);");

                    }
                });
            });
        }
    </script> 

The entire <body> tag will look like this:

<body>

    <div class="backdropContainer"></div>
    <div class="backgroundContainer"></div>
    <div class="mainDrawer hide"></div>
    <div class="skinHeader"></div>
    <div class="mainAnimatedPages skinBody"></div>
    <div class="mainDrawerHandle"></div>
    <script src="scripts/apploader.js" defer></script>
    <script src="scripts/vanilla-tilt.js"></script>
    <script src="scripts/mutation.js"></script>  
    <script>
        mutations();

        function mutations() {
            //Doesn't have to return promise, but eventually with all the animations from mojs and tilt, we may need to move on before it's ready...
            return new Promise(() => {

                ready('.cardScalable', function(element) {
                    // Ignore cards on the Emby dashboard
                    if (!document.querySelector('body').classList.contains('dashboardDocument')) {
                        VanillaTilt.init(element);
                        // give card overlays a nice 3D look over the tilting card
                        element.setAttribute("style", "transform-style: preserve-3d; transform: perspective(1000px);");
                    }
                });

                ready('.cardOverlayContainer', function(element) {
                    // Ignore cards on the Emby dashboard
                    if (!document.querySelector('body').classList.contains('dashboardDocument')) {
                        element.setAttribute("style", "transform: translateZ(20px);");

                    }
                });
            });
        }
    </script>
</body> 

If this bothers you, remove the code in the <script> tag, and it will no longer be enabled.

This will create the basic tilt effect on the cards.

If you don't see the tilt effect, clear browsing cache, and reload the page.

----------------------------------------------------------------------------------------------------------------------------------------------------------------

Advanced: Glare Effect (You'll want Visual Studio Code to edit this part):

In order to enable the glare effect on each card you need to edit the 'cardBuilder.js' file.

 

Notice: If you do this improperly you could really mess up your Emby installation for the Web app.

So maybe you had better copy this file to your desktop as a backup, just in case!

 

1. Open in visual studio Code: *AppData*\Roaming\Emby-Server\system\dashboard-ui\bower_components\emby-webcomponents\cardbuilder\cardBuilder.js

 

2. Yes it is 'Minified'... Use a VS Code addon called "Pretty Formatter" to make the code readable. Ctrl+Shift P > Format Document With ... > Choose: Pretty Formatter

 

3. Locate function 'getImageWidth'

 Find the code:

div.innerHTML = '<div class="card ' + shape + 'Card"><div class="cardBox"><div class="cardScalable"></div></div></div>'

replace the following:

div.innerHTML = '<div class="card ' + shape + 'Card"><div class="cardBox"><div data-tilt data-tilt-glare data-tilt-max-glare="0.8" class="cardScalable"></div></div></div>'

4. Find the function: buildCard

Find the code:

var cardScalableClass = 'cardScalable';
        isLayoutTv && !options.cardLayout && (cardScalableClass += " card-focuscontent", enableFocusTransfrom || (cardScalableClass += " card-focuscontent-large")),
            cardImageContainerOpen = '<div class="' + cardBoxClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainerOpen,
            cardImageContainerOpen += indicators.getTypeIndicator(item);

Replace with:

var cardScalableClass = 'cardScalable';
        isLayoutTv && !options.cardLayout && (cardScalableClass += " card-focuscontent", enableFocusTransfrom || (cardScalableClass += " card-focuscontent-large")),
            cardImageContainerOpen = '<div class="' + cardBoxClass + '"><div data-tilt-glare data-tilt-max-glare="0.8" class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainerOpen,
            cardImageContainerOpen += indicators.getTypeIndicator(item);

5. Save it! Done!

All we did there was add some data- attributes to any div being built with the class of '.cardScalable'.

We added glare. There are other data- attributes you can add to the poster cards if you so want to.

 

All the API info is posted here: https://micku7zu.github.io/vanilla-tilt.js/

I'll try to answer questions, and help with what I can.

Please just back up the cardBuilder.js before you edit it if you are not use to reading/writing Javascript.

Edited by chef
  • Like 2
Link to comment
Share on other sites

jachin99

You need to be the new web app Dev. My only complaint about the emby interface is that it's usually too flat and plain. All of the effects you put in the web app would be awesome in theater. Emby needs some eye candy.

  • Like 1
Link to comment
Share on other sites

We can't do something like that in the web app where hovering over the menu has clickable elements. The clickable elements should not be moving around.

 

But it could be done in theater with the poster that has remote control focus.

Link to comment
Share on other sites

I looked at their api and I don't see anything about just creating a random tilt that's not tied to the mouse. That's what I would need to test it in theater.

  • Like 1
Link to comment
Share on other sites

Yeah their API is only based on client.x/client.y locations inside an element, not useful for 10 foot ui.

 

Animations are tricky for the theater because if the user misses it then who's to say what has focus on the screen. Hence the importance of the focus frame.

 

Drawing attention to cards in the theater without using a focus frame could be as easy as a css pseduo element creating a glare effect in an infinite loop every couple seconds, but something tells me that it might not be accepted by users who are expecting the utility of the UI and care less about the dynamic of it.

Link to comment
Share on other sites

I wasn't sure if I should write an example of how to add the tilt js into emby, because there is an instance where you edit javascript. Although it isn't the hardest thing in the world, if you messed up the cardBuilder, then Emby would be broken.

 

The rest of the setup is just adding some javascript I wrote to he bottom of the emby index, so it's easy.

Link to comment
Share on other sites

jachin99

Yeah their API is only based on client.x/client.y locations inside an element, not useful for 10 foot ui.

 

Animations are tricky for the theater because if the user misses it then who's to say what has focus on the screen. Hence the importance of the focus frame.

 

Drawing attention to cards in the theater without using a focus frame could be as easy as a css pseduo element creating a glare effect in an infinite loop every couple seconds, but something tells me that it might not be accepted by users who are expecting the utility of the UI and care less about the dynamic of it.

 

There might be someone who complains about it but coming from EMC, I think there are some users won't use Theater because it lacks the same visual appeal.  I think Kodi, and its skins have such a large following partially because of the appearance.  Why not add something to it and see where it goes?  It could be something simple like the glare effect you mentioned above. I hate to suggest this but maybe there could be a setting to disable animations or effects if too many people are put off by it.  

Link to comment
Share on other sites

cool! The effects are visible on mobile. Tilt-js will use the phones accelerometer to add the effect when the phone moves around!

 

It's very cool!

Edited by chef
Link to comment
Share on other sites

If you could find some way to have it just tilt randomly without relying on any user input, then that would make it realistic to integrate into Theater.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...
jachin99

If you can't find out how to get the tilt or glass effect without user input then even a lot of your page transitions and the little effects you have created would do a lot for theater also. Those kinds of touches really add a lot of depth and character to the UI in my opinion.

Link to comment
Share on other sites

chef

If you can't find out how to get the tilt or glass effect without user input then even a lot of your page transitions and the little effects you have created would do a lot for theater also. Those kinds of touches really add a lot of depth and character to the UI in my opinion.

I'm working on an idea a 'Kids' theme using mo.js (motion.js).

 

Lots of primary colors, and cool motion animation.

 

Kind of like 'YouTube Kids' but it's 'Emby Kids'.

 

Im thinking it should have lots of cool color animation explosions on like buttons, and lots of transition animations that make the Emby cards colorful.

Edited by chef
Link to comment
Share on other sites

chef

I've updated the code above to add styles to the cardScalable to make things look more 3D. Also, The tilt effect ignores cards on the Dashboard now.

Link to comment
Share on other sites

  • 2 weeks later...

WOW! I've been able to use IntersectionObserver to lazy load Vanilla-Tilt and it has changed everything. It is a  super fast load on animations now. I can post code if anyone is using this in the Web App!

Link to comment
Share on other sites

 

this lazy loads Vanilla-tilt, and also animates the items in the scroller whe they scroll into the view port.

 

I also slowed down the scroller quiet a bit!

Link to comment
Share on other sites

jachin99

How hard would it be to add all of this to emby theater on my own? Does it use mostly Java and case to create it's views? I would love to have strictly emby clients on every screen. I would go with the web app but I know codec support is limited in a browser and I would like to be able to launch games.

Link to comment
Share on other sites

  • 7 months later...
  • 8 months later...
jachin99

Hello Chef, most of this still works up until I try to add the glare effect.  Cardbuilder.js isn't in *AppData*\Roaming\Emby-Server\system\dashboard-ui\bower_components\emby-webcomponents\cardbuilder\cardBuilder.js .  Instead I found the cardbuilder js file at \AppData\Roaming\Emby-Server\system\dashboard-ui\modules\cardbuilder\cardbuilder.js.  When It tried to modify cardbuilder I couldn't find this line anywhere.  

div.innerHTML = '<div class="card ' + shape + 'Card"><div class="cardBox"><div class="cardScalable"></div></div></div>'
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...