this.canvas = canvas} />;
76 | }
77 | }
78 |
79 | export default DesertScene;
80 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/components/Heading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled from 'styled-components';
7 |
8 | const Heading = styled.div`
9 | background-color: #00ce67;
10 | color: white;
11 | font-size: 2.4em;
12 | font-weight: 700;
13 | line-height: 1.2;
14 | margin-bottom: 1em;
15 | margin-top: 0;
16 | padding-bottom: 16px;
17 | padding-top: 16px;
18 | text-align: center;
19 | `;
20 |
21 | export default Heading;
22 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Nav
3 | * It goes where you go
4 | */
5 |
6 | import React from 'react';
7 | import { Link } from 'react-router-dom';
8 | import styled from 'styled-components';
9 |
10 | const Nav = styled.nav`
11 | background-color: white;
12 | box-shadow: 0 2px 4px rgba(0, 8, 16, 0.2);
13 | display: flex;
14 | margin-bottom: 32px;
15 | margin-left: 0;
16 | margin-right: 0;
17 | padding: 32px;
18 | `;
19 |
20 | const NavLink = styled.div`
21 | font-weight: 700;
22 | margin-left: 16px;
23 | margin-right: 16px;
24 |
25 | & a {
26 | color: #00ce67;
27 | text-decoration: none;
28 | transition: color 200ms;
29 |
30 | &:focus,
31 | &:hover {
32 | color: #00b85f;
33 | }
34 | }
35 | `;
36 |
37 | export default () => (
38 |
42 | );
43 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/components/Subheading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled, { keyframes } from 'styled-components';
7 |
8 | const spin = keyframes`
9 | to {
10 | transform: rotate3d(0, 1, 0, 1turn);
11 | }
12 | `;
13 |
14 | const Subheading = styled.div`
15 | animation-duration: 4s;
16 | animation-iteration-count: 300;
17 | animation-name: ${spin};
18 | animation-timing-function: linear;
19 | font-size: 1.5em;
20 | font-style: italic;
21 | font-weight: 400;
22 | letter-spacing: 0.125em;
23 | line-height: 1.2;
24 | margin-bottom: 1em;
25 | margin-top: 0.5em;
26 | text-align: center;
27 | text-transform: uppercase;
28 | `;
29 |
30 | export default Subheading;
31 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Index
3 | * Where it all begins
4 | */
5 |
6 | import React from 'react';
7 | import ReactDOM from 'react-dom';
8 | import {
9 | BrowserRouter as Router,
10 | Route,
11 | Link,
12 | Redirect,
13 | withRouter
14 | } from 'react-router-dom';
15 | import styled, { injectGlobal } from 'styled-components';
16 | import Async from 'react-code-splitting';
17 |
18 | const Nav = () => (
);
21 |
22 | import backgroundImage from './assets/background.svg';
23 |
24 | injectGlobal`
25 | html,
26 | body {
27 | height: 100%;
28 | margin: 0;
29 | }
30 |
31 | html {
32 | background-image: url(${backgroundImage});
33 | background-repeat: repeat;
34 | background-size: auto 256px;
35 | }
36 |
37 | * {
38 | box-sizing: border-box;
39 | }
40 | `;
41 |
42 | const AppStyles = styled.div`
43 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
44 | `;
45 |
46 | const App = () => (
47 |
55 | );
56 |
57 | ReactDOM.render(
58 | React.createElement(App),
59 | document.getElementById('app-root'),
60 | );
61 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/lib/cactusModel.json:
--------------------------------------------------------------------------------
1 | {
2 | "normals":[-0.934004,-0.340305,0.108764,-0.977363,-0.210994,-0.0155688,-0.63695,0.765259,0.0931305,0.293514,-0.730598,-0.616503,-0.504066,0.4132,0.758409,-0.453905,0.490555,0.743859,0.121935,-0.48893,-0.863759,-0.933597,0.147849,-0.3264,0.506743,-0.315187,-0.802414,0.752806,0.638572,-0.159716,0.481829,-0.669727,-0.565072,0.115732,-0.759095,-0.640609,0.976359,0.135217,-0.168639,0.773174,0.490322,0.402226,0.889475,0.181375,0.419449,-0.959097,-0.27885,0.0487405,-0.77183,0.588326,0.241145,0.0190343,-0.984941,-0.171838,0.3048,0.295726,-0.905341,-0.629651,-0.547784,-0.550883,0.898032,0.351972,-0.263922,-0.300827,-0.810674,0.502305,-0.48807,-0.798141,0.353211,-0.421594,0.195019,0.885566,0.678763,-0.710064,-0.187322,-0.705561,0.458006,-0.540753,0.572009,-0.780113,0.253435,0.614787,-0.39639,-0.681845,0.656628,0.070494,-0.750912,-0.757463,-0.040272,0.651634,-0.705348,-0.56378,0.429694,0.184813,0.830189,-0.525956,-0.496781,0.826573,-0.264547,-0.775151,0.606537,0.17679,0.850479,0.322855,0.41527,-0.521119,-0.50555,-0.687644,0.68309,0.0770035,0.726263,-0.452268,0.31448,-0.8346,0.592176,0.66326,-0.457618,-0.583123,0.643889,-0.495353,-0.554658,0.632559,0.540577,0.547432,0.594361,0.589113,0.535557,0.0453709,0.843279,-0.688658,0.212181,0.693347,-0.657859,0.078197,-0.74907,0.673795,0.138285,-0.725863,0.609055,-0.43206,-0.665114,-0.657183,0.0138783,-0.753603,-0.605914,-0.347718,0.715515,-0.95,0.159854,0.268229,-0.496777,-0.447817,-0.74342,0.409124,-0.330661,0.850459,0.519997,-0.132433,0.843839,0.411397,0.0863918,0.907353,-0.929533,-0.16109,-0.331689,-0.808966,0.0196838,-0.587526,0.659916,0.19058,-0.726767,0.808818,0.0547388,-0.585506,0.905146,-0.194485,-0.378002,-0.388926,-0.283165,-0.876672,0.431915,0.141795,0.890699],
3 | "metadata":{
4 | "version":3,
5 | "vertices":45,
6 | "generator":"io_three",
7 | "normals":61,
8 | "faces":61,
9 | "type":"Geometry"
10 | },
11 | "vertices":[-0.300803,5.21962,-2.40982,0.00885725,4.34318,-1.87,-0.272946,3.89615,-0.299987,-0.611808,4.30357,-1.9352,-0.112233,5.25244,-1.3898,0.495805,4.47908,-1.61587,-0.717074,4.75391,-1.43012,0.00819606,2.5532,0.250924,0.081711,4.37467,-0.692596,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,0.535965,3.27878,3.76143,-0.146769,2.17102,3.71399,-0.958925,3.36221,3.36352,-0.605378,2.17641,3.56135,-0.434734,2.4976,2.96216,-0.344565,3.61621,2.75875,-0.139043,4.06988,3.69136,-0.0767336,3.39776,4.13828,0.212165,2.26134,3.32216,-0.102779,2.09515,2.51143,-0.361312,2.3378,1.09194,-0.861599,-0.655066,1.01502,0.304171,-0.577505,0.989137,-0.633588,2.43514,1.37986,-0.773859,3.72084,2.05479,-1.06225,0.635164,0.934365,-1.50921,1.8301,0.51324,-0.51002,5.2504,1.7559,-1.89499,3.60959,0.88243,-1.61025,5.10116,0.801648,-0.435947,6.14863,0.780838,1.19442,4.67411,0.753474,1.18849,3.17018,0.78912,0.0694638,1.56138,0.809495,0.36255,0.592642,0.821591,-1.34921,0.972115,1.59124,-1.09462,-0.525398,1.58873,-0.390343,5.39487,-0.25263,-0.114915,3.57968,-0.670439,-0.298634,0.856904,0.290523,-0.280229,-0.698967,-0.187097,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,-0.361312,2.3378,1.09194],
12 | "faces":[32,6,3,2,0,0,0,33,2,3,7,6,1,1,1,1,32,4,0,6,2,2,2,33,7,3,1,5,3,3,3,3,32,6,7,8,4,4,4,32,6,8,4,5,5,5,32,1,3,0,6,6,6,32,3,6,0,7,7,7,32,0,5,1,8,8,8,32,4,5,0,9,9,9,32,1,5,7,10,10,10,32,1,7,3,11,11,11,32,3,2,7,12,12,12,32,4,8,5,13,13,13,32,8,7,5,14,14,14,32,3,7,6,15,15,15,32,20,21,9,16,16,16,32,20,43,21,17,17,17,32,12,14,18,18,18,18,32,42,43,44,19,19,19,32,14,9,10,20,20,20,32,15,10,20,21,21,21,32,19,15,20,22,22,22,32,16,15,19,23,23,23,32,15,14,10,24,24,24,32,12,11,19,25,25,25,32,17,13,16,26,26,26,32,13,17,18,27,27,27,32,14,13,18,28,28,28,32,16,19,11,29,29,29,32,17,16,11,30,30,30,32,12,9,14,31,31,31,32,12,19,42,32,32,32,32,9,19,20,33,33,33,32,13,14,15,34,34,34,32,11,18,17,35,35,35,32,16,13,15,36,36,36,32,12,18,11,37,37,37,32,32,38,31,38,38,38,32,31,38,30,39,39,39,32,31,30,28,40,40,40,32,28,32,31,41,41,41,33,25,33,32,28,42,42,42,42,33,28,30,29,25,43,43,43,43,33,29,30,38,39,44,44,44,44,33,38,32,33,39,45,45,45,45,32,34,39,33,46,46,46,32,29,39,27,47,47,47,33,25,29,27,24,48,48,48,48,33,24,27,26,36,49,49,49,49,32,27,40,26,50,50,50,33,24,34,33,25,51,51,51,51,32,36,34,24,52,52,52,33,23,35,36,37,53,53,53,53,33,22,37,36,26,54,54,54,54,33,41,22,26,40,55,55,55,55,32,35,40,34,56,56,56,33,23,41,40,35,57,57,57,57,32,39,34,40,58,58,58,32,39,40,27,59,59,59,32,34,36,35,60,60,60]
13 | }
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/views/about.js:
--------------------------------------------------------------------------------
1 | /**
2 | * About
3 | * The mandatory caboose of every website
4 | */
5 |
6 | import React from 'react';
7 |
8 | import Cell from '../components/Cell';
9 |
10 | const About = () => (
11 |
14 | );
15 |
16 | export default About;
17 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/views/countdown.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Countdown
3 | * Hopefully it’s the final one
4 | */
5 |
6 | import React from 'react';
7 | import moment from 'moment';
8 | import styled from 'styled-components';
9 |
10 | import Cell from '../components/Cell';
11 | import Heading from '../components/Heading';
12 | import Subheading from '../components/Subheading';
13 |
14 | const Timer = styled.div`
15 | background-color: white;
16 | border-radius: 12px;
17 | box-shadow: 0 0 0 3px rgb(0, 16, 48);
18 | font-style: italic;
19 | padding: 16px;
20 | text-align: center;
21 | `;
22 |
23 | class Countdown extends React.Component {
24 | constructor(props) {
25 | super(props);
26 | this.tick.bind(this);
27 | }
28 |
29 | componentWillMount() {
30 | this.setState({ daysLeft: '' });
31 | }
32 |
33 | componentDidMount() {
34 | setInterval(() => this.tick(), 1000);
35 | }
36 |
37 | daysUntil2020() {
38 | const then = moment([2020, 1, 1]);
39 | const now = moment();
40 |
41 | const years = then.diff(now, 'years');
42 | now.add(years, 'years');
43 | const months = then.diff(now, 'months');
44 | now.add(months, 'months');
45 | const days = then.diff(now, 'days');
46 | now.add(days, 'days');
47 | const hours = then.diff(now, 'hours');
48 | now.add(hours, 'hours');
49 | const minutes = then.diff(now, 'minutes');
50 | now.add(minutes, 'minutes');
51 | const seconds = then.diff(now, 'seconds');
52 | now.add(seconds, 'seconds');
53 |
54 | return `${years} year${years === 1 ? '' : 's'}, ${months} month${months === 1 ? '' : 's'}, ${days} day${days === 1 ? '' : 's'}, ${hours} hour${hours === 1 ? '' : 's'}, ${minutes} minute${minutes === 1 ? '' : 's'}, ${seconds} second${seconds === 1 ? '' : 's'}`;
55 | }
56 |
57 | tick() {
58 | this.setState({ daysLeft: this.daysUntil2020() });
59 | }
60 |
61 | render() {
62 | return (
63 |
70 | );
71 | }
72 | }
73 |
74 | export default Countdown;
75 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/src/views/home.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Home
3 | * …is where the 🌵 is
4 | */
5 |
6 | import React from 'react';
7 | import styled from 'styled-components';
8 |
9 | import Cell from '../components/Cell';
10 | import DesertScene from '../components/DesertScene';
11 | import Heading from '../components/Heading';
12 |
13 | const ThreeD = styled.div`
14 | background-color: black;
15 | box-shadow: 0 32px 64px rgba(248, 106, 0, 0.5);
16 | margin-bottom: 128px;
17 | margin-left: auto;
18 | margin-right: auto;
19 | max-width: 1024px;
20 | position: relative;
21 | width: calc(100vw - 64px);
22 |
23 | & canvas {
24 | display: block;
25 | width: 100%;
26 | }
27 | `;
28 |
29 | const Title = styled.h1`
30 | bottom: 32px;
31 | color: red;
32 | font-family: cursive;
33 | font-size: 1.25em;
34 | font-style: italic;
35 | line-height: 1;
36 | margin-bottom: 0;
37 | margin-top: 0;
38 | position: absolute;
39 | right: 32px;
40 | z-index: 10;
41 | `;
42 |
43 | class Home extends React.Component {
44 | constructor(props) {
45 | super(props);
46 | }
47 |
48 | componentWillMount() {
49 | this.setState({
50 | height: 544.625,
51 | width: 1024,
52 | });
53 | }
54 |
55 | render() {
56 | return (
57 |
66 | );
67 | }
68 | }
69 |
70 | export default Home;
71 |
--------------------------------------------------------------------------------
/4-deterministic-hashes/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 |
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
6 | const WebpackChunkHash = require('webpack-chunk-hash');
7 |
8 | /* Shared Dev & Production */
9 |
10 | const config = {
11 | context: path.resolve(__dirname, 'src'),
12 |
13 | entry: {
14 | index: './index.js',
15 | },
16 |
17 | module: {
18 | rules: [
19 | {
20 | test: /\.js$/,
21 | use: 'babel-loader',
22 | exclude: /node_modules/,
23 | },
24 | {
25 | test: /\.(jpg|jpeg|gif|png|svg|woff|woff2)$/,
26 | use: {
27 | loader: 'file-loader',
28 | options: { name: '[name].[hash].[ext]' },
29 | },
30 | },
31 | ],
32 | },
33 |
34 | output: {
35 | path: path.resolve(__dirname, 'dist'),
36 | filename: '[name].bundle.js',
37 | publicPath: '/',
38 | },
39 |
40 | resolve: {
41 | extensions: ['.js'],
42 | modules: [path.resolve(__dirname, 'src'), 'node_modules'],
43 | },
44 |
45 | plugins: [
46 | new webpack.optimize.ModuleConcatenationPlugin(),
47 | new HtmlWebpackPlugin({
48 | appMountId: 'app-root',
49 | inlineManifestWebpackName: 'webpackManifest',
50 | template: require('html-webpack-template'),
51 | title: '🌵🏜🌵 Welcome to Cactus World! 🌵🏜🌵',
52 | }),
53 | ],
54 |
55 | devServer: {
56 | historyApiFallback: true,
57 | },
58 | };
59 |
60 | /* Production */
61 |
62 | if (process.env.NODE_ENV === 'production') {
63 | // Use [chunkhash]
64 | config.output.filename = '[name].[chunkhash].js';
65 | // Enable deterministic hashes
66 | // -> https://webpack.js.org/guides/caching/#deterministic-hashes
67 | config.plugins = [
68 | ...config.plugins,
69 | new webpack.HashedModuleIdsPlugin(),
70 | new WebpackChunkHash(),
71 | new ChunkManifestPlugin({
72 | filename: 'chunk-manifest.json',
73 | manifestVariable: 'webpackManifest',
74 | inlineManifest: true,
75 | }),
76 | ];
77 | }
78 |
79 | module.exports = config;
80 |
--------------------------------------------------------------------------------
/5-commons-chunk/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env"],
3 | "plugins": ["syntax-dynamic-import", "transform-react-jsx"]
4 | }
5 |
--------------------------------------------------------------------------------
/5-commons-chunk/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "airbnb"
3 | };
--------------------------------------------------------------------------------
/5-commons-chunk/README.md:
--------------------------------------------------------------------------------
1 | # Commons Chunk
2 |
--------------------------------------------------------------------------------
/5-commons-chunk/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "engines": {
3 | "node": "8.1.4"
4 | },
5 | "scripts": {
6 | "build": "node_modules/.bin/webpack -p",
7 | "dev": "node_modules/.bin/webpack-dev-server"
8 | },
9 | "dependencies": {
10 | "babel-core": "^6.25.0",
11 | "babel-loader": "^7.1.1",
12 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
13 | "babel-plugin-transform-react-jsx": "^6.24.1",
14 | "babel-preset-env": "^1.6.0",
15 | "chunk-manifest-webpack-plugin": "^1.1.0",
16 | "file-loader": "^0.11.2",
17 | "html-webpack-plugin": "^2.29.0",
18 | "html-webpack-template": "^6.0.1",
19 | "moment": "^2.18.1",
20 | "react": "^15.6.1",
21 | "react-code-splitting": "^1.1.1",
22 | "react-dom": "^15.6.1",
23 | "react-router": "^4.1.1",
24 | "react-router-dom": "^4.1.1",
25 | "styled-components": "^2.1.1",
26 | "three": "^0.86.0",
27 | "webpack": "^3.1.0",
28 | "webpack-chunk-hash": "^0.4.0",
29 | "webpack-dev-server": "^2.5.1"
30 | },
31 | "devDependencies": {
32 | "eslint": "^3.19.0",
33 | "eslint-config-airbnb": "^15.0.2",
34 | "eslint-plugin-import": "^2.7.0",
35 | "eslint-plugin-jsx-a11y": "^5.1.1",
36 | "eslint-plugin-react": "^7.1.0"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/browserslist:
--------------------------------------------------------------------------------
1 | > 5%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/components/Cell.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cell
3 | * Width-limiting container
4 | */
5 |
6 | import styled from 'styled-components';
7 |
8 | const Cell = styled.div`
9 | display: block;
10 | margin-left: auto;
11 | margin-right: auto;
12 | max-width: 800px;
13 | padding-left: 16px;
14 | padding-right: 16px;
15 | `;
16 |
17 | export default Cell;
18 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/components/DesertScene.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Desert Scene
3 | * In the desert, you can’t remember your name…
4 | */
5 |
6 | import React from 'react';
7 | import * as THREE from 'three';
8 | import ReactDOM from 'react-dom';
9 |
10 | const cactusModel = require('../lib/cactusModel.json');
11 |
12 | class DesertScene extends React.Component {
13 | constructor(props) {
14 | super(props);
15 | this.paint.bind(this);
16 | }
17 |
18 | componentDidMount() {
19 | const canvasBox = this.canvas.getBoundingClientRect();
20 | const canvasWidth = canvasBox.width;
21 | const canvasHeight = canvasBox.width * 0.4255;
22 |
23 | this.scene = new THREE.Scene();
24 | this.scene.background = new THREE.Color(0xc8faeb);
25 | this.camera = new THREE.PerspectiveCamera(20, canvasWidth/canvasHeight, 0.1, 1000);
26 | this.camera.position.z = 100;
27 | this.renderer = new THREE.WebGLRenderer();
28 | this.renderer.setPixelRatio(window.devicePixelRatio);
29 | this.renderer.setSize(canvasWidth, canvasHeight);
30 | this.canvas.appendChild(this.renderer.domElement);
31 | this.scene.add(this.camera);
32 |
33 | const lights = [];
34 | lights[0] = new THREE.HemisphereLight(0xffffbb, 0xf89316, 1);
35 | this.scene.add(lights[0]);
36 |
37 | // 🌵
38 | const cactusGeometry = new THREE.JSONLoader().parse(cactusModel).geometry;
39 | const cactusMaterial = new THREE.MeshPhongMaterial({ color: 0xA9DCAA, shininess: 50 });
40 | for (var n = 0; n < 50; n += 1) {
41 | const cactus = new THREE.Mesh(cactusGeometry, cactusMaterial);
42 | cactus.position.x = Math.random() * 400 - 200;
43 | cactus.position.z = Math.random() * 400 - 200;
44 | cactus.rotation.y = Math.random() * 2 * Math.PI;
45 | const scalefactor = Math.random() * 4;
46 | cactus.scale.set(scalefactor, scalefactor, scalefactor);
47 | this.scene.add(cactus);
48 | }
49 |
50 | // 🏜
51 | this.desert = new THREE.Mesh(
52 | new THREE.BoxGeometry(500, 0.1, 500),
53 | new THREE.MeshPhongMaterial({ color: 0xe3d9c0 }),
54 | );
55 | this.desert.rotation.x = Math.PI;
56 | this.scene.add(this.desert);
57 | this.paint();
58 | }
59 |
60 | paint(frame) {
61 | const rotationSpeed = frame / 4000;
62 | this.camera.position.set(
63 | 100 * Math.sin(rotationSpeed),
64 | 10,
65 | 100 * Math.cos(rotationSpeed),
66 | );
67 | this.desert.rotation.y += 0.01;
68 | this.camera.lookAt(this.scene.position);
69 |
70 | this.renderer.render(this.scene, this.camera);
71 | window.requestAnimationFrame(nextFrame => this.paint(nextFrame));
72 | }
73 |
74 | render() {
75 | return
this.canvas = canvas} />;
76 | }
77 | }
78 |
79 | export default DesertScene;
80 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/components/Heading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled from 'styled-components';
7 |
8 | const Heading = styled.div`
9 | background-color: #00ce67;
10 | color: white;
11 | font-size: 2.4em;
12 | font-weight: 700;
13 | line-height: 1.2;
14 | margin-bottom: 1em;
15 | margin-top: 0;
16 | padding-bottom: 16px;
17 | padding-top: 16px;
18 | text-align: center;
19 | `;
20 |
21 | export default Heading;
22 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Nav
3 | * It goes where you go
4 | */
5 |
6 | import React from 'react';
7 | import { Link } from 'react-router-dom';
8 | import styled from 'styled-components';
9 |
10 | const Nav = styled.nav`
11 | background-color: white;
12 | box-shadow: 0 2px 4px rgba(0, 8, 16, 0.2);
13 | display: flex;
14 | margin-bottom: 32px;
15 | margin-left: 0;
16 | margin-right: 0;
17 | padding: 32px;
18 | `;
19 |
20 | const NavLink = styled.div`
21 | font-weight: 700;
22 | margin-left: 16px;
23 | margin-right: 16px;
24 |
25 | & a {
26 | color: #00ce67;
27 | text-decoration: none;
28 | transition: color 200ms;
29 |
30 | &:focus,
31 | &:hover {
32 | color: #00b85f;
33 | }
34 | }
35 | `;
36 |
37 | export default () => (
38 |
42 | );
43 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/components/Subheading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled, { keyframes } from 'styled-components';
7 |
8 | const spin = keyframes`
9 | to {
10 | transform: rotate3d(0, 1, 0, 1turn);
11 | }
12 | `;
13 |
14 | const Subheading = styled.div`
15 | animation-duration: 4s;
16 | animation-iteration-count: 300;
17 | animation-name: ${spin};
18 | animation-timing-function: linear;
19 | font-size: 1.5em;
20 | font-style: italic;
21 | font-weight: 400;
22 | letter-spacing: 0.125em;
23 | line-height: 1.2;
24 | margin-bottom: 1em;
25 | margin-top: 0.5em;
26 | text-align: center;
27 | text-transform: uppercase;
28 | `;
29 |
30 | export default Subheading;
31 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Index
3 | * Where it all begins
4 | */
5 |
6 | import React from 'react';
7 | import ReactDOM from 'react-dom';
8 | import {
9 | BrowserRouter as Router,
10 | Route,
11 | Link,
12 | Redirect,
13 | withRouter
14 | } from 'react-router-dom';
15 | import styled, { injectGlobal } from 'styled-components';
16 | import Async from 'react-code-splitting';
17 |
18 | const Nav = () => (
);
19 | const Home = () => (
);
20 | const Countdown = () => (
);
21 |
22 | import backgroundImage from './assets/background.svg';
23 |
24 | injectGlobal`
25 | html,
26 | body {
27 | height: 100%;
28 | margin: 0;
29 | }
30 |
31 | html {
32 | background-image: url(${backgroundImage});
33 | background-repeat: repeat;
34 | background-size: auto 256px;
35 | }
36 |
37 | * {
38 | box-sizing: border-box;
39 | }
40 | `;
41 |
42 | const AppStyles = styled.div`
43 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
44 | `;
45 |
46 | const App = () => (
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | );
56 |
57 | ReactDOM.render(
58 | React.createElement(App),
59 | document.getElementById('app-root'),
60 | );
61 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/lib/SkyShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author zz85 / https://github.com/zz85
3 | *
4 | * Based on "A Practical Analytic Model for Daylight"
5 | * aka The Preetham Model, the de facto standard analytic skydome model
6 | * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
7 | *
8 | * First implemented by Simon Wallner
9 | * http://www.simonwallner.at/projects/atmospheric-scattering
10 | *
11 | * Improved by Martin Upitis
12 | * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
13 | *
14 | * Three.js integration by zz85 http://twitter.com/blurspline
15 | */
16 |
17 | import * as THREE from 'three';
18 |
19 | THREE.Sky = function () {
20 |
21 | var skyShader = THREE.Sky.SkyShader;
22 |
23 | var skyUniforms = THREE.UniformsUtils.clone( skyShader.uniforms );
24 |
25 | var skyMat = new THREE.ShaderMaterial( {
26 | fragmentShader: skyShader.fragmentShader,
27 | vertexShader: skyShader.vertexShader,
28 | uniforms: skyUniforms,
29 | side: THREE.BackSide
30 | } );
31 |
32 | var skyGeo = new THREE.SphereBufferGeometry( 450000, 32, 15 );
33 | var skyMesh = new THREE.Mesh( skyGeo, skyMat );
34 |
35 | // Expose variables
36 | this.mesh = skyMesh;
37 | this.uniforms = skyUniforms;
38 |
39 | };
40 |
41 | THREE.Sky.SkyShader = {
42 |
43 | uniforms: {
44 | luminance: { value: 1 },
45 | turbidity: { value: 2 },
46 | rayleigh: { value: 1 },
47 | mieCoefficient: { value: 0.005 },
48 | mieDirectionalG: { value: 0.8 },
49 | sunPosition: { value: new THREE.Vector3() }
50 | },
51 |
52 | vertexShader: [
53 | 'uniform vec3 sunPosition;',
54 | 'uniform float rayleigh;',
55 | 'uniform float turbidity;',
56 | 'uniform float mieCoefficient;',
57 |
58 | 'varying vec3 vWorldPosition;',
59 | 'varying vec3 vSunDirection;',
60 | 'varying float vSunfade;',
61 | 'varying vec3 vBetaR;',
62 | 'varying vec3 vBetaM;',
63 | 'varying float vSunE;',
64 |
65 | 'const vec3 up = vec3( 0.0, 1.0, 0.0 );',
66 |
67 | // constants for atmospheric scattering
68 | 'const float e = 2.71828182845904523536028747135266249775724709369995957;',
69 | 'const float pi = 3.141592653589793238462643383279502884197169;',
70 |
71 | // wavelength of used primaries, according to preetham
72 | 'const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 );',
73 | // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function:
74 | // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn))
75 | 'const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 );',
76 |
77 | // mie stuff
78 | // K coefficient for the primaries
79 | 'const float v = 4.0;',
80 | 'const vec3 K = vec3( 0.686, 0.678, 0.666 );',
81 | // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K
82 | 'const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 );',
83 |
84 | // earth shadow hack
85 | // cutoffAngle = pi / 1.95;
86 | 'const float cutoffAngle = 1.6110731556870734;',
87 | 'const float steepness = 1.5;',
88 | 'const float EE = 1000.0;',
89 |
90 | 'float sunIntensity( float zenithAngleCos ) {',
91 | ' zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 );',
92 | ' return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) );',
93 | '}',
94 |
95 | 'vec3 totalMie( float T ) {',
96 | ' float c = ( 0.2 * T ) * 10E-18;',
97 | ' return 0.434 * c * MieConst;',
98 | '}',
99 |
100 | 'void main() {',
101 |
102 | ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
103 | ' vWorldPosition = worldPosition.xyz;',
104 |
105 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
106 |
107 | ' vSunDirection = normalize( sunPosition );',
108 |
109 | ' vSunE = sunIntensity( dot( vSunDirection, up ) );',
110 |
111 | ' vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 );',
112 |
113 | ' float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) );',
114 |
115 | // extinction (absorbtion + out scattering)
116 | // rayleigh coefficients
117 | ' vBetaR = totalRayleigh * rayleighCoefficient;',
118 |
119 | // mie coefficients
120 | ' vBetaM = totalMie( turbidity ) * mieCoefficient;',
121 |
122 | '}'
123 | ].join( '\n' ),
124 |
125 | fragmentShader: [
126 | 'varying vec3 vWorldPosition;',
127 | 'varying vec3 vSunDirection;',
128 | 'varying float vSunfade;',
129 | 'varying vec3 vBetaR;',
130 | 'varying vec3 vBetaM;',
131 | 'varying float vSunE;',
132 |
133 | 'uniform float luminance;',
134 | 'uniform float mieDirectionalG;',
135 |
136 | 'const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 );',
137 |
138 | // constants for atmospheric scattering
139 | 'const float pi = 3.141592653589793238462643383279502884197169;',
140 |
141 | 'const float n = 1.0003;', // refractive index of air
142 | 'const float N = 2.545E25;', // number of molecules per unit volume for air at
143 | // 288.15K and 1013mb (sea level -45 celsius)
144 |
145 | // optical length at zenith for molecules
146 | 'const float rayleighZenithLength = 8.4E3;',
147 | 'const float mieZenithLength = 1.25E3;',
148 | 'const vec3 up = vec3( 0.0, 1.0, 0.0 );',
149 | // 66 arc seconds -> degrees, and the cosine of that
150 | 'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;',
151 |
152 | // 3.0 / ( 16.0 * pi )
153 | 'const float THREE_OVER_SIXTEENPI = 0.05968310365946075;',
154 | // 1.0 / ( 4.0 * pi )
155 | 'const float ONE_OVER_FOURPI = 0.07957747154594767;',
156 |
157 | 'float rayleighPhase( float cosTheta ) {',
158 | ' return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) );',
159 | '}',
160 |
161 | 'float hgPhase( float cosTheta, float g ) {',
162 | ' float g2 = pow( g, 2.0 );',
163 | ' float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 );',
164 | ' return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse );',
165 | '}',
166 |
167 | // Filmic ToneMapping http://filmicgames.com/archives/75
168 | 'const float A = 0.15;',
169 | 'const float B = 0.50;',
170 | 'const float C = 0.10;',
171 | 'const float D = 0.20;',
172 | 'const float E = 0.02;',
173 | 'const float F = 0.30;',
174 |
175 | 'const float whiteScale = 1.0748724675633854;', // 1.0 / Uncharted2Tonemap(1000.0)
176 |
177 | 'vec3 Uncharted2Tonemap( vec3 x ) {',
178 | ' return ( ( x * ( A * x + C * B ) + D * E ) / ( x * ( A * x + B ) + D * F ) ) - E / F;',
179 | '}',
180 |
181 |
182 | 'void main() {',
183 | // optical length
184 | // cutoff angle at 90 to avoid singularity in next formula.
185 | ' float zenithAngle = acos( max( 0.0, dot( up, normalize( vWorldPosition - cameraPos ) ) ) );',
186 | ' float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) );',
187 | ' float sR = rayleighZenithLength * inverse;',
188 | ' float sM = mieZenithLength * inverse;',
189 |
190 | // combined extinction factor
191 | ' vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) );',
192 |
193 | // in scattering
194 | ' float cosTheta = dot( normalize( vWorldPosition - cameraPos ), vSunDirection );',
195 |
196 | ' float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 );',
197 | ' vec3 betaRTheta = vBetaR * rPhase;',
198 |
199 | ' float mPhase = hgPhase( cosTheta, mieDirectionalG );',
200 | ' vec3 betaMTheta = vBetaM * mPhase;',
201 |
202 | ' vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) );',
203 | ' Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) );',
204 |
205 | // nightsky
206 | ' vec3 direction = normalize( vWorldPosition - cameraPos );',
207 | ' float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2]',
208 | ' float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2]',
209 | ' vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 );',
210 | ' vec3 L0 = vec3( 0.1 ) * Fex;',
211 |
212 | // composition + solar disc
213 | ' float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );',
214 | ' L0 += ( vSunE * 19000.0 * Fex ) * sundisk;',
215 |
216 | ' vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );',
217 |
218 | ' vec3 curr = Uncharted2Tonemap( ( log2( 2.0 / pow( luminance, 4.0 ) ) ) * texColor );',
219 | ' vec3 color = curr * whiteScale;',
220 |
221 | ' vec3 retColor = pow( color, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );',
222 |
223 | ' gl_FragColor = vec4( retColor, 1.0 );',
224 |
225 | '}'
226 | ].join( '\n' )
227 |
228 | };
229 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/lib/cactusModel.json:
--------------------------------------------------------------------------------
1 | {
2 | "normals":[-0.934004,-0.340305,0.108764,-0.977363,-0.210994,-0.0155688,-0.63695,0.765259,0.0931305,0.293514,-0.730598,-0.616503,-0.504066,0.4132,0.758409,-0.453905,0.490555,0.743859,0.121935,-0.48893,-0.863759,-0.933597,0.147849,-0.3264,0.506743,-0.315187,-0.802414,0.752806,0.638572,-0.159716,0.481829,-0.669727,-0.565072,0.115732,-0.759095,-0.640609,0.976359,0.135217,-0.168639,0.773174,0.490322,0.402226,0.889475,0.181375,0.419449,-0.959097,-0.27885,0.0487405,-0.77183,0.588326,0.241145,0.0190343,-0.984941,-0.171838,0.3048,0.295726,-0.905341,-0.629651,-0.547784,-0.550883,0.898032,0.351972,-0.263922,-0.300827,-0.810674,0.502305,-0.48807,-0.798141,0.353211,-0.421594,0.195019,0.885566,0.678763,-0.710064,-0.187322,-0.705561,0.458006,-0.540753,0.572009,-0.780113,0.253435,0.614787,-0.39639,-0.681845,0.656628,0.070494,-0.750912,-0.757463,-0.040272,0.651634,-0.705348,-0.56378,0.429694,0.184813,0.830189,-0.525956,-0.496781,0.826573,-0.264547,-0.775151,0.606537,0.17679,0.850479,0.322855,0.41527,-0.521119,-0.50555,-0.687644,0.68309,0.0770035,0.726263,-0.452268,0.31448,-0.8346,0.592176,0.66326,-0.457618,-0.583123,0.643889,-0.495353,-0.554658,0.632559,0.540577,0.547432,0.594361,0.589113,0.535557,0.0453709,0.843279,-0.688658,0.212181,0.693347,-0.657859,0.078197,-0.74907,0.673795,0.138285,-0.725863,0.609055,-0.43206,-0.665114,-0.657183,0.0138783,-0.753603,-0.605914,-0.347718,0.715515,-0.95,0.159854,0.268229,-0.496777,-0.447817,-0.74342,0.409124,-0.330661,0.850459,0.519997,-0.132433,0.843839,0.411397,0.0863918,0.907353,-0.929533,-0.16109,-0.331689,-0.808966,0.0196838,-0.587526,0.659916,0.19058,-0.726767,0.808818,0.0547388,-0.585506,0.905146,-0.194485,-0.378002,-0.388926,-0.283165,-0.876672,0.431915,0.141795,0.890699],
3 | "metadata":{
4 | "version":3,
5 | "vertices":45,
6 | "generator":"io_three",
7 | "normals":61,
8 | "faces":61,
9 | "type":"Geometry"
10 | },
11 | "vertices":[-0.300803,5.21962,-2.40982,0.00885725,4.34318,-1.87,-0.272946,3.89615,-0.299987,-0.611808,4.30357,-1.9352,-0.112233,5.25244,-1.3898,0.495805,4.47908,-1.61587,-0.717074,4.75391,-1.43012,0.00819606,2.5532,0.250924,0.081711,4.37467,-0.692596,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,0.535965,3.27878,3.76143,-0.146769,2.17102,3.71399,-0.958925,3.36221,3.36352,-0.605378,2.17641,3.56135,-0.434734,2.4976,2.96216,-0.344565,3.61621,2.75875,-0.139043,4.06988,3.69136,-0.0767336,3.39776,4.13828,0.212165,2.26134,3.32216,-0.102779,2.09515,2.51143,-0.361312,2.3378,1.09194,-0.861599,-0.655066,1.01502,0.304171,-0.577505,0.989137,-0.633588,2.43514,1.37986,-0.773859,3.72084,2.05479,-1.06225,0.635164,0.934365,-1.50921,1.8301,0.51324,-0.51002,5.2504,1.7559,-1.89499,3.60959,0.88243,-1.61025,5.10116,0.801648,-0.435947,6.14863,0.780838,1.19442,4.67411,0.753474,1.18849,3.17018,0.78912,0.0694638,1.56138,0.809495,0.36255,0.592642,0.821591,-1.34921,0.972115,1.59124,-1.09462,-0.525398,1.58873,-0.390343,5.39487,-0.25263,-0.114915,3.57968,-0.670439,-0.298634,0.856904,0.290523,-0.280229,-0.698967,-0.187097,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,-0.361312,2.3378,1.09194],
12 | "faces":[32,6,3,2,0,0,0,33,2,3,7,6,1,1,1,1,32,4,0,6,2,2,2,33,7,3,1,5,3,3,3,3,32,6,7,8,4,4,4,32,6,8,4,5,5,5,32,1,3,0,6,6,6,32,3,6,0,7,7,7,32,0,5,1,8,8,8,32,4,5,0,9,9,9,32,1,5,7,10,10,10,32,1,7,3,11,11,11,32,3,2,7,12,12,12,32,4,8,5,13,13,13,32,8,7,5,14,14,14,32,3,7,6,15,15,15,32,20,21,9,16,16,16,32,20,43,21,17,17,17,32,12,14,18,18,18,18,32,42,43,44,19,19,19,32,14,9,10,20,20,20,32,15,10,20,21,21,21,32,19,15,20,22,22,22,32,16,15,19,23,23,23,32,15,14,10,24,24,24,32,12,11,19,25,25,25,32,17,13,16,26,26,26,32,13,17,18,27,27,27,32,14,13,18,28,28,28,32,16,19,11,29,29,29,32,17,16,11,30,30,30,32,12,9,14,31,31,31,32,12,19,42,32,32,32,32,9,19,20,33,33,33,32,13,14,15,34,34,34,32,11,18,17,35,35,35,32,16,13,15,36,36,36,32,12,18,11,37,37,37,32,32,38,31,38,38,38,32,31,38,30,39,39,39,32,31,30,28,40,40,40,32,28,32,31,41,41,41,33,25,33,32,28,42,42,42,42,33,28,30,29,25,43,43,43,43,33,29,30,38,39,44,44,44,44,33,38,32,33,39,45,45,45,45,32,34,39,33,46,46,46,32,29,39,27,47,47,47,33,25,29,27,24,48,48,48,48,33,24,27,26,36,49,49,49,49,32,27,40,26,50,50,50,33,24,34,33,25,51,51,51,51,32,36,34,24,52,52,52,33,23,35,36,37,53,53,53,53,33,22,37,36,26,54,54,54,54,33,41,22,26,40,55,55,55,55,32,35,40,34,56,56,56,33,23,41,40,35,57,57,57,57,32,39,34,40,58,58,58,32,39,40,27,59,59,59,32,34,36,35,60,60,60]
13 | }
--------------------------------------------------------------------------------
/5-commons-chunk/src/views/about.js:
--------------------------------------------------------------------------------
1 | /**
2 | * About
3 | * The mandatory caboose of every website
4 | */
5 |
6 | import React from 'react';
7 |
8 | import Cell from '../components/Cell';
9 |
10 | const About = () => (
11 |
12 | About
13 | |
14 | );
15 |
16 | export default About;
17 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/views/countdown.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Countdown
3 | * Hopefully it’s the final one
4 | */
5 |
6 | import React from 'react';
7 | import moment from 'moment';
8 | import styled from 'styled-components';
9 |
10 | import Cell from '../components/Cell';
11 | import Heading from '../components/Heading';
12 | import Subheading from '../components/Subheading';
13 |
14 | const Timer = styled.div`
15 | background-color: white;
16 | border-radius: 12px;
17 | box-shadow: 0 0 0 3px rgb(0, 16, 48);
18 | font-style: italic;
19 | padding: 16px;
20 | text-align: center;
21 | `;
22 |
23 | class Countdown extends React.Component {
24 | constructor(props) {
25 | super(props);
26 | this.tick.bind(this);
27 | }
28 |
29 | componentWillMount() {
30 | this.setState({ daysLeft: '' });
31 | }
32 |
33 | componentDidMount() {
34 | setInterval(() => this.tick(), 1000);
35 | }
36 |
37 | daysUntil2020() {
38 | const then = moment([2020, 1, 1]);
39 | const now = moment();
40 |
41 | const years = then.diff(now, 'years');
42 | now.add(years, 'years');
43 | const months = then.diff(now, 'months');
44 | now.add(months, 'months');
45 | const days = then.diff(now, 'days');
46 | now.add(days, 'days');
47 | const hours = then.diff(now, 'hours');
48 | now.add(hours, 'hours');
49 | const minutes = then.diff(now, 'minutes');
50 | now.add(minutes, 'minutes');
51 | const seconds = then.diff(now, 'seconds');
52 | now.add(seconds, 'seconds');
53 |
54 | return `${years} year${years === 1 ? '' : 's'}, ${months} month${months === 1 ? '' : 's'}, ${days} day${days === 1 ? '' : 's'}, ${hours} hour${hours === 1 ? '' : 's'}, ${minutes} minute${minutes === 1 ? '' : 's'}, ${seconds} second${seconds === 1 ? '' : 's'}`;
55 | }
56 |
57 | tick() {
58 | this.setState({ daysLeft: this.daysUntil2020() });
59 | }
60 |
61 | render() {
62 | return (
63 |
64 | Grand Opening
65 | 🎉 Opens Jan 1, 2020! 🎉
66 |
67 | {this.state.daysLeft}
68 |
69 | |
70 | );
71 | }
72 | }
73 |
74 | export default Countdown;
75 |
--------------------------------------------------------------------------------
/5-commons-chunk/src/views/home.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Home
3 | * …is where the 🌵 is
4 | */
5 |
6 | import React from 'react';
7 | import styled from 'styled-components';
8 |
9 | import Cell from '../components/Cell';
10 | import DesertScene from '../components/DesertScene';
11 | import Heading from '../components/Heading';
12 |
13 | const ThreeD = styled.div`
14 | background-color: black;
15 | box-shadow: 0 32px 64px rgba(248, 106, 0, 0.5);
16 | margin-bottom: 128px;
17 | margin-left: auto;
18 | margin-right: auto;
19 | max-width: 1024px;
20 | position: relative;
21 | width: calc(100vw - 64px);
22 |
23 | & canvas {
24 | display: block;
25 | width: 100%;
26 | }
27 | `;
28 |
29 | const Title = styled.h1`
30 | bottom: 32px;
31 | color: red;
32 | font-family: cursive;
33 | font-size: 1.25em;
34 | font-style: italic;
35 | line-height: 1;
36 | margin-bottom: 0;
37 | margin-top: 0;
38 | position: absolute;
39 | right: 32px;
40 | z-index: 10;
41 | `;
42 |
43 | class Home extends React.Component {
44 | constructor(props) {
45 | super(props);
46 | }
47 |
48 | componentWillMount() {
49 | this.setState({
50 | height: 544.625,
51 | width: 1024,
52 | });
53 | }
54 |
55 | render() {
56 | return (
57 |
58 |
59 | 🌵🏜🌵 Welcome to Cactus World! 🌵🏜🌵
60 | |
61 |
62 |
63 | Artist’s Rendering
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | export default Home;
71 |
--------------------------------------------------------------------------------
/5-commons-chunk/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 |
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
6 | const WebpackChunkHash = require('webpack-chunk-hash');
7 |
8 | /* Shared Dev & Production */
9 |
10 | const config = {
11 | context: path.resolve(__dirname, 'src'),
12 |
13 | entry: {
14 | index: './index.js',
15 | vendor: ['react', 'react-dom', 'react-router'],
16 | },
17 |
18 | module: {
19 | rules: [
20 | {
21 | test: /\.js$/,
22 | use: 'babel-loader',
23 | exclude: /node_modules/,
24 | },
25 | {
26 | test: /\.(jpg|jpeg|gif|png|svg|woff|woff2)$/,
27 | use: {
28 | loader: 'file-loader',
29 | options: { name: '[name].[hash].[ext]' },
30 | },
31 | },
32 | ],
33 | },
34 |
35 | output: {
36 | path: path.resolve(__dirname, 'dist'),
37 | filename: '[name].bundle.js',
38 | publicPath: '/',
39 | },
40 |
41 | resolve: {
42 | extensions: ['.js'],
43 | modules: [path.resolve(__dirname, 'src'), 'node_modules'],
44 | },
45 |
46 | plugins: [
47 | // Use 'vendor' bundle as global commons chunk
48 | new webpack.optimize.CommonsChunkPlugin({
49 | name: 'vendor',
50 | }),
51 | new webpack.optimize.ModuleConcatenationPlugin(),
52 | new HtmlWebpackPlugin({
53 | appMountId: 'app-root',
54 | inlineManifestWebpackName: 'webpackManifest',
55 | template: require('html-webpack-template'),
56 | title: '🌵🏜🌵 Welcome to Cactus World! 🌵🏜🌵',
57 | }),
58 | ],
59 |
60 | devServer: {
61 | historyApiFallback: true,
62 | },
63 | };
64 |
65 | if (process.env.NODE_ENV === 'production') {
66 | config.output.filename = '[name].[chunkhash].js';
67 | config.plugins = [
68 | ...config.plugins,
69 | new webpack.HashedModuleIdsPlugin(),
70 | new WebpackChunkHash(),
71 | new ChunkManifestPlugin({
72 | filename: 'chunk-manifest.json',
73 | manifestVariable: 'webpackManifest',
74 | inlineManifest: true,
75 | }),
76 | ];
77 | }
78 |
79 | module.exports = config;
80 |
--------------------------------------------------------------------------------
/6-offline-plugin/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env"],
3 | "plugins": ["syntax-dynamic-import", "transform-react-jsx"]
4 | }
5 |
--------------------------------------------------------------------------------
/6-offline-plugin/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "airbnb"
3 | };
--------------------------------------------------------------------------------
/6-offline-plugin/README.md:
--------------------------------------------------------------------------------
1 | # Offline Plugin
2 |
3 | Take advantage of webpack’s [offline plugin](https://github.com/NekR/offline-plugin).
4 |
5 | ## Installation
6 |
7 | See the plugin’s [README](https://github.com/NekR/offline-plugin).
8 |
--------------------------------------------------------------------------------
/6-offline-plugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "engines": {
3 | "node": "8.1.4"
4 | },
5 | "scripts": {
6 | "build": "node_modules/.bin/webpack -p",
7 | "dev": "node_modules/.bin/webpack-dev-server"
8 | },
9 | "dependencies": {
10 | "babel-core": "^6.25.0",
11 | "babel-loader": "^7.1.1",
12 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
13 | "babel-plugin-transform-react-jsx": "^6.24.1",
14 | "babel-preset-env": "^1.6.0",
15 | "chunk-manifest-webpack-plugin": "^1.1.0",
16 | "file-loader": "^0.11.2",
17 | "html-webpack-plugin": "^2.29.0",
18 | "html-webpack-template": "^6.0.1",
19 | "moment": "^2.18.1",
20 | "offline-plugin": "^4.8.3",
21 | "react": "^15.6.1",
22 | "react-code-splitting": "^1.1.1",
23 | "react-dom": "^15.6.1",
24 | "react-router": "^4.1.1",
25 | "react-router-dom": "^4.1.1",
26 | "styled-components": "^2.1.1",
27 | "three": "^0.86.0",
28 | "webpack": "^3.1.0",
29 | "webpack-chunk-hash": "^0.4.0",
30 | "webpack-dev-server": "^2.5.1"
31 | },
32 | "devDependencies": {
33 | "eslint": "^3.19.0",
34 | "eslint-config-airbnb": "^15.0.2",
35 | "eslint-plugin-import": "^2.7.0",
36 | "eslint-plugin-jsx-a11y": "^5.1.1",
37 | "eslint-plugin-react": "^7.1.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/browserslist:
--------------------------------------------------------------------------------
1 | > 5%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/components/Cell.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cell
3 | * Width-limiting container
4 | */
5 |
6 | import styled from 'styled-components';
7 |
8 | const Cell = styled.div`
9 | display: block;
10 | margin-left: auto;
11 | margin-right: auto;
12 | max-width: 800px;
13 | padding-left: 16px;
14 | padding-right: 16px;
15 | `;
16 |
17 | export default Cell;
18 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/components/DesertScene.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Desert Scene
3 | * In the desert, you can’t remember your name…
4 | */
5 |
6 | import React from 'react';
7 | import * as THREE from 'three';
8 | import ReactDOM from 'react-dom';
9 |
10 | const cactusModel = require('../lib/cactusModel.json');
11 |
12 | class DesertScene extends React.Component {
13 | constructor(props) {
14 | super(props);
15 | this.paint.bind(this);
16 | }
17 |
18 | componentDidMount() {
19 | const canvasBox = this.canvas.getBoundingClientRect();
20 | const canvasWidth = canvasBox.width;
21 | const canvasHeight = canvasBox.width * 0.4255;
22 |
23 | this.scene = new THREE.Scene();
24 | this.scene.background = new THREE.Color(0xc8faeb);
25 | this.camera = new THREE.PerspectiveCamera(20, canvasWidth/canvasHeight, 0.1, 1000);
26 | this.camera.position.z = 100;
27 | this.renderer = new THREE.WebGLRenderer();
28 | this.renderer.setPixelRatio(window.devicePixelRatio);
29 | this.renderer.setSize(canvasWidth, canvasHeight);
30 | this.canvas.appendChild(this.renderer.domElement);
31 | this.scene.add(this.camera);
32 |
33 | const lights = [];
34 | lights[0] = new THREE.HemisphereLight(0xffffbb, 0xf89316, 1);
35 | this.scene.add(lights[0]);
36 |
37 | // 🌵
38 | const cactusGeometry = new THREE.JSONLoader().parse(cactusModel).geometry;
39 | const cactusMaterial = new THREE.MeshPhongMaterial({ color: 0xA9DCAA, shininess: 50 });
40 | for (var n = 0; n < 50; n += 1) {
41 | const cactus = new THREE.Mesh(cactusGeometry, cactusMaterial);
42 | cactus.position.x = Math.random() * 400 - 200;
43 | cactus.position.z = Math.random() * 400 - 200;
44 | cactus.rotation.y = Math.random() * 2 * Math.PI;
45 | const scalefactor = Math.random() * 4;
46 | cactus.scale.set(scalefactor, scalefactor, scalefactor);
47 | this.scene.add(cactus);
48 | }
49 |
50 | // 🏜
51 | this.desert = new THREE.Mesh(
52 | new THREE.BoxGeometry(500, 0.1, 500),
53 | new THREE.MeshPhongMaterial({ color: 0xe3d9c0 }),
54 | );
55 | this.desert.rotation.x = Math.PI;
56 | this.scene.add(this.desert);
57 | this.paint();
58 | }
59 |
60 | paint(frame) {
61 | const rotationSpeed = frame / 4000;
62 | this.camera.position.set(
63 | 100 * Math.sin(rotationSpeed),
64 | 10,
65 | 100 * Math.cos(rotationSpeed),
66 | );
67 | this.desert.rotation.y += 0.01;
68 | this.camera.lookAt(this.scene.position);
69 |
70 | this.renderer.render(this.scene, this.camera);
71 | window.requestAnimationFrame(nextFrame => this.paint(nextFrame));
72 | }
73 |
74 | render() {
75 | return
this.canvas = canvas} />;
76 | }
77 | }
78 |
79 | export default DesertScene;
80 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/components/Heading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled from 'styled-components';
7 |
8 | const Heading = styled.div`
9 | background-color: #00ce67;
10 | color: white;
11 | font-size: 2.4em;
12 | font-weight: 700;
13 | line-height: 1.2;
14 | margin-bottom: 1em;
15 | margin-top: 0;
16 | padding-bottom: 16px;
17 | padding-top: 16px;
18 | text-align: center;
19 | `;
20 |
21 | export default Heading;
22 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Nav
3 | * It goes where you go
4 | */
5 |
6 | import React from 'react';
7 | import { Link } from 'react-router-dom';
8 | import styled from 'styled-components';
9 |
10 | const Nav = styled.nav`
11 | background-color: white;
12 | box-shadow: 0 2px 4px rgba(0, 8, 16, 0.2);
13 | display: flex;
14 | margin-bottom: 32px;
15 | margin-left: 0;
16 | margin-right: 0;
17 | padding: 32px;
18 | `;
19 |
20 | const NavLink = styled.div`
21 | font-weight: 700;
22 | margin-left: 16px;
23 | margin-right: 16px;
24 |
25 | & a {
26 | color: #00ce67;
27 | text-decoration: none;
28 | transition: color 200ms;
29 |
30 | &:focus,
31 | &:hover {
32 | color: #00b85f;
33 | }
34 | }
35 | `;
36 |
37 | export default () => (
38 |
42 | );
43 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/components/Subheading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled, { keyframes } from 'styled-components';
7 |
8 | const spin = keyframes`
9 | to {
10 | transform: rotate3d(0, 1, 0, 1turn);
11 | }
12 | `;
13 |
14 | const Subheading = styled.div`
15 | animation-duration: 4s;
16 | animation-iteration-count: 300;
17 | animation-name: ${spin};
18 | animation-timing-function: linear;
19 | font-size: 1.5em;
20 | font-style: italic;
21 | font-weight: 400;
22 | letter-spacing: 0.125em;
23 | line-height: 1.2;
24 | margin-bottom: 1em;
25 | margin-top: 0.5em;
26 | text-align: center;
27 | text-transform: uppercase;
28 | `;
29 |
30 | export default Subheading;
31 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Index
3 | * Where it all begins
4 | */
5 |
6 | import React from 'react';
7 | import ReactDOM from 'react-dom';
8 | import {
9 | BrowserRouter as Router,
10 | Route,
11 | Link,
12 | Redirect,
13 | withRouter
14 | } from 'react-router-dom';
15 | import styled, { injectGlobal } from 'styled-components';
16 | import Async from 'react-code-splitting';
17 |
18 | const Nav = () => (
);
19 | const Home = () => (
);
20 | const Countdown = () => (
);
21 |
22 | import backgroundImage from './assets/background.svg';
23 |
24 | // Offline plugin
25 |
26 | if (process.env.NODE_ENV === 'production') {
27 | const runtime = require('offline-plugin/runtime');
28 |
29 | runtime.install({
30 | onUpdateReady() {
31 | runtime.applyUpdate();
32 | },
33 | onUpdated() {
34 | window.location.reload();
35 | },
36 | });
37 | }
38 |
39 | injectGlobal`
40 | html,
41 | body {
42 | height: 100%;
43 | margin: 0;
44 | }
45 |
46 | html {
47 | background-image: url(${backgroundImage});
48 | background-repeat: repeat;
49 | background-size: auto 256px;
50 | }
51 |
52 | * {
53 | box-sizing: border-box;
54 | }
55 | `;
56 |
57 | const AppStyles = styled.div`
58 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
59 | `;
60 |
61 | const App = () => (
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | );
71 |
72 | ReactDOM.render(
73 | React.createElement(App),
74 | document.getElementById('app-root'),
75 | );
76 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/lib/SkyShader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author zz85 / https://github.com/zz85
3 | *
4 | * Based on "A Practical Analytic Model for Daylight"
5 | * aka The Preetham Model, the de facto standard analytic skydome model
6 | * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf
7 | *
8 | * First implemented by Simon Wallner
9 | * http://www.simonwallner.at/projects/atmospheric-scattering
10 | *
11 | * Improved by Martin Upitis
12 | * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR
13 | *
14 | * Three.js integration by zz85 http://twitter.com/blurspline
15 | */
16 |
17 | import * as THREE from 'three';
18 |
19 | THREE.Sky = function () {
20 |
21 | var skyShader = THREE.Sky.SkyShader;
22 |
23 | var skyUniforms = THREE.UniformsUtils.clone( skyShader.uniforms );
24 |
25 | var skyMat = new THREE.ShaderMaterial( {
26 | fragmentShader: skyShader.fragmentShader,
27 | vertexShader: skyShader.vertexShader,
28 | uniforms: skyUniforms,
29 | side: THREE.BackSide
30 | } );
31 |
32 | var skyGeo = new THREE.SphereBufferGeometry( 450000, 32, 15 );
33 | var skyMesh = new THREE.Mesh( skyGeo, skyMat );
34 |
35 | // Expose variables
36 | this.mesh = skyMesh;
37 | this.uniforms = skyUniforms;
38 |
39 | };
40 |
41 | THREE.Sky.SkyShader = {
42 |
43 | uniforms: {
44 | luminance: { value: 1 },
45 | turbidity: { value: 2 },
46 | rayleigh: { value: 1 },
47 | mieCoefficient: { value: 0.005 },
48 | mieDirectionalG: { value: 0.8 },
49 | sunPosition: { value: new THREE.Vector3() }
50 | },
51 |
52 | vertexShader: [
53 | 'uniform vec3 sunPosition;',
54 | 'uniform float rayleigh;',
55 | 'uniform float turbidity;',
56 | 'uniform float mieCoefficient;',
57 |
58 | 'varying vec3 vWorldPosition;',
59 | 'varying vec3 vSunDirection;',
60 | 'varying float vSunfade;',
61 | 'varying vec3 vBetaR;',
62 | 'varying vec3 vBetaM;',
63 | 'varying float vSunE;',
64 |
65 | 'const vec3 up = vec3( 0.0, 1.0, 0.0 );',
66 |
67 | // constants for atmospheric scattering
68 | 'const float e = 2.71828182845904523536028747135266249775724709369995957;',
69 | 'const float pi = 3.141592653589793238462643383279502884197169;',
70 |
71 | // wavelength of used primaries, according to preetham
72 | 'const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 );',
73 | // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function:
74 | // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn))
75 | 'const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 );',
76 |
77 | // mie stuff
78 | // K coefficient for the primaries
79 | 'const float v = 4.0;',
80 | 'const vec3 K = vec3( 0.686, 0.678, 0.666 );',
81 | // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K
82 | 'const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 );',
83 |
84 | // earth shadow hack
85 | // cutoffAngle = pi / 1.95;
86 | 'const float cutoffAngle = 1.6110731556870734;',
87 | 'const float steepness = 1.5;',
88 | 'const float EE = 1000.0;',
89 |
90 | 'float sunIntensity( float zenithAngleCos ) {',
91 | ' zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 );',
92 | ' return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) );',
93 | '}',
94 |
95 | 'vec3 totalMie( float T ) {',
96 | ' float c = ( 0.2 * T ) * 10E-18;',
97 | ' return 0.434 * c * MieConst;',
98 | '}',
99 |
100 | 'void main() {',
101 |
102 | ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
103 | ' vWorldPosition = worldPosition.xyz;',
104 |
105 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
106 |
107 | ' vSunDirection = normalize( sunPosition );',
108 |
109 | ' vSunE = sunIntensity( dot( vSunDirection, up ) );',
110 |
111 | ' vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 );',
112 |
113 | ' float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) );',
114 |
115 | // extinction (absorbtion + out scattering)
116 | // rayleigh coefficients
117 | ' vBetaR = totalRayleigh * rayleighCoefficient;',
118 |
119 | // mie coefficients
120 | ' vBetaM = totalMie( turbidity ) * mieCoefficient;',
121 |
122 | '}'
123 | ].join( '\n' ),
124 |
125 | fragmentShader: [
126 | 'varying vec3 vWorldPosition;',
127 | 'varying vec3 vSunDirection;',
128 | 'varying float vSunfade;',
129 | 'varying vec3 vBetaR;',
130 | 'varying vec3 vBetaM;',
131 | 'varying float vSunE;',
132 |
133 | 'uniform float luminance;',
134 | 'uniform float mieDirectionalG;',
135 |
136 | 'const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 );',
137 |
138 | // constants for atmospheric scattering
139 | 'const float pi = 3.141592653589793238462643383279502884197169;',
140 |
141 | 'const float n = 1.0003;', // refractive index of air
142 | 'const float N = 2.545E25;', // number of molecules per unit volume for air at
143 | // 288.15K and 1013mb (sea level -45 celsius)
144 |
145 | // optical length at zenith for molecules
146 | 'const float rayleighZenithLength = 8.4E3;',
147 | 'const float mieZenithLength = 1.25E3;',
148 | 'const vec3 up = vec3( 0.0, 1.0, 0.0 );',
149 | // 66 arc seconds -> degrees, and the cosine of that
150 | 'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;',
151 |
152 | // 3.0 / ( 16.0 * pi )
153 | 'const float THREE_OVER_SIXTEENPI = 0.05968310365946075;',
154 | // 1.0 / ( 4.0 * pi )
155 | 'const float ONE_OVER_FOURPI = 0.07957747154594767;',
156 |
157 | 'float rayleighPhase( float cosTheta ) {',
158 | ' return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) );',
159 | '}',
160 |
161 | 'float hgPhase( float cosTheta, float g ) {',
162 | ' float g2 = pow( g, 2.0 );',
163 | ' float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 );',
164 | ' return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse );',
165 | '}',
166 |
167 | // Filmic ToneMapping http://filmicgames.com/archives/75
168 | 'const float A = 0.15;',
169 | 'const float B = 0.50;',
170 | 'const float C = 0.10;',
171 | 'const float D = 0.20;',
172 | 'const float E = 0.02;',
173 | 'const float F = 0.30;',
174 |
175 | 'const float whiteScale = 1.0748724675633854;', // 1.0 / Uncharted2Tonemap(1000.0)
176 |
177 | 'vec3 Uncharted2Tonemap( vec3 x ) {',
178 | ' return ( ( x * ( A * x + C * B ) + D * E ) / ( x * ( A * x + B ) + D * F ) ) - E / F;',
179 | '}',
180 |
181 |
182 | 'void main() {',
183 | // optical length
184 | // cutoff angle at 90 to avoid singularity in next formula.
185 | ' float zenithAngle = acos( max( 0.0, dot( up, normalize( vWorldPosition - cameraPos ) ) ) );',
186 | ' float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) );',
187 | ' float sR = rayleighZenithLength * inverse;',
188 | ' float sM = mieZenithLength * inverse;',
189 |
190 | // combined extinction factor
191 | ' vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) );',
192 |
193 | // in scattering
194 | ' float cosTheta = dot( normalize( vWorldPosition - cameraPos ), vSunDirection );',
195 |
196 | ' float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 );',
197 | ' vec3 betaRTheta = vBetaR * rPhase;',
198 |
199 | ' float mPhase = hgPhase( cosTheta, mieDirectionalG );',
200 | ' vec3 betaMTheta = vBetaM * mPhase;',
201 |
202 | ' vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) );',
203 | ' Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) );',
204 |
205 | // nightsky
206 | ' vec3 direction = normalize( vWorldPosition - cameraPos );',
207 | ' float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2]',
208 | ' float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2]',
209 | ' vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 );',
210 | ' vec3 L0 = vec3( 0.1 ) * Fex;',
211 |
212 | // composition + solar disc
213 | ' float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );',
214 | ' L0 += ( vSunE * 19000.0 * Fex ) * sundisk;',
215 |
216 | ' vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );',
217 |
218 | ' vec3 curr = Uncharted2Tonemap( ( log2( 2.0 / pow( luminance, 4.0 ) ) ) * texColor );',
219 | ' vec3 color = curr * whiteScale;',
220 |
221 | ' vec3 retColor = pow( color, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );',
222 |
223 | ' gl_FragColor = vec4( retColor, 1.0 );',
224 |
225 | '}'
226 | ].join( '\n' )
227 |
228 | };
229 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/lib/cactusModel.json:
--------------------------------------------------------------------------------
1 | {
2 | "normals":[-0.934004,-0.340305,0.108764,-0.977363,-0.210994,-0.0155688,-0.63695,0.765259,0.0931305,0.293514,-0.730598,-0.616503,-0.504066,0.4132,0.758409,-0.453905,0.490555,0.743859,0.121935,-0.48893,-0.863759,-0.933597,0.147849,-0.3264,0.506743,-0.315187,-0.802414,0.752806,0.638572,-0.159716,0.481829,-0.669727,-0.565072,0.115732,-0.759095,-0.640609,0.976359,0.135217,-0.168639,0.773174,0.490322,0.402226,0.889475,0.181375,0.419449,-0.959097,-0.27885,0.0487405,-0.77183,0.588326,0.241145,0.0190343,-0.984941,-0.171838,0.3048,0.295726,-0.905341,-0.629651,-0.547784,-0.550883,0.898032,0.351972,-0.263922,-0.300827,-0.810674,0.502305,-0.48807,-0.798141,0.353211,-0.421594,0.195019,0.885566,0.678763,-0.710064,-0.187322,-0.705561,0.458006,-0.540753,0.572009,-0.780113,0.253435,0.614787,-0.39639,-0.681845,0.656628,0.070494,-0.750912,-0.757463,-0.040272,0.651634,-0.705348,-0.56378,0.429694,0.184813,0.830189,-0.525956,-0.496781,0.826573,-0.264547,-0.775151,0.606537,0.17679,0.850479,0.322855,0.41527,-0.521119,-0.50555,-0.687644,0.68309,0.0770035,0.726263,-0.452268,0.31448,-0.8346,0.592176,0.66326,-0.457618,-0.583123,0.643889,-0.495353,-0.554658,0.632559,0.540577,0.547432,0.594361,0.589113,0.535557,0.0453709,0.843279,-0.688658,0.212181,0.693347,-0.657859,0.078197,-0.74907,0.673795,0.138285,-0.725863,0.609055,-0.43206,-0.665114,-0.657183,0.0138783,-0.753603,-0.605914,-0.347718,0.715515,-0.95,0.159854,0.268229,-0.496777,-0.447817,-0.74342,0.409124,-0.330661,0.850459,0.519997,-0.132433,0.843839,0.411397,0.0863918,0.907353,-0.929533,-0.16109,-0.331689,-0.808966,0.0196838,-0.587526,0.659916,0.19058,-0.726767,0.808818,0.0547388,-0.585506,0.905146,-0.194485,-0.378002,-0.388926,-0.283165,-0.876672,0.431915,0.141795,0.890699],
3 | "metadata":{
4 | "version":3,
5 | "vertices":45,
6 | "generator":"io_three",
7 | "normals":61,
8 | "faces":61,
9 | "type":"Geometry"
10 | },
11 | "vertices":[-0.300803,5.21962,-2.40982,0.00885725,4.34318,-1.87,-0.272946,3.89615,-0.299987,-0.611808,4.30357,-1.9352,-0.112233,5.25244,-1.3898,0.495805,4.47908,-1.61587,-0.717074,4.75391,-1.43012,0.00819606,2.5532,0.250924,0.081711,4.37467,-0.692596,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,0.535965,3.27878,3.76143,-0.146769,2.17102,3.71399,-0.958925,3.36221,3.36352,-0.605378,2.17641,3.56135,-0.434734,2.4976,2.96216,-0.344565,3.61621,2.75875,-0.139043,4.06988,3.69136,-0.0767336,3.39776,4.13828,0.212165,2.26134,3.32216,-0.102779,2.09515,2.51143,-0.361312,2.3378,1.09194,-0.861599,-0.655066,1.01502,0.304171,-0.577505,0.989137,-0.633588,2.43514,1.37986,-0.773859,3.72084,2.05479,-1.06225,0.635164,0.934365,-1.50921,1.8301,0.51324,-0.51002,5.2504,1.7559,-1.89499,3.60959,0.88243,-1.61025,5.10116,0.801648,-0.435947,6.14863,0.780838,1.19442,4.67411,0.753474,1.18849,3.17018,0.78912,0.0694638,1.56138,0.809495,0.36255,0.592642,0.821591,-1.34921,0.972115,1.59124,-1.09462,-0.525398,1.58873,-0.390343,5.39487,-0.25263,-0.114915,3.57968,-0.670439,-0.298634,0.856904,0.290523,-0.280229,-0.698967,-0.187097,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,-0.361312,2.3378,1.09194],
12 | "faces":[32,6,3,2,0,0,0,33,2,3,7,6,1,1,1,1,32,4,0,6,2,2,2,33,7,3,1,5,3,3,3,3,32,6,7,8,4,4,4,32,6,8,4,5,5,5,32,1,3,0,6,6,6,32,3,6,0,7,7,7,32,0,5,1,8,8,8,32,4,5,0,9,9,9,32,1,5,7,10,10,10,32,1,7,3,11,11,11,32,3,2,7,12,12,12,32,4,8,5,13,13,13,32,8,7,5,14,14,14,32,3,7,6,15,15,15,32,20,21,9,16,16,16,32,20,43,21,17,17,17,32,12,14,18,18,18,18,32,42,43,44,19,19,19,32,14,9,10,20,20,20,32,15,10,20,21,21,21,32,19,15,20,22,22,22,32,16,15,19,23,23,23,32,15,14,10,24,24,24,32,12,11,19,25,25,25,32,17,13,16,26,26,26,32,13,17,18,27,27,27,32,14,13,18,28,28,28,32,16,19,11,29,29,29,32,17,16,11,30,30,30,32,12,9,14,31,31,31,32,12,19,42,32,32,32,32,9,19,20,33,33,33,32,13,14,15,34,34,34,32,11,18,17,35,35,35,32,16,13,15,36,36,36,32,12,18,11,37,37,37,32,32,38,31,38,38,38,32,31,38,30,39,39,39,32,31,30,28,40,40,40,32,28,32,31,41,41,41,33,25,33,32,28,42,42,42,42,33,28,30,29,25,43,43,43,43,33,29,30,38,39,44,44,44,44,33,38,32,33,39,45,45,45,45,32,34,39,33,46,46,46,32,29,39,27,47,47,47,33,25,29,27,24,48,48,48,48,33,24,27,26,36,49,49,49,49,32,27,40,26,50,50,50,33,24,34,33,25,51,51,51,51,32,36,34,24,52,52,52,33,23,35,36,37,53,53,53,53,33,22,37,36,26,54,54,54,54,33,41,22,26,40,55,55,55,55,32,35,40,34,56,56,56,33,23,41,40,35,57,57,57,57,32,39,34,40,58,58,58,32,39,40,27,59,59,59,32,34,36,35,60,60,60]
13 | }
--------------------------------------------------------------------------------
/6-offline-plugin/src/views/about.js:
--------------------------------------------------------------------------------
1 | /**
2 | * About
3 | * The mandatory caboose of every website
4 | */
5 |
6 | import React from 'react';
7 |
8 | import Cell from '../components/Cell';
9 |
10 | const About = () => (
11 |
12 | About
13 | |
14 | );
15 |
16 | export default About;
17 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/views/countdown.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Countdown
3 | * Hopefully it’s the final one
4 | */
5 |
6 | import React from 'react';
7 | import moment from 'moment';
8 | import styled from 'styled-components';
9 |
10 | import Cell from '../components/Cell';
11 | import Heading from '../components/Heading';
12 | import Subheading from '../components/Subheading';
13 |
14 | const Timer = styled.div`
15 | background-color: white;
16 | border-radius: 12px;
17 | box-shadow: 0 0 0 3px rgb(0, 16, 48);
18 | font-style: italic;
19 | padding: 16px;
20 | text-align: center;
21 | `;
22 |
23 | class Countdown extends React.Component {
24 | constructor(props) {
25 | super(props);
26 | this.tick.bind(this);
27 | }
28 |
29 | componentWillMount() {
30 | this.setState({ daysLeft: '' });
31 | }
32 |
33 | componentDidMount() {
34 | setInterval(() => this.tick(), 1000);
35 | }
36 |
37 | daysUntil2020() {
38 | const then = moment([2020, 1, 1]);
39 | const now = moment();
40 |
41 | const years = then.diff(now, 'years');
42 | now.add(years, 'years');
43 | const months = then.diff(now, 'months');
44 | now.add(months, 'months');
45 | const days = then.diff(now, 'days');
46 | now.add(days, 'days');
47 | const hours = then.diff(now, 'hours');
48 | now.add(hours, 'hours');
49 | const minutes = then.diff(now, 'minutes');
50 | now.add(minutes, 'minutes');
51 | const seconds = then.diff(now, 'seconds');
52 | now.add(seconds, 'seconds');
53 |
54 | return `${years} year${years === 1 ? '' : 's'}, ${months} month${months === 1 ? '' : 's'}, ${days} day${days === 1 ? '' : 's'}, ${hours} hour${hours === 1 ? '' : 's'}, ${minutes} minute${minutes === 1 ? '' : 's'}, ${seconds} second${seconds === 1 ? '' : 's'}`;
55 | }
56 |
57 | tick() {
58 | this.setState({ daysLeft: this.daysUntil2020() });
59 | }
60 |
61 | render() {
62 | return (
63 |
64 | Grand Opening
65 | 🎉 Opens Jan 1, 2020! 🎉
66 |
67 | {this.state.daysLeft}
68 |
69 | |
70 | );
71 | }
72 | }
73 |
74 | export default Countdown;
75 |
--------------------------------------------------------------------------------
/6-offline-plugin/src/views/home.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Home
3 | * …is where the 🌵 is
4 | */
5 |
6 | import React from 'react';
7 | import styled from 'styled-components';
8 |
9 | import Cell from '../components/Cell';
10 | import DesertScene from '../components/DesertScene';
11 | import Heading from '../components/Heading';
12 |
13 | const ThreeD = styled.div`
14 | background-color: black;
15 | box-shadow: 0 32px 64px rgba(248, 106, 0, 0.5);
16 | margin-bottom: 128px;
17 | margin-left: auto;
18 | margin-right: auto;
19 | max-width: 1024px;
20 | position: relative;
21 | width: calc(100vw - 64px);
22 |
23 | & canvas {
24 | display: block;
25 | width: 100%;
26 | }
27 | `;
28 |
29 | const Title = styled.h1`
30 | bottom: 32px;
31 | color: red;
32 | font-family: cursive;
33 | font-size: 1.25em;
34 | font-style: italic;
35 | line-height: 1;
36 | margin-bottom: 0;
37 | margin-top: 0;
38 | position: absolute;
39 | right: 32px;
40 | z-index: 10;
41 | `;
42 |
43 | class Home extends React.Component {
44 | constructor(props) {
45 | super(props);
46 | }
47 |
48 | componentWillMount() {
49 | this.setState({
50 | height: 544.625,
51 | width: 1024,
52 | });
53 | }
54 |
55 | render() {
56 | return (
57 |
58 |
59 | 🌵🏜🌵 Welcome to Cactus World! 🌵🏜🌵
60 | |
61 |
62 |
63 | Artist’s Rendering
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | export default Home;
71 |
--------------------------------------------------------------------------------
/6-offline-plugin/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 |
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
6 | const OfflinePlugin = require('offline-plugin');
7 | const WebpackChunkHash = require('webpack-chunk-hash');
8 |
9 | /* Shared Dev & Production */
10 |
11 | const config = {
12 | context: path.resolve(__dirname, 'src'),
13 |
14 | entry: {
15 | index: './index.js',
16 | vendor: ['react', 'react-dom', 'react-router'],
17 | },
18 |
19 | module: {
20 | rules: [
21 | {
22 | test: /\.js$/,
23 | use: 'babel-loader',
24 | exclude: /node_modules/,
25 | },
26 | {
27 | test: /\.(jpg|jpeg|gif|png|svg|woff|woff2)$/,
28 | use: {
29 | loader: 'file-loader',
30 | options: { name: '[name].[hash].[ext]' },
31 | },
32 | },
33 | ],
34 | },
35 |
36 | output: {
37 | path: path.resolve(__dirname, 'dist'),
38 | filename: '[name].bundle.js',
39 | publicPath: '/',
40 | },
41 |
42 | resolve: {
43 | extensions: ['.js'],
44 | modules: [path.resolve(__dirname, 'src'), 'node_modules'],
45 | },
46 |
47 | plugins: [
48 | new webpack.optimize.CommonsChunkPlugin({
49 | name: 'vendor',
50 | }),
51 | new webpack.optimize.ModuleConcatenationPlugin(),
52 | new HtmlWebpackPlugin({
53 | appMountId: 'app-root',
54 | inlineManifestWebpackName: 'webpackManifest',
55 | template: require('html-webpack-template'),
56 | title: '🌵🏜🌵 Welcome to Cactus World! 🌵🏜🌵',
57 | }),
58 | // Add offline plugin (should be last)
59 | new OfflinePlugin({
60 | AppCache: false,
61 | ServiceWorker: { events: true },
62 | }),
63 | ],
64 |
65 | devServer: {
66 | historyApiFallback: true,
67 | },
68 | };
69 |
70 | if (process.env.NODE_ENV === 'production') {
71 | config.output.filename = '[name].[chunkhash].js';
72 | config.plugins = [
73 | ...config.plugins,
74 | new webpack.HashedModuleIdsPlugin(),
75 | new WebpackChunkHash(),
76 | new ChunkManifestPlugin({
77 | filename: 'chunk-manifest.json',
78 | manifestVariable: 'webpackManifest',
79 | inlineManifest: true,
80 | }),
81 | ];
82 | }
83 |
84 | module.exports = config;
85 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env"],
3 | "plugins": ["syntax-dynamic-import", "transform-react-jsx"]
4 | }
5 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "airbnb"
3 | };
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/README.md:
--------------------------------------------------------------------------------
1 | # webpack Bundle Analyzer
2 |
3 | Utilize the [webpack-bundle-analyzer](https://github.com/th0r/webpack-bundle-analyzer) plugin.
4 |
5 | ## Installation
6 |
7 | See the webpack Bundle Analyzer’s [docs](https://github.com/th0r/webpack-bundle-analyzer) for detailed instructions.
8 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "engines": {
3 | "node": "8.1.4"
4 | },
5 | "scripts": {
6 | "build": "node_modules/.bin/webpack -p",
7 | "dev": "node_modules/.bin/webpack-dev-server"
8 | },
9 | "dependencies": {
10 | "babel-core": "^6.25.0",
11 | "babel-loader": "^7.1.1",
12 | "babel-plugin-syntax-dynamic-import": "^6.18.0",
13 | "babel-plugin-transform-react-jsx": "^6.24.1",
14 | "babel-preset-env": "^1.6.0",
15 | "chunk-manifest-webpack-plugin": "^1.1.0",
16 | "file-loader": "^0.11.2",
17 | "html-webpack-plugin": "^2.29.0",
18 | "html-webpack-template": "^6.0.1",
19 | "moment": "^2.18.1",
20 | "offline-plugin": "^4.8.3",
21 | "react": "^15.6.1",
22 | "react-code-splitting": "^1.1.1",
23 | "react-dom": "^15.6.1",
24 | "react-router": "^4.1.1",
25 | "react-router-dom": "^4.1.1",
26 | "styled-components": "^2.1.1",
27 | "three": "^0.86.0",
28 | "webpack": "^3.1.0",
29 | "webpack-chunk-hash": "^0.4.0",
30 | "webpack-dev-server": "^2.5.1"
31 | },
32 | "devDependencies": {
33 | "eslint": "^3.19.0",
34 | "eslint-config-airbnb": "^15.0.2",
35 | "eslint-plugin-import": "^2.7.0",
36 | "eslint-plugin-jsx-a11y": "^5.1.1",
37 | "eslint-plugin-react": "^7.1.0",
38 | "webpack-bundle-analyzer": "^2.8.3"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/assets/background.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/browserslist:
--------------------------------------------------------------------------------
1 | > 5%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/components/Cell.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cell
3 | * Width-limiting container
4 | */
5 |
6 | import styled from 'styled-components';
7 |
8 | const Cell = styled.div`
9 | display: block;
10 | margin-left: auto;
11 | margin-right: auto;
12 | max-width: 800px;
13 | padding-left: 16px;
14 | padding-right: 16px;
15 | `;
16 |
17 | export default Cell;
18 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/components/DesertScene.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Desert Scene
3 | * In the desert, you can’t remember your name…
4 | */
5 |
6 | import React from 'react';
7 | import * as THREE from 'three';
8 | import ReactDOM from 'react-dom';
9 |
10 | const cactusModel = require('../lib/cactusModel.json');
11 |
12 | class DesertScene extends React.Component {
13 | constructor(props) {
14 | super(props);
15 | this.paint.bind(this);
16 | }
17 |
18 | componentDidMount() {
19 | const canvasBox = this.canvas.getBoundingClientRect();
20 | const canvasWidth = canvasBox.width;
21 | const canvasHeight = canvasBox.width * 0.4255;
22 |
23 | this.scene = new THREE.Scene();
24 | this.scene.background = new THREE.Color(0xc8faeb);
25 | this.camera = new THREE.PerspectiveCamera(20, canvasWidth/canvasHeight, 0.1, 1000);
26 | this.camera.position.z = 100;
27 | this.renderer = new THREE.WebGLRenderer();
28 | this.renderer.setPixelRatio(window.devicePixelRatio);
29 | this.renderer.setSize(canvasWidth, canvasHeight);
30 | this.canvas.appendChild(this.renderer.domElement);
31 | this.scene.add(this.camera);
32 |
33 | const lights = [];
34 | lights[0] = new THREE.HemisphereLight(0xffffbb, 0xf89316, 1);
35 | this.scene.add(lights[0]);
36 |
37 | // 🌵
38 | const cactusGeometry = new THREE.JSONLoader().parse(cactusModel).geometry;
39 | const cactusMaterial = new THREE.MeshPhongMaterial({ color: 0xA9DCAA, shininess: 50 });
40 | for (var n = 0; n < 50; n += 1) {
41 | const cactus = new THREE.Mesh(cactusGeometry, cactusMaterial);
42 | cactus.position.x = Math.random() * 400 - 200;
43 | cactus.position.z = Math.random() * 400 - 200;
44 | cactus.rotation.y = Math.random() * 2 * Math.PI;
45 | const scalefactor = Math.random() * 4;
46 | cactus.scale.set(scalefactor, scalefactor, scalefactor);
47 | this.scene.add(cactus);
48 | }
49 |
50 | // 🏜
51 | this.desert = new THREE.Mesh(
52 | new THREE.BoxGeometry(500, 0.1, 500),
53 | new THREE.MeshPhongMaterial({ color: 0xe3d9c0 }),
54 | );
55 | this.desert.rotation.x = Math.PI;
56 | this.scene.add(this.desert);
57 | this.paint();
58 | }
59 |
60 | paint(frame) {
61 | const rotationSpeed = frame / 4000;
62 | this.camera.position.set(
63 | 100 * Math.sin(rotationSpeed),
64 | 10,
65 | 100 * Math.cos(rotationSpeed),
66 | );
67 | this.desert.rotation.y += 0.01;
68 | this.camera.lookAt(this.scene.position);
69 |
70 | this.renderer.render(this.scene, this.camera);
71 | window.requestAnimationFrame(nextFrame => this.paint(nextFrame));
72 | }
73 |
74 | render() {
75 | return
this.canvas = canvas} />;
76 | }
77 | }
78 |
79 | export default DesertScene;
80 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/components/Heading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled from 'styled-components';
7 |
8 | const Heading = styled.div`
9 | background-color: #00ce67;
10 | color: white;
11 | font-size: 2.4em;
12 | font-weight: 700;
13 | line-height: 1.2;
14 | margin-bottom: 1em;
15 | margin-top: 0;
16 | padding-bottom: 16px;
17 | padding-top: 16px;
18 | text-align: center;
19 | `;
20 |
21 | export default Heading;
22 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/components/Nav.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Nav
3 | * It goes where you go
4 | */
5 |
6 | import React from 'react';
7 | import { Link } from 'react-router-dom';
8 | import styled from 'styled-components';
9 |
10 | const Nav = styled.nav`
11 | background-color: white;
12 | box-shadow: 0 2px 4px rgba(0, 8, 16, 0.2);
13 | display: flex;
14 | margin-bottom: 32px;
15 | margin-left: 0;
16 | margin-right: 0;
17 | padding: 32px;
18 | `;
19 |
20 | const NavLink = styled.div`
21 | font-weight: 700;
22 | margin-left: 16px;
23 | margin-right: 16px;
24 |
25 | & a {
26 | color: #00ce67;
27 | text-decoration: none;
28 | transition: color 200ms;
29 |
30 | &:focus,
31 | &:hover {
32 | color: #00b85f;
33 | }
34 | }
35 | `;
36 |
37 | export default () => (
38 |
42 | );
43 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/components/Subheading.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Heading
3 | * Big Text
4 | */
5 |
6 | import styled, { keyframes } from 'styled-components';
7 |
8 | const spin = keyframes`
9 | to {
10 | transform: rotate3d(0, 1, 0, 1turn);
11 | }
12 | `;
13 |
14 | const Subheading = styled.div`
15 | animation-duration: 4s;
16 | animation-iteration-count: 300;
17 | animation-name: ${spin};
18 | animation-timing-function: linear;
19 | font-size: 1.5em;
20 | font-style: italic;
21 | font-weight: 400;
22 | letter-spacing: 0.125em;
23 | line-height: 1.2;
24 | margin-bottom: 1em;
25 | margin-top: 0.5em;
26 | text-align: center;
27 | text-transform: uppercase;
28 | `;
29 |
30 | export default Subheading;
31 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Index
3 | * Where it all begins
4 | */
5 |
6 | import React from 'react';
7 | import ReactDOM from 'react-dom';
8 | import {
9 | BrowserRouter as Router,
10 | Route,
11 | Link,
12 | Redirect,
13 | withRouter
14 | } from 'react-router-dom';
15 | import styled, { injectGlobal } from 'styled-components';
16 | import Async from 'react-code-splitting';
17 |
18 | const Nav = () => (
);
19 | const Home = () => (
);
20 | const Countdown = () => (
);
21 |
22 | import backgroundImage from './assets/background.svg';
23 |
24 | // Offline plugin
25 |
26 | const runtime = require('offline-plugin/runtime');
27 |
28 | if (process.env.NODE_ENV === 'production') {
29 | runtime.install({
30 | onUpdateReady() {
31 | runtime.applyUpdate();
32 | },
33 | onUpdated() {
34 | window.location.reload();
35 | },
36 | });
37 | }
38 |
39 | injectGlobal`
40 | html,
41 | body {
42 | height: 100%;
43 | margin: 0;
44 | }
45 |
46 | html {
47 | background-image: url(${backgroundImage});
48 | background-repeat: repeat;
49 | background-size: auto 256px;
50 | }
51 |
52 | * {
53 | box-sizing: border-box;
54 | }
55 | `;
56 |
57 | const AppStyles = styled.div`
58 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
59 | `;
60 |
61 | const App = () => (
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | );
71 |
72 | ReactDOM.render(
73 | React.createElement(App),
74 | document.getElementById('app-root'),
75 | );
76 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/lib/cactusModel.json:
--------------------------------------------------------------------------------
1 | {
2 | "normals":[-0.934004,-0.340305,0.108764,-0.977363,-0.210994,-0.0155688,-0.63695,0.765259,0.0931305,0.293514,-0.730598,-0.616503,-0.504066,0.4132,0.758409,-0.453905,0.490555,0.743859,0.121935,-0.48893,-0.863759,-0.933597,0.147849,-0.3264,0.506743,-0.315187,-0.802414,0.752806,0.638572,-0.159716,0.481829,-0.669727,-0.565072,0.115732,-0.759095,-0.640609,0.976359,0.135217,-0.168639,0.773174,0.490322,0.402226,0.889475,0.181375,0.419449,-0.959097,-0.27885,0.0487405,-0.77183,0.588326,0.241145,0.0190343,-0.984941,-0.171838,0.3048,0.295726,-0.905341,-0.629651,-0.547784,-0.550883,0.898032,0.351972,-0.263922,-0.300827,-0.810674,0.502305,-0.48807,-0.798141,0.353211,-0.421594,0.195019,0.885566,0.678763,-0.710064,-0.187322,-0.705561,0.458006,-0.540753,0.572009,-0.780113,0.253435,0.614787,-0.39639,-0.681845,0.656628,0.070494,-0.750912,-0.757463,-0.040272,0.651634,-0.705348,-0.56378,0.429694,0.184813,0.830189,-0.525956,-0.496781,0.826573,-0.264547,-0.775151,0.606537,0.17679,0.850479,0.322855,0.41527,-0.521119,-0.50555,-0.687644,0.68309,0.0770035,0.726263,-0.452268,0.31448,-0.8346,0.592176,0.66326,-0.457618,-0.583123,0.643889,-0.495353,-0.554658,0.632559,0.540577,0.547432,0.594361,0.589113,0.535557,0.0453709,0.843279,-0.688658,0.212181,0.693347,-0.657859,0.078197,-0.74907,0.673795,0.138285,-0.725863,0.609055,-0.43206,-0.665114,-0.657183,0.0138783,-0.753603,-0.605914,-0.347718,0.715515,-0.95,0.159854,0.268229,-0.496777,-0.447817,-0.74342,0.409124,-0.330661,0.850459,0.519997,-0.132433,0.843839,0.411397,0.0863918,0.907353,-0.929533,-0.16109,-0.331689,-0.808966,0.0196838,-0.587526,0.659916,0.19058,-0.726767,0.808818,0.0547388,-0.585506,0.905146,-0.194485,-0.378002,-0.388926,-0.283165,-0.876672,0.431915,0.141795,0.890699],
3 | "metadata":{
4 | "version":3,
5 | "vertices":45,
6 | "generator":"io_three",
7 | "normals":61,
8 | "faces":61,
9 | "type":"Geometry"
10 | },
11 | "vertices":[-0.300803,5.21962,-2.40982,0.00885725,4.34318,-1.87,-0.272946,3.89615,-0.299987,-0.611808,4.30357,-1.9352,-0.112233,5.25244,-1.3898,0.495805,4.47908,-1.61587,-0.717074,4.75391,-1.43012,0.00819606,2.5532,0.250924,0.081711,4.37467,-0.692596,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,0.535965,3.27878,3.76143,-0.146769,2.17102,3.71399,-0.958925,3.36221,3.36352,-0.605378,2.17641,3.56135,-0.434734,2.4976,2.96216,-0.344565,3.61621,2.75875,-0.139043,4.06988,3.69136,-0.0767336,3.39776,4.13828,0.212165,2.26134,3.32216,-0.102779,2.09515,2.51143,-0.361312,2.3378,1.09194,-0.861599,-0.655066,1.01502,0.304171,-0.577505,0.989137,-0.633588,2.43514,1.37986,-0.773859,3.72084,2.05479,-1.06225,0.635164,0.934365,-1.50921,1.8301,0.51324,-0.51002,5.2504,1.7559,-1.89499,3.60959,0.88243,-1.61025,5.10116,0.801648,-0.435947,6.14863,0.780838,1.19442,4.67411,0.753474,1.18849,3.17018,0.78912,0.0694638,1.56138,0.809495,0.36255,0.592642,0.821591,-1.34921,0.972115,1.59124,-1.09462,-0.525398,1.58873,-0.390343,5.39487,-0.25263,-0.114915,3.57968,-0.670439,-0.298634,0.856904,0.290523,-0.280229,-0.698967,-0.187097,-0.665864,1.42254,2.35015,-1.04231,2.15758,2.04951,-0.361312,2.3378,1.09194],
12 | "faces":[32,6,3,2,0,0,0,33,2,3,7,6,1,1,1,1,32,4,0,6,2,2,2,33,7,3,1,5,3,3,3,3,32,6,7,8,4,4,4,32,6,8,4,5,5,5,32,1,3,0,6,6,6,32,3,6,0,7,7,7,32,0,5,1,8,8,8,32,4,5,0,9,9,9,32,1,5,7,10,10,10,32,1,7,3,11,11,11,32,3,2,7,12,12,12,32,4,8,5,13,13,13,32,8,7,5,14,14,14,32,3,7,6,15,15,15,32,20,21,9,16,16,16,32,20,43,21,17,17,17,32,12,14,18,18,18,18,32,42,43,44,19,19,19,32,14,9,10,20,20,20,32,15,10,20,21,21,21,32,19,15,20,22,22,22,32,16,15,19,23,23,23,32,15,14,10,24,24,24,32,12,11,19,25,25,25,32,17,13,16,26,26,26,32,13,17,18,27,27,27,32,14,13,18,28,28,28,32,16,19,11,29,29,29,32,17,16,11,30,30,30,32,12,9,14,31,31,31,32,12,19,42,32,32,32,32,9,19,20,33,33,33,32,13,14,15,34,34,34,32,11,18,17,35,35,35,32,16,13,15,36,36,36,32,12,18,11,37,37,37,32,32,38,31,38,38,38,32,31,38,30,39,39,39,32,31,30,28,40,40,40,32,28,32,31,41,41,41,33,25,33,32,28,42,42,42,42,33,28,30,29,25,43,43,43,43,33,29,30,38,39,44,44,44,44,33,38,32,33,39,45,45,45,45,32,34,39,33,46,46,46,32,29,39,27,47,47,47,33,25,29,27,24,48,48,48,48,33,24,27,26,36,49,49,49,49,32,27,40,26,50,50,50,33,24,34,33,25,51,51,51,51,32,36,34,24,52,52,52,33,23,35,36,37,53,53,53,53,33,22,37,36,26,54,54,54,54,33,41,22,26,40,55,55,55,55,32,35,40,34,56,56,56,33,23,41,40,35,57,57,57,57,32,39,34,40,58,58,58,32,39,40,27,59,59,59,32,34,36,35,60,60,60]
13 | }
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/views/about.js:
--------------------------------------------------------------------------------
1 | /**
2 | * About
3 | * The mandatory caboose of every website
4 | */
5 |
6 | import React from 'react';
7 |
8 | import Cell from '../components/Cell';
9 |
10 | const About = () => (
11 |
12 | About
13 | |
14 | );
15 |
16 | export default About;
17 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/views/countdown.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Countdown
3 | * Hopefully it’s the final one
4 | */
5 |
6 | import React from 'react';
7 | import moment from 'moment';
8 | import styled from 'styled-components';
9 |
10 | import Cell from '../components/Cell';
11 | import Heading from '../components/Heading';
12 | import Subheading from '../components/Subheading';
13 |
14 | const Timer = styled.div`
15 | background-color: white;
16 | border-radius: 12px;
17 | box-shadow: 0 0 0 3px rgb(0, 16, 48);
18 | font-style: italic;
19 | padding: 16px;
20 | text-align: center;
21 | `;
22 |
23 | class Countdown extends React.Component {
24 | constructor(props) {
25 | super(props);
26 | this.tick.bind(this);
27 | }
28 |
29 | componentWillMount() {
30 | this.setState({ daysLeft: '' });
31 | }
32 |
33 | componentDidMount() {
34 | setInterval(() => this.tick(), 1000);
35 | }
36 |
37 | daysUntil2020() {
38 | const then = moment([2020, 1, 1]);
39 | const now = moment();
40 |
41 | const years = then.diff(now, 'years');
42 | now.add(years, 'years');
43 | const months = then.diff(now, 'months');
44 | now.add(months, 'months');
45 | const days = then.diff(now, 'days');
46 | now.add(days, 'days');
47 | const hours = then.diff(now, 'hours');
48 | now.add(hours, 'hours');
49 | const minutes = then.diff(now, 'minutes');
50 | now.add(minutes, 'minutes');
51 | const seconds = then.diff(now, 'seconds');
52 | now.add(seconds, 'seconds');
53 |
54 | return `${years} year${years === 1 ? '' : 's'}, ${months} month${months === 1 ? '' : 's'}, ${days} day${days === 1 ? '' : 's'}, ${hours} hour${hours === 1 ? '' : 's'}, ${minutes} minute${minutes === 1 ? '' : 's'}, ${seconds} second${seconds === 1 ? '' : 's'}`;
55 | }
56 |
57 | tick() {
58 | this.setState({ daysLeft: this.daysUntil2020() });
59 | }
60 |
61 | render() {
62 | return (
63 |
64 | Grand Opening
65 | 🎉 Opens Jan 1, 2020! 🎉
66 |
67 | {this.state.daysLeft}
68 |
69 | |
70 | );
71 | }
72 | }
73 |
74 | export default Countdown;
75 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/src/views/home.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Home
3 | * …is where the 🌵 is
4 | */
5 |
6 | import React from 'react';
7 | import styled from 'styled-components';
8 |
9 | import Cell from '../components/Cell';
10 | import DesertScene from '../components/DesertScene';
11 | import Heading from '../components/Heading';
12 |
13 | const ThreeD = styled.div`
14 | background-color: black;
15 | box-shadow: 0 32px 64px rgba(248, 106, 0, 0.5);
16 | margin-bottom: 128px;
17 | margin-left: auto;
18 | margin-right: auto;
19 | max-width: 1024px;
20 | position: relative;
21 | width: calc(100vw - 64px);
22 |
23 | & canvas {
24 | display: block;
25 | width: 100%;
26 | }
27 | `;
28 |
29 | const Title = styled.h1`
30 | bottom: 32px;
31 | color: red;
32 | font-family: cursive;
33 | font-size: 1.25em;
34 | font-style: italic;
35 | line-height: 1;
36 | margin-bottom: 0;
37 | margin-top: 0;
38 | position: absolute;
39 | right: 32px;
40 | z-index: 10;
41 | `;
42 |
43 | class Home extends React.Component {
44 | constructor(props) {
45 | super(props);
46 | }
47 |
48 | componentWillMount() {
49 | this.setState({
50 | height: 544.625,
51 | width: 1024,
52 | });
53 | }
54 |
55 | render() {
56 | return (
57 |
58 |
59 | 🌵🏜🌵 Welcome to Cactus World! 🌵🏜🌵
60 | |
61 |
62 |
63 | Artist’s Rendering
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | export default Home;
71 |
--------------------------------------------------------------------------------
/7-webpack-bundle-analyzer/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 |
4 | const HtmlWebpackPlugin = require('html-webpack-plugin');
5 | const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
6 | const OfflinePlugin = require('offline-plugin');
7 | const WebpackChunkHash = require('webpack-chunk-hash');
8 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
9 |
10 | /* Shared Dev & Production */
11 |
12 | const config = {
13 | context: path.resolve(__dirname, 'src'),
14 |
15 | entry: {
16 | index: './index.js',
17 | vendor: ['react', 'react-dom', 'react-router'],
18 | },
19 |
20 | module: {
21 | rules: [
22 | {
23 | test: /\.js$/,
24 | use: 'babel-loader',
25 | exclude: /node_modules/,
26 | },
27 | {
28 | test: /\.(jpg|jpeg|gif|png|svg|woff|woff2)$/,
29 | use: {
30 | loader: 'file-loader',
31 | options: { name: '[name].[hash].[ext]' },
32 | },
33 | },
34 | ],
35 | },
36 |
37 | output: {
38 | path: path.resolve(__dirname, 'dist'),
39 | filename: '[name].bundle.js',
40 | publicPath: '/',
41 | },
42 |
43 | resolve: {
44 | extensions: ['.js'],
45 | modules: [path.resolve(__dirname, 'src'), 'node_modules'],
46 | },
47 |
48 | plugins: [
49 | // New moment.js optimization
50 | new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
51 | new webpack.optimize.CommonsChunkPlugin({
52 | name: 'vendor',
53 | }),
54 | new webpack.optimize.ModuleConcatenationPlugin(),
55 | new HtmlWebpackPlugin({
56 | appMountId: 'app-root',
57 | inlineManifestWebpackName: 'webpackManifest',
58 | template: require('html-webpack-template'),
59 | title: '🌵🏜🌵 Welcome to Cactus World! 🌵🏜🌵',
60 | }),
61 | new OfflinePlugin({ // should be last
62 | AppCache: false,
63 | ServiceWorker: { events: true },
64 | }),
65 | ],
66 |
67 | devServer: {
68 | historyApiFallback: true,
69 | },
70 | };
71 |
72 | if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
73 | config.plugins = [
74 | ...config.plugins,
75 | new BundleAnalyzerPlugin(),
76 | ];
77 | }
78 |
79 | if (process.env.NODE_ENV === 'production') {
80 | config.output.filename = '[name].[chunkhash].js';
81 | config.plugins = [
82 | ...config.plugins,
83 | new webpack.HashedModuleIdsPlugin(),
84 | new WebpackChunkHash(),
85 | new ChunkManifestPlugin({
86 | filename: 'chunk-manifest.json',
87 | manifestVariable: 'webpackManifest',
88 | inlineManifest: true,
89 | }),
90 | ];
91 | }
92 |
93 | module.exports = config;
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Optimizing webpack 3 performance for the front-end
2 |
3 | Sample code for a blog post on optimizing webpack performance.
4 |
5 | The following techniques yield the following size reductions. Some techniques, like [deterministic hashes](https://webpack.js.org/guides/caching/#deterministic-hashes), don’t affect the bundle size, but contribute in other harder-to-measure ways such as caching.
6 |
7 | | Technique | Entry size | gzipped | Savings from previous step |
8 | |-------------------------------------------------------------|-------------:|---------:|---------------------------:|
9 | | **[Base App](./0-base-app)** | `2.5 MB` | `525 kB` | — |
10 | | **[Scope Hoisting](./1-scope-hoisting)** | `2.5 MB` | `525 kB` | — |
11 | | **Uglification** of base app via `webpack -p` | `1 MB` | `266 kB` | + 50–60% |
12 | | **[Dynamic import](./3-dynamic-import)** | `229 kB` | `70 kB` | + 70–75% |
13 | | **[Deterministic Hashes](./4-deterministic-hashes)** | — | — | — |
14 | | **[Commons Chunk Plugin](./5-commons-chunk)** † | `230 kB` | `71 kB` | – 1% |
15 | | **[webpack Offline Plugin](./6-offline-plugin)** | — | — | — |
16 | | **[webpack Bundle Analyzer](./7-webpack-bundle-analyzer)**‡ | — | — | |
17 |
18 | _† The Commons Chunk Plugin technique split one entry file into 2, and the
19 | combined size of both is listed._
20 |
21 | _‡ The Bundle Analyzer technique saved `161 kB` on a particular request. This
22 | is significant savings even if it doesn’t apply to 100% of users._
23 |
24 | ---
25 |
26 | For the full explanation, see the blog post.
27 |
--------------------------------------------------------------------------------