Skip to content

Instantly share code, notes, and snippets.

@josiahbryan
Last active February 22, 2019 13:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josiahbryan/d080e2987de26179ec7b1943ee677de7 to your computer and use it in GitHub Desktop.
Save josiahbryan/d080e2987de26179ec7b1943ee677de7 to your computer and use it in GitHub Desktop.
This is a sample "index.html" to for use with Ember. It moves the loading of the compiled "vendor.js" into an animation frame and instead renders a simple splash screen first with a simple CSS loading indicator. Then, once the splash screen is on screen, it starts loading the <scripts> generated by Ember to boot the app. This should be generic e…
<!DOCTYPE html>
<html style='height:100%;overflow:none'>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>My App</title>
<meta name="description" content="My App by My Company, Inc.">
<!-- http://www.favicon-generator.org/ generates a good starting manifest that I tweak by hand -->
<link rel="manifest" href="/manifest.json">
<!-- Set background here to prevent any flashing before the loading screen CSS is interpreted.
I don't set it inline on <body style=""> because that would take precedence over other
stylesheets loaded "in the future" without !important on that background attribute -->
<style>body{background:#0275D8};/* just for loading, overridden by following stylesheets*/</style>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes">
<!-- Apple will use this as the default title of the shortcut when added to home -->
<meta name="apple-mobile-web-app-title" content="My App Name">
<!-- Insert your branding icons here -->
<link rel="apple-touch-icon" sizes="57x57" href="/assets/app-icons/light-bluebg-std/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/assets/app-icons/light-bluebg-std/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/assets/app-icons/light-bluebg-std/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/assets/app-icons/light-bluebg-std/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/assets/app-icons/light-bluebg-std/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/assets/app-icons/light-bluebg-std/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/assets/app-icons/light-bluebg-std/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/assets/app-icons/light-bluebg-std/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/app-icons/light-bluebg-std/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/app-icons/light-bluebg/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/app-icons/light-bluebg/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/assets/app-icons/light-bluebg-std/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="192x192" href="/assets/app-icons/light-bluebg-std/android-icon-192x192.png">
<meta name="msapplication-TileColor" content="#0275D8">
<meta name="msapplication-TileImage" content="/assets/app-icons/light-bluebg-std/ms-icon-144x144.png">
<meta name="theme-color" content="#0275D8">
<!-- Windows Phone -->
<meta name="msapplication-navbutton-color" content="#0275D8">
<!-- iOS Safari -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<!-- Note: I include the CSS inline here in my index.html because I want the page to render as
fast as possible with as few network requests as possible
In production, I use 'ember-cli-html-minifier' to minify the index.html to reduce network
transfer even further.
-->
<!-- Simple loader CSS from https://css-tricks.com/snippets/css/bouncy-animated-loading-animation/
NOTE: Tried using GIF data URIs for more compat, but the browser couldn't render them at all
before the next animation frame kicked off loading scripts and froze the DOM again
-->
<style>
.loader {
text-align: center;
height: 72px;
}
.loader span {
display: inline-block;
vertical-align: middle;
width: 10px;
height: 10px;
margin: 50px auto;
background: white;
border-radius: 50px;
-webkit-animation: loader 0.9s infinite alternate;
-moz-animation: loader 0.9s infinite alternate;
}
.loader span:nth-of-type(2) {
-webkit-animation-delay: 0.3s;
-moz-animation-delay: 0.3s;
}
.loader span:nth-of-type(3) {
-webkit-animation-delay: 0.6s;
-moz-animation-delay: 0.6s;
}
@-webkit-keyframes loader {
0% {
width: 10px;
height: 10px;
opacity: 0.9;
-webkit-transform: translateY(0);
}
100% {
width: 24px;
height: 24px;
opacity: 0.1;
-webkit-transform: translateY(-21px);
}
}
@-moz-keyframes loader {
0% {
width: 10px;
height: 10px;
opacity: 0.9;
-moz-transform: translateY(0);
}
100% {
width: 24px;
height: 24px;
opacity: 0.1;
-moz-transform: translateY(-21px);
}
}
</style>
</head>
<body style='height:100%'>
<div style='position: relative;width:100%;height:100%;background:#0275D8' id='app-splash-screen'>
<div style='position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align:center'>
<div>
<!-- Your brand Icon here -->
<img src="/assets/app-icons/light-bluebg-std/android-icon-192x192.png">
</div>
<div class="loader">
<span></span>
<span></span>
<span></span>
</div>
<!-- You could also add brand name here if desired. This is a system font stack I found somewhere. I ultimately decided I didn't like to clutter the splash screen with it and that my logo as enough. -->
<!-- <div style='font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-style: normal;font-weight:800;color:white;font-size:48px'>
Brand Name
</div>
<div style='font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";font-style: normal;font-weight:600;color:rgba(255,255,255,0.8);margin-top:1em'>
Loading awesomeness ...
<span id='app-loading-spinner'>
<div class="loader">
<span></span>
<span></span>
<span></span>
</div>
</span>
</div>
-->
</div>
</div>
<textarea id="post-splash-head-content" style='display:none'>
{{content-for "head"}}
<!-- Insert other global CSS here, like Google Fonts API links, etc -->
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/filos.css">
{{content-for "head-footer"}}
<!-- Insert other global scripts here, like Google Maps or Font Awesome -->
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/filos.js"></script>
</textarea>
<textarea id="post-splash-body-content" style='display:none'>
{{content-for "body"}}
{{content-for "body-footer"}}
</textarea>
<script>
// Don't start loading the scripts
window.requestAnimationFrame(function() {
// Instead of the CSS spinner above, you could use an ASCII spinner...
// I decided not to use it because it took too long to start animating.
// // Spinners from https://jsfiddle.net/mnpenner/gm86u2jv/
// var spinner = "⣾⣽⣻⢿⡿⣟⣯⣷";
// var el = document.querySelector('#app-loading-spinner');
// (function(spinner,el) {
// var i = 0;
// el.innerHTML = spinner[i];
// setInterval(function() {
// el.innerHTML = spinner[i];
// i = (i + 1) % spinner.length;
// }, 60);
// })(spinner,el);
// Let the spinner get rendered, then start our app bootup
window.requestAnimationFrame(function() {
// Now that the spinner is started, boot up the app
var head = document.getElementsByTagName('head')[0];
var appBoot = document.querySelector('#post-splash-head-content').value;
// Parse the content 'hidden' in the textarea above
var fragment = document.createElement('div');
fragment.id = 'app-boot-container';
fragment.innerHTML = appBoot;
// Simply appending the fragment to the head is enough to get the <meta> and the stylesheets
// recognized and loaded by the browser.
// However, for <script> tags, we have to do a bit more work
head.appendChild(fragment);
// Before we boot the scripts, put our body content on the body
var bodyContent = document.querySelector('#post-splash-body-content').value;
var bodyFragment = document.createElement('div');
bodyFragment.id = 'app-boot-body-extra';
bodyFragment.innerHTML = bodyContent;
document.body.appendChild(bodyFragment);
// Since adding the fragment to head did NOT init scripts (likey due to browser security
// and the browser not wanting to start scripts from raw html), then we have to start
// scripts explicitly ourselves.
// First, we find the scripts in the fragment and make a native javascript array out of them
// because we will use shift() below (which querySelectorAll()'s return of NodeList does not have)
// to load scripts in sequence
var scriptTags = fragment.querySelectorAll('script');
var scripts = [];
// 'forEach' is not present on scriptTags in some browsers, e.g. Mozilla, etc,
// so we loop the old-fashioned way
for(var i=0; i<scriptTags.length; i++) {
scripts.push(scriptTags[i].src);
};
// Load scripts one at a time for predictable execution order.
// If we DON'T wait for one to finish before the other starts to load,
// we potentially (likely) will have errors, because ember and other scripts
// rely on the default nature of browsers loading scripts syncroniously.
// If we just inserted all the scripts all at once into the body,
// the scripts would be loaded asyncroniously, which means smaller scripts
// would finish before big ones, even if loaded after - which causes
// dependency errors (such as define not being defined!)
function loadScript(list) {
if(!list || !list.length)
return;
var src = list.shift();
var script = document.createElement ("script");
script.type = "text/javascript";
document.body.appendChild(script);
script.onload = function() {
loadScript(list);
};
script.src = src;
}
loadScript(scripts);
});
});
</script>
</body>
</html>
@jfrux
Copy link

jfrux commented Feb 22, 2019

What would you say the biggest caveats are to this method?
THinking about using this method for smart-tv's since they are quite limited in resources.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment