Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
commit b11cb468cc7ab88827fd5994244d066da748bb2a
Author: Jan Paul Posma <jp.posma@brigade.com>
Date: Sat Jun 27 16:51:05 2015 -0700
Add 3d effect to hot loading
I thought it'd be fun to create something to show that the page is
hot loading when making changes, to give us something to look at for
those few seconds that you can't really productively spend otherwise
anyway. This introduces a fun 3d effect to do just that.
It uses socket.io to listen to hot loader events. Only minimal changes
to actual production code is necessary: duplication of a colour to allow
setting a different colour on <html>, a min-height on <body> to render
it properly when zooming out, and passing along the socket.io URL. (I
wanted to set this in the actual code instead of only in
hot_loader_effects.js to prevent us having very different styles when
developing than in production.)
Change-Id: I2ce0535085b82c9607bb0f983eec15b0de8cc36c
Reviewed-on: https://code.brigade.com/1932
Reviewed-by: Joe Lencioni <joe.lencioni@brigade.com>
Reviewed-by: Flarnie Marchan <flarnie.marchan@brigade.com>
Tested-by: Leeroy Jenkins <jenkins@brigade.com>
diff --git a/app/assets/javascripts/_application.js b/app/assets/javascripts/_application.js
index e2feefb..751f637 100644
--- a/app/assets/javascripts/_application.js
+++ b/app/assets/javascripts/_application.js
@@ -93,3 +93,8 @@ pageview.start();
// Workarounds for browser quirks
require('browser_quirks/ios_workarounds');
+
+/* globals HOT_MODULES_URL */
+if (HOT_MODULES_URL) {
+ require('lib/hot_loader_effects')();
+}
diff --git a/app/assets/javascripts/lib/hot_loader_effects.js b/app/assets/javascripts/lib/hot_loader_effects.js
new file mode 100644
index 0000000..7209957
--- /dev/null
+++ b/app/assets/javascripts/lib/hot_loader_effects.js
@@ -0,0 +1,89 @@
+const socketIo = require('webpack-dev-server/node_modules/socket.io-client');
+
+const SCALE_FACTOR = 0.8;
+
+/*
+ * Generates a fun 3d effect while loading new application code, so that you
+ * know when this is happening and when it is done.
+ * Note: legitimises the use of sunglasses when coding.
+ */
+module.exports = function() {
+ /* globals HOT_MODULES_URL */
+ const io = socketIo.connect(HOT_MODULES_URL);
+
+ let turnTimeout, lastRotation;
+ function turnRandomlyAtInterval() {
+ let verticalOffset = 0;
+ if (document.body.offsetHeight > window.innerHeight) {
+ const scrollFraction =
+ window.scrollY / (document.body.offsetHeight - window.innerHeight);
+
+ verticalOffset = (scrollFraction - 0.5) *
+ (1 - SCALE_FACTOR) * document.body.offsetHeight;
+ }
+
+ document.body.parentElement.style.perspective = '1000px';
+ document.body.parentElement.style.perspectiveOrigin =
+ `50% ${window.scrollY + window.innerHeight / 2}px`;
+ document.body.style.transition = 'transform 200ms';
+ document.body.style.transform =
+ `scale(${SCALE_FACTOR}) translateY(${verticalOffset}px)`;
+
+ function turn() {
+ const rotations = [
+ {x: -1, y: 0 },
+ {x: -1, y: -1 },
+ {x: -1, y: 1 },
+ {x: 1, y: 0 },
+ {x: 1, y: -1 },
+ {x: 1, y: 1 },
+ ];
+
+ let rotation;
+ do {
+ rotation = Math.floor(Math.random() * rotations.length);
+ } while (rotation === lastRotation);
+ lastRotation = rotation;
+
+ document.body.style.transition = 'transform 6s';
+ document.body.style.transform =
+ `scale(${SCALE_FACTOR}) ` +
+ `rotateX(${Math.round(rotations[rotation].x * 5)}deg) ` +
+ `rotateY(${Math.round(rotations[rotation].y * 20)}deg) ` +
+ `translateY(${verticalOffset}px)`;
+
+ clearTimeout(turnTimeout);
+ turnTimeout = setTimeout(turn, 5000);
+ }
+ clearTimeout(turnTimeout);
+ turnTimeout = setTimeout(turn, 200);
+ }
+
+ function moveBack() {
+ clearTimeout(turnTimeout);
+ document.body.style.transition = 'transform 100ms';
+ document.body.style.transform = 'none';
+ }
+
+ io.on('invalid', function() {
+ document.body.parentElement.style.background = 'black';
+ turnRandomlyAtInterval();
+ });
+
+ io.on('warnings', function() {
+ document.body.parentElement.style.background = 'crimson';
+ turnRandomlyAtInterval();
+ });
+
+ io.on('errors', function() {
+ document.body.parentElement.style.background = 'crimson';
+ turnRandomlyAtInterval();
+ });
+
+ io.on('still-ok', function() {
+ moveBack();
+ });
+ io.on('ok', function() {
+ moveBack();
+ });
+};
diff --git a/app/assets/stylesheets/layouts/_base.scss b/app/assets/stylesheets/layouts/_base.scss
index 5d8bd2e..184f3f3 100644
--- a/app/assets/stylesheets/layouts/_base.scss
+++ b/app/assets/stylesheets/layouts/_base.scss
@@ -1,16 +1,23 @@
// Styles for the base layout.
+$background-color: #f7f7f5;
+
html {
- background-color: #f7f7f5;
+ background-color: $background-color;
height: 100%;
}
// If you're thinking about changing these styles, make sure it doesn't break
// <ModalContainer>.
body {
+ // Duplicated to make hot_loader_effects look good.
+ background-color: $background-color;
+
// Create a block formatting context
display: inline-block;
+ min-height: 100%;
+
// To avoid positioning surprises when setting position:fixed in
// <ModalContainer>
position: relative;
diff --git a/package.json b/package.json
index f057912..98183d5 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"raw-loader": "^0.5.1",
"react-hot-loader": "^1.2.0",
"sass-loader": "^1.0.2",
+ "socket.io-client": "^1.3.3",
"style-loader": "^0.12.3",
"svgo": "^0.5.2",
"webpack": "^1.2.0",
diff --git a/webpack.config.js b/webpack.config.js
index d3cc160..11cd674 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -34,9 +34,13 @@ var jsLoaders = [
'babel-loader?cacheDirectory=true'
];
+var hot_modules_url;
+
if (process.env.HOT_MODULES) {
+ hot_modules_url = 'http://' + appHost + ':8080';
+
var devServerFiles = [
- 'webpack-dev-server/client?http://' + appHost + ':8080',
+ 'webpack-dev-server/client?' + hot_modules_url,
'webpack/hot/only-dev-server'
];
@@ -142,6 +146,8 @@ module.exports = {
TUNE_IOS_SITE_ID: '99244',
TUNE_ANDROID_SITE_ID: '98790',
TUNE_BASE_URL: "'https://159040.measurementapi.com/serve?'",
+
+ HOT_MODULES_URL: JSON.stringify(hot_modules_url),
}),
new webpack.optimize.CommonsChunkPlugin({
// Use the application entry point as our commons chunk. This means that
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment