├── .gitignore
├── app
├── constants
│ └── AppConstants.js
├── displayobjects
│ ├── Thingie
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ └── Thingie.js
│ ├── Logo
│ │ ├── logo@2x.png
│ │ └── Logo.js
│ ├── RedLine
│ │ ├── line.png
│ │ └── RedLine.js
│ ├── Background
│ │ ├── soft.jpg
│ │ ├── diagnostic.png
│ │ └── Background.js
│ └── ScaledContainer
│ │ └── ScaledContainer.js
├── utils.js
├── filters
│ └── color
│ │ ├── color.frag
│ │ ├── color.vert
│ │ └── color.js
├── stores
│ ├── Store.js
│ ├── AnimationStore.js
│ ├── AppStore.js
│ └── RendererStore.js
├── index.html
├── Renderer
│ └── Renderer.js
├── screens
│ ├── Loader.js
│ └── Example.js
└── entry.js
├── .babelrc
├── .prettierrc
├── .editorconfig
├── templates
├── store.js
├── displayobject.js
└── reactman.config.js
├── .eslintrc
├── LICENSE
├── webpack.config.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /build
3 | *.log*
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/app/constants/AppConstants.js:
--------------------------------------------------------------------------------
1 | export const canvasWidth = 1920;
2 | export const canvasHeight = 1080;
3 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": ["@babel/plugin-proposal-object-rest-spread"]
4 | }
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true
6 | }
--------------------------------------------------------------------------------
/app/displayobjects/Thingie/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Thingie/1.png
--------------------------------------------------------------------------------
/app/displayobjects/Thingie/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Thingie/2.png
--------------------------------------------------------------------------------
/app/displayobjects/Thingie/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Thingie/3.png
--------------------------------------------------------------------------------
/app/displayobjects/Thingie/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Thingie/4.png
--------------------------------------------------------------------------------
/app/displayobjects/Thingie/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Thingie/5.png
--------------------------------------------------------------------------------
/app/displayobjects/Logo/logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Logo/logo@2x.png
--------------------------------------------------------------------------------
/app/displayobjects/RedLine/line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/RedLine/line.png
--------------------------------------------------------------------------------
/app/displayobjects/Background/soft.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Background/soft.jpg
--------------------------------------------------------------------------------
/app/displayobjects/Background/diagnostic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edwinwebb/pixi-seed/HEAD/app/displayobjects/Background/diagnostic.png
--------------------------------------------------------------------------------
/app/utils.js:
--------------------------------------------------------------------------------
1 | export const checkScreen = (w, h, cw, ch) => w !== cw || h !== ch;
2 | export const randomRange = (m, x) => Math.random() * (x - m) + m;
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.{js,json,jshintrc,html}]
12 | indent_style = space
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/app/filters/color/color.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | varying vec2 vTextureCoord;
4 | uniform sampler2D uSampler;
5 | uniform vec3 color;
6 |
7 | void main(void) {
8 | vec4 c = vec4(color.r, color.g, color.b, 1.0);
9 | vec4 txtC = texture2D(uSampler, vTextureCoord);
10 | gl_FragColor = txtC * c;
11 | }
--------------------------------------------------------------------------------
/templates/store.js:
--------------------------------------------------------------------------------
1 | const NEU = 'seed/animation/NEU';
2 |
3 | export default (state = {}, action = {}) => {
4 | switch (action.type) {
5 | case NEU:
6 | return {
7 | ...state
8 | };
9 | default:
10 | return state;
11 | }
12 | };
13 |
14 | export const neu = () => ({ type: NEU });
15 |
--------------------------------------------------------------------------------
/app/filters/color/color.vert:
--------------------------------------------------------------------------------
1 | attribute vec2 aVertexPosition;
2 | attribute vec2 aTextureCoord;
3 |
4 | uniform mat3 projectionMatrix;
5 | varying vec2 vTextureCoord;
6 |
7 | void main(void){
8 | gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
9 | vTextureCoord = aTextureCoord;
10 | }
--------------------------------------------------------------------------------
/app/stores/Store.js:
--------------------------------------------------------------------------------
1 | import { createStore, combineReducers } from 'redux';
2 | import Animation from './AnimationStore';
3 | import Renderer from './RendererStore';
4 | import App from './AppStore';
5 |
6 | const Combi = combineReducers({
7 | Renderer,
8 | App,
9 | });
10 |
11 | export const AnimationStore = createStore(Animation);
12 |
13 | export default createStore(Combi);
14 |
--------------------------------------------------------------------------------
/app/filters/color/color.js:
--------------------------------------------------------------------------------
1 | import { Filter, utils } from 'pixi.js';
2 | import VERT from './color.vert';
3 | import FRAG from './color.frag';
4 |
5 | export default class ColorFilter extends Filter {
6 | constructor(color = 0xff00ff) {
7 | super(VERT, FRAG);
8 | this.color = color;
9 | }
10 |
11 | set color(color) {
12 | if (typeof color === 'number') {
13 | this.uniforms.color = utils.hex2rgb(color);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/displayobjects/Background/Background.js:
--------------------------------------------------------------------------------
1 | import { Sprite } from 'pixi.js';
2 | import SOFT from './soft.jpg';
3 | import ScaledContainer from '../ScaledContainer/ScaledContainer';
4 |
5 | /**
6 | * Loads the adds the diagnostic image
7 | *
8 | * @exports Background
9 | * @extends ScaledContainer
10 | */
11 | export default class Background extends ScaledContainer {
12 | constructor() {
13 | super();
14 |
15 | const bg = Sprite.from(SOFT);
16 |
17 | this.addChild(bg);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/templates/displayobject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * {%=o.description%}
3 | *
4 | * @exports {%=o.exports%}
5 | * @extends {%=o.extends%}
6 | */
7 | {% if (o.extendpixi) { %}
8 | import { {%=o.extends%} } from 'pixi.js';
9 | {% } else { %}
10 | import { %=o.extends% } from '../{%=o.extends%}/{%=o.extends%}.js';
11 | {% } %}
12 |
13 | export default class {%=o.exports%} extends {%=o.extends%} {
14 |
15 | constructor(...args) {
16 | super(...args);
17 | }
18 | {% for(var i = 0; i < o.functions.length; i++) { %}
19 | {%= o.functions[i] %}() {
20 |
21 | }
22 | {% } %}
23 | }
24 |
--------------------------------------------------------------------------------
/app/displayobjects/Logo/Logo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Pixi Seed Logo
3 | *
4 | * @exports Logo
5 | * @extends Sprite
6 | */
7 |
8 | import { Sprite, Texture } from 'pixi.js';
9 | import LOGO from './logo@2x.png';
10 | import ScaledContainer from '../ScaledContainer/ScaledContainer';
11 |
12 | export default class Logo extends ScaledContainer {
13 | constructor() {
14 | const texture = Texture.from(LOGO);
15 | const sprite = new Sprite(texture);
16 | super();
17 | this.addChild(sprite);
18 | sprite.anchor.x = 0.5;
19 | sprite.anchor.y = 0.5;
20 | sprite.position.set(1920 / 2, 1080 / 2);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/stores/AnimationStore.js:
--------------------------------------------------------------------------------
1 | // template : https://github.com/erikras/ducks-modular-redux
2 |
3 | const TICK = 'seed/animation/TICK';
4 |
5 | const defaultState = {
6 | tick: 1,
7 | previousTick: 0,
8 | startTime: window.performance.now(),
9 | currentTime: window.performance.now(),
10 | };
11 |
12 | export default (state = defaultState, action = {}) => {
13 | switch (action.type) {
14 | case TICK:
15 | return {
16 | ...state,
17 | tick: state.tick + 1,
18 | previousTick: state.tick,
19 | currentTime: window.performance.now(),
20 | };
21 | default:
22 | return state;
23 | }
24 | };
25 |
26 | export const tick = () => ({ type: TICK });
27 |
--------------------------------------------------------------------------------
/app/stores/AppStore.js:
--------------------------------------------------------------------------------
1 | const FILTER_COLOR = 'seed/animation/FILTER_COLOR';
2 | const FILTER_ON = 'seed/animation/FILTER_ON';
3 |
4 | export default (
5 | state = {
6 | color: 0x9c0a3c,
7 | coloron: false,
8 | },
9 | action = {}
10 | ) => {
11 | switch (action.type) {
12 | case FILTER_COLOR:
13 | return {
14 | ...state,
15 | color: parseInt(action.value.replace('#', '0x')),
16 | };
17 | case FILTER_ON:
18 | return {
19 | ...state,
20 | coloron: action.value,
21 | };
22 | default:
23 | return state;
24 | }
25 | };
26 |
27 | export const updateFilterColor = (value) => ({ type: FILTER_COLOR, value });
28 | export const updateFilterIsOn = (value) => ({ type: FILTER_ON, value });
29 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true,
5 | "node": true,
6 | "es6": true
7 | },
8 | "extends": ["eslint:recommended", "prettier"],
9 | "rules": {
10 | "strict": [2, "never"],
11 | "quotes": [
12 | 2,
13 | "single"
14 | ],
15 | "no-unused-vars": [2, {
16 | "vars": "local",
17 | "args": "after-used"
18 | }],
19 | "no-use-before-define": 2,
20 | "no-console": 2,
21 | "no-debugger": 2,
22 | "no-alert": 2,
23 | "no-trailing-spaces": 2,
24 | "key-spacing": [2, {
25 | "beforeColon": false,
26 | "afterColon": true
27 | }],
28 | "no-extra-semi": 2,
29 | "no-func-assign": 2,
30 | "no-invalid-regexp": 2,
31 | "no-dupe-keys": 2,
32 | "comma-dangle": [2, "never"]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/displayobjects/RedLine/RedLine.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Red line gfx
3 | *
4 | * Popmotion Tween Example
5 | *
6 | * @exports RedLine
7 | * @extends Sprite
8 | */
9 |
10 | import { Sprite, Texture } from 'pixi.js';
11 | import LINE from './line.png';
12 | import { randomRange } from '../../utils';
13 | import { easeInOut, animate } from 'popmotion';
14 |
15 | export default class RedLine extends Sprite {
16 | constructor(x, y) {
17 | const texture = Texture.from(LINE);
18 | super(texture);
19 | const offset = randomRange(-500, 500);
20 | this.alpha = randomRange(0.2, 0.4);
21 | this.position.set(x, randomRange(y - 100, y + 200));
22 | this.scale.set(randomRange(0.8, 1.2), randomRange(0.7, 1.4));
23 | animate({
24 | from: this.y,
25 | to: y + offset,
26 | duration: randomRange(200000, 400000),
27 | ease: easeInOut,
28 | flip: Infinity,
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/stores/RendererStore.js:
--------------------------------------------------------------------------------
1 | // template : https://github.com/erikras/ducks-modular-redux
2 |
3 | import { canvasWidth, canvasHeight } from '../constants/AppConstants';
4 |
5 | const windowSize = () => ({
6 | width: window.innerWidth,
7 | height: window.innerHeight,
8 | resolution: window.devicePixelRatio,
9 | stageCenter: { x: window.innerWidth / 2, y: window.innerHeight / 2 },
10 | });
11 |
12 | const defaultState = {
13 | canvasHeight,
14 | canvasWidth,
15 | canvasCenter: {
16 | x: canvasWidth / 2,
17 | y: canvasHeight / 2,
18 | },
19 | ...windowSize(),
20 | };
21 |
22 | const RESIZE = 'seed/animation/TICK';
23 |
24 | export default (state = defaultState, action = {}) => {
25 | switch (action.type) {
26 | case RESIZE:
27 | return {
28 | ...state,
29 | ...windowSize(),
30 | };
31 | default:
32 | return state;
33 | }
34 | };
35 |
36 | export const resize = () => ({ type: RESIZE });
37 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Pixi Seed Project
7 |
35 |
36 |
37 |
38 |
39 |
40 | GLSL Color Mod
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Edwin Webb
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var DEBUG = process.env.NODE_ENV !== 'production';
3 |
4 | module.exports = {
5 | entry: {
6 | index: ['./app/entry.js']
7 | },
8 | output: {
9 | filename: 'bundle.js',
10 | path: path.resolve(__dirname, 'build')
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.js$/,
16 | exclude: /node_modules/,
17 | use: {
18 | loader: 'babel-loader'
19 | }
20 | },
21 | {
22 | test: /\.html$/,
23 | use: {
24 | loader: 'file-loader',
25 | options: {
26 | name: 'index.html'
27 | }
28 | }
29 | },
30 | {
31 | test: /\.jpe?g$|\.svg$|\.png$/,
32 | use: {
33 | loader: 'file-loader'
34 | }
35 | },
36 | {
37 | test: /\.(shader|vert|frag|geom)$/i,
38 | use: 'raw-loader'
39 | }
40 | ]
41 | },
42 | devServer: {
43 | static: path.join(__dirname, 'build'),
44 | compress: true,
45 | port: 8080
46 | },
47 | mode: DEBUG ? 'development' : 'production',
48 | devtool: DEBUG ? 'source-map' : false
49 | };
50 |
--------------------------------------------------------------------------------
/app/Renderer/Renderer.js:
--------------------------------------------------------------------------------
1 | import { Renderer } from 'pixi.js';
2 | import Store, { AnimationStore } from '../stores/Store';
3 | import { tick } from '../stores/AnimationStore';
4 | import { resize } from '../stores/RendererStore';
5 |
6 | /**
7 | * GL Renderer with hooks into a Store
8 | *
9 | * Manages main animation loop
10 | *
11 | * @exports AnimatedRenderer
12 | * @extends Renderer
13 | */
14 | export default class AnimatedRenderer extends Renderer {
15 | constructor(options) {
16 | super(options);
17 |
18 | window.addEventListener('resize', this.resizeHandler.bind(this));
19 |
20 | this.resizeHandler();
21 | }
22 |
23 | /**
24 | * Dispatch resize
25 | * @return {null}
26 | */
27 | resizeHandler() {
28 | Store.dispatch(resize());
29 | this.resize(window.innerWidth, window.innerHeight);
30 | }
31 |
32 | /**
33 | * Start the animation loop
34 | * @return {null}
35 | */
36 | start() {
37 | this.active = true;
38 | window.requestAnimationFrame(this.animate.bind(this));
39 | }
40 |
41 | /**
42 | * Stop the animation loop
43 | * @return {null}
44 | */
45 | stop() {
46 | this.active = false;
47 | }
48 |
49 | /**
50 | * Main animation loop, updates animation store
51 | * @return {null}
52 | */
53 | animate() {
54 | if (this.active) {
55 | window.requestAnimationFrame(this.animate.bind(this));
56 | AnimationStore.dispatch(tick());
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/screens/Loader.js:
--------------------------------------------------------------------------------
1 | import { Graphics, Loader } from 'pixi.js';
2 | import { AnimationStore } from '../stores/Store';
3 | import Store from '../stores/Store';
4 | import ScaledContainer from '../displayobjects/ScaledContainer/ScaledContainer';
5 |
6 | /**
7 | * Loading Screen
8 | *
9 | * @exports LoaderScreen
10 | * @extends ScaledContainer
11 | */
12 |
13 | export default class LoaderScreen extends ScaledContainer {
14 | constructor() {
15 | const { canvasWidth, canvasHeight } = Store.getState().Renderer;
16 |
17 | super();
18 |
19 | this.loader = new Loader();
20 | this.done = () => {};
21 |
22 | // set up a bar
23 | this.bar = new Graphics().beginFill(0xff0000).drawRect(0, -2.5, 200, 5);
24 | this.bar.x = canvasWidth / 2 - 100;
25 | this.bar.y = canvasHeight / 2;
26 | this.bar.scale.x = 0;
27 | this.progress = 0;
28 | this.ease = 0;
29 |
30 | // animate it
31 | this.unsubscribe = AnimationStore.subscribe(() => {
32 | this.ease += (this.progress - this.ease) * 0.03;
33 | this.bar.scale.x = this.ease;
34 | });
35 |
36 | this.addChild(this.bar);
37 | }
38 |
39 | start(assets = []) {
40 | this.loader.add(assets);
41 | this.loader.load();
42 | this.loader.onProgress.add(this.onUpdate.bind(this));
43 | this.loader.onComplete.add(this.onComplete.bind(this));
44 | }
45 |
46 | onUpdate(ldr) {
47 | this.progress = ldr.progress / 100;
48 | }
49 |
50 | onComplete() {
51 | this.done();
52 | this.unsubscribe();
53 | }
54 |
55 | onLoaded(callback = () => {}) {
56 | this.done = callback;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/templates/reactman.config.js:
--------------------------------------------------------------------------------
1 | function commaSplit(i) {
2 | i = i.replace(" ", "");
3 | return i.split(",");
4 | }
5 |
6 | module.exports = {
7 | "templatesFolder" : "./templates/",
8 | "outputFolder" : "./app/",
9 | "scripts" : {
10 | "displayobject" : {
11 | "files" : {
12 | "displayobject.js" : "displayobjects/{%=o.exports%}/{%=o.exports%}.js"
13 | },
14 | "script" : [{
15 | "name": "exports",
16 | "message": "Exports",
17 | "required": true,
18 | "default": "Exports",
19 | "type": "input"
20 | }, {
21 | "name": "extendpixi",
22 | "message": "Extend Pixi.js?",
23 | "default": true,
24 | "required": true,
25 | "type": "confirm"
26 | }, {
27 | "name": "extends",
28 | "message": "Extends",
29 | "default": "Extends",
30 | "required": true,
31 | "type": "input"
32 | }, {
33 | "name": "description",
34 | "message": "Description",
35 | "default": "A display object",
36 | "required": true,
37 | "type": "input"
38 | }, {
39 | "name": "functions",
40 | "message": "Comma seperated list of functions",
41 | "default": "",
42 | "required": false,
43 | "type": "input",
44 | "filter" : commaSplit
45 | }]
46 | },
47 | "store" : {
48 | "files" : {
49 | "store.js" : "stores/{%=o.exports%}.js"
50 | },
51 | "script" : [{
52 | "name": "exports",
53 | "message": "Exports",
54 | "required": true,
55 | "default": "Exports",
56 | "type": "input"
57 | }]
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/displayobjects/Thingie/Thingie.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A small shape
3 | *
4 | * @exports Thingie
5 | * @extends Sprite
6 | */
7 |
8 | import { Sprite, Texture, Point } from 'pixi.js';
9 | import ONE from './1.png';
10 | import TWO from './2.png';
11 | import FOUR from './4.png';
12 | import FIVE from './5.png';
13 |
14 | const assets = [ONE, TWO, FOUR, FIVE];
15 |
16 | export default class Thingie extends Sprite {
17 | constructor() {
18 | const asset = assets[Math.floor(Math.random() * assets.length)];
19 | const texture = Texture.from(asset);
20 | super(texture);
21 | this.speed = Math.random() / 2 + 0.25;
22 | this.offset = new Point(0, 0);
23 | this.targetOffset = new Point(0, 0);
24 | this.originPosition = new Point(0, 0);
25 | this.alpha = 0.9;
26 | }
27 |
28 | setInitialPoint(x, y) {
29 | this.position.set(x, y);
30 | this.originPosition.set(x, y);
31 | }
32 |
33 | update(mousepos) {
34 | const { x, y } = mousepos;
35 | const x1 = this.originPosition.x;
36 | const y1 = this.originPosition.y;
37 | const xDist = x1 - x;
38 | const yDist = y1 - y;
39 | const dist = Math.sqrt(xDist * xDist + yDist * yDist);
40 | if (dist < 200) {
41 | const angle = Math.atan2(yDist, xDist);
42 | const xaDist = Math.cos(angle) * dist;
43 | const yaDist = Math.sin(angle) * dist;
44 | this.targetOffset.set(xaDist, yaDist);
45 | } else {
46 | this.targetOffset.set(0, 0);
47 | }
48 | this.offset.x += (this.targetOffset.x - this.offset.x) * 0.01;
49 | this.offset.y += (this.targetOffset.y - this.offset.y) * 0.01;
50 | this.position.set(
51 | this.originPosition.x + this.offset.x,
52 | this.originPosition.y + this.offset.y
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pixi-seed",
3 | "version": "0.3.8",
4 | "repository": "https://github.com/edwinwebb/pixi-seed",
5 | "description": "Pixi.js project seed / boiler-plate with ES6, Webpack and Redux",
6 | "keywords": [
7 | "webgl",
8 | "pixi",
9 | "pixijs",
10 | "pixi.js",
11 | "seed",
12 | "boilerplate",
13 | "es6",
14 | "glsl"
15 | ],
16 | "contributors": [
17 | "Edwin Webb "
18 | ],
19 | "scripts": {
20 | "start": "webpack serve",
21 | "webpack": "webpack --mode development",
22 | "build": "webpack --mode production",
23 | "format": "find app -name '*.js' | xargs -I{} ./node_modules/.bin/prettier --write --single-quote {}",
24 | "prewebpack": "npm run clean",
25 | "precommit": "lint-staged",
26 | "clean": "rm -rf ./build && mkdir ./build",
27 | "lint": "eslint ./app/**/*.js"
28 | },
29 | "dependencies": {
30 | "pixi.js": "^6.3.0",
31 | "popmotion": "^11.0.3",
32 | "redux": "^4.1.2"
33 | },
34 | "devDependencies": {
35 | "@babel/core": "^7.17.8",
36 | "@babel/plugin-proposal-object-rest-spread": "^7.17.3",
37 | "@babel/preset-env": "^7.16.11",
38 | "babel-eslint": "^10.1.0",
39 | "babel-loader": "^8.2.4",
40 | "eslint": "^8.12.0",
41 | "eslint-config-prettier": "^8.5.0",
42 | "eslint-plugin-prettier": "^4.0.0",
43 | "file-loader": "^6.2.0",
44 | "husky": "^7.0.4",
45 | "lint-staged": "^12.3.7",
46 | "prettier": "2.6.1",
47 | "raw-loader": "^4.0.2",
48 | "rimraf": "^3.0.2",
49 | "webpack": "^5.70.0",
50 | "webpack-cli": "^4.9.2",
51 | "webpack-dev-server": "^4.7.4"
52 | },
53 | "engines": {
54 | "node": ">=8.0.0"
55 | },
56 | "plugins": [
57 | "prettier"
58 | ],
59 | "lint-staged": {
60 | "*.{js,json}": [
61 | "prettier --write --single-quote",
62 | "git add"
63 | ]
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/screens/Example.js:
--------------------------------------------------------------------------------
1 | import { Container, Point } from 'pixi.js';
2 | import { canvasWidth, canvasHeight } from '../constants/AppConstants';
3 | import { AnimationStore } from '../stores/Store';
4 | import Logo from '../displayobjects/Logo/Logo';
5 | import Background from '../displayobjects/Background/Background.js';
6 | import Thingie from '../displayobjects/Thingie/Thingie';
7 | import RedLine from '../displayobjects/RedLine/RedLine';
8 |
9 | const isNear = (p1, p2) => {
10 | const a = p1.x - p2.x;
11 | const b = p1.y - p2.y;
12 | const c = Math.sqrt(a * a + b * b);
13 | return c < 100;
14 | };
15 |
16 | /**
17 | * Main Display Object
18 | *
19 | * @exports Example
20 | * @extends Container
21 | */
22 | export default class Example extends Container {
23 | constructor(...args) {
24 | var bg = new Background();
25 |
26 | super(...args);
27 |
28 | const logo = new Logo();
29 | this.addChild(bg);
30 | this.addLines();
31 | this.addThingies();
32 | this.addChild(logo);
33 | this.mousepos = new Point(500, 500);
34 | }
35 |
36 | addThingies() {
37 | this.thingies = [];
38 | for (let index = 0; index < 200; index++) {
39 | const t = new Thingie();
40 | t.setInitialPoint(
41 | canvasWidth * Math.random(),
42 | (canvasHeight + 300) * Math.random() - 300
43 | );
44 | const near = this.thingies.some((t2) => isNear(t.position, t2.position));
45 | if (!near) {
46 | this.thingies.push(t);
47 | this.addChild(t);
48 | }
49 | }
50 |
51 | AnimationStore.subscribe(() => {
52 | this.thingies.forEach((t) => t.update(this.mousepos));
53 | });
54 |
55 | this.interactive = true;
56 | }
57 |
58 | addLines() {
59 | const count = 100;
60 | for (let index = 0; index < count; index++) {
61 | const y = Math.sin(index * 2) * canvasHeight - 500;
62 | const step = (canvasWidth / count) * index;
63 | const l = new RedLine(step, y);
64 | this.addChild(l);
65 | }
66 | }
67 |
68 | mousemove(e) {
69 | const { x, y } = e.data.global;
70 | if (this.mousepos.x !== x && this.mousepos.y !== y) {
71 | this.mousepos.set(x, y);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/displayobjects/ScaledContainer/ScaledContainer.js:
--------------------------------------------------------------------------------
1 | import { Container, Point } from 'pixi.js';
2 | import Store from '../../stores/Store';
3 | import { resize } from '../../stores/RendererStore';
4 | import { checkScreen } from '../../utils';
5 |
6 | /**
7 | * ScaledContainer
8 | *
9 | * A DisplayObjectContainer which attempts to scale and best-fit into the
10 | * window size dispatched from the RendererStore
11 | *
12 | * @extends Container
13 | * @exports ScaledContainer
14 | */
15 | export default class ScaledContainer extends Container {
16 | /**
17 | * Set target size
18 | * @param {Number} target_w width
19 | * @param {number} target_h height
20 | * @return {null}
21 | */
22 | constructor(...args) {
23 | super(...args);
24 |
25 | this.currentSize = {
26 | w: 0,
27 | h: 0,
28 | };
29 |
30 | // TODO : init resize should come from renderer
31 | this.resizeHandler(
32 | window.innerWidth,
33 | window.innerHeight,
34 | Store.getState().Renderer.canvasWidth,
35 | Store.getState().Renderer.canvasHeight
36 | );
37 |
38 | Store.subscribe(() => {
39 | const { width, height, canvasWidth, canvasHeight } = Store.getState().Renderer;
40 | const { w, h } = this.currentSize;
41 | const needsResize = checkScreen(width, height, w, h);
42 |
43 | if (needsResize) {
44 | this.resizeHandler(width, height, canvasWidth, canvasHeight);
45 | }
46 |
47 | this.currentSize = {
48 | w: width,
49 | h: height,
50 | };
51 | });
52 | }
53 |
54 | /**
55 | * Scales and positions Container to best-fit to target dimensions
56 | * @return {null}
57 | */
58 | resizeHandler(rw, rh, tw = 1920, th = 1080) {
59 | const Xratio = rw / tw;
60 | const Yratio = rh / th;
61 | let scaleRatio = rw > rh ? Xratio : Yratio;
62 | let scale = new Point(scaleRatio, scaleRatio);
63 | let offsetX = rw / 2 - (tw * scaleRatio) / 2;
64 | let offsetY = rh / 2 - (th * scaleRatio) / 2;
65 |
66 | if (th * scaleRatio < rh) {
67 | scaleRatio = Yratio;
68 | scale = new Point(scaleRatio, scaleRatio);
69 | offsetX = rw / 2 - (tw * scaleRatio) / 2;
70 | offsetY = rh / 2 - (th * scaleRatio) / 2;
71 | }
72 |
73 | this.position.x = offsetX;
74 | this.position.y = offsetY;
75 | this.scale = scale;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/entry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * App.js
3 | *
4 | * The main entry point from WebPack
5 | * - Appends render canvas to DOM
6 | * - Calls renderer.render
7 | * - Add Loading Screen and loads assets
8 | * - Adds Example Screen once loading is complete
9 | * - Subscribes and Dispatches to AppStore & DOM
10 | */
11 | import { utils, Container } from 'pixi.js';
12 | import './index.html';
13 | import Renderer from './Renderer/Renderer';
14 | import { AnimationStore } from './stores/Store';
15 | import Store from './stores/Store';
16 | import Example from './screens/Example';
17 | import Loader from './screens/Loader';
18 | import ColorFilter from './filters/color/color';
19 | import { updateFilterColor, updateFilterIsOn } from './stores/AppStore';
20 | import BG from './displayobjects/Background/soft.jpg';
21 | import LOGO from './displayobjects/Logo/logo@2x.png';
22 |
23 | const renderer = new Renderer({ resolution: window.devicePixelRatio }); // an extension of WebGLRenderer which dispatches to RendererStore
24 | const app = new Container(); // Auto scale to screen size, subscribed to RendererStore
25 | const loader = new Loader(); // Basic Loading screen
26 |
27 | // Controls for filter/DOM Redux example
28 | const colorOnInput = document.querySelector('#checkbox');
29 | const colorValueInput = document.querySelector('#color');
30 | const colorFilter = new ColorFilter();
31 |
32 | // append renderer to DOM
33 | document.body.appendChild(renderer.view);
34 |
35 | // animate loop for render
36 | AnimationStore.subscribe(() => {
37 | renderer.render(app);
38 | });
39 |
40 | // Update DOM and App.filters from AppStore
41 | Store.subscribe(() => {
42 | const { color, coloron } = Store.getState().App;
43 | colorOnInput.checked = coloron;
44 | colorValueInput.value = utils.hex2string(color);
45 | colorFilter.color = color;
46 | app.filters = coloron ? [colorFilter] : [];
47 | });
48 |
49 | // Dispatch from DOM to AppStore
50 | colorValueInput.addEventListener('change', (v) =>
51 | Store.dispatch(updateFilterColor(v.currentTarget.value))
52 | );
53 | colorOnInput.addEventListener('change', (v) =>
54 | Store.dispatch(updateFilterIsOn(v.currentTarget.checked))
55 | );
56 |
57 | // Add loader to App Display Object and start loading assets
58 | app.addChild(loader);
59 | loader.start([BG, LOGO]);
60 |
61 | // remove loader then show example once asset loading is complete
62 | loader.onLoaded(() => {
63 | const example = new Example();
64 | app.removeChild(loader);
65 | app.addChild(example);
66 | });
67 |
68 | // start the render loop
69 | renderer.start();
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Pixi Seed
4 |
5 | [Demo](http://edwinwebb.github.io/pixi-seed/)
6 |
7 | This project is designed to bootstrap your [Pixi.js](https://github.com/pixijs/pixi.js) development with modern tooling, technology and project organisation. Use as boilerplate for your next project.
8 |
9 | Webpack with ES6 provides a more class based approach to Pixi.js development and allows you to include assets within your JS. [Reactman](https://www.npmjs.com/package/reactman) enables you to quickly add code to your project and the using Redux Stores helps keep your data in one place.
10 |
11 | The project comes with Render and Animation stores and a ScaledContainer to help work across multiple devices with a ‘best-fit’ rendering methodology. The code contains demos for TweenJS and renderer update loops.
12 |
13 | ## V3 Updates
14 | * Change stores to REDUX /w ducks
15 | * Update to Webpack V2
16 | * Update to Pixi V4.6
17 | * Added a loader screen
18 | * Added a custom [glsl filter example](https://github.com/edwinwebb/pixi-seed/tree/master/app/filters/color)
19 | * PopMotion with minor [examples](https://github.com/edwinwebb/pixi-seed/tree/master/app/displayobjects/RedLine/RedLine.js)
20 | * Animation loop [examples](https://github.com/edwinwebb/pixi-seed/tree/master/app/displayobjects/THingie/Thingie.js)
21 | * HTML forms to Redux/Pixi.js examples
22 |
23 | ## TODO
24 | * Move loader to REDUX
25 | * Script to redo package.json on new project
26 | * Add a screen manager
27 |
28 | ## Getting started
29 |
30 | Clone the project, remove the git repository and install to get going:
31 |
32 | ```bash
33 | git clone --depth=1 https://github.com/edwinwebb/pixi-seed.git my-project
34 | cd my-project
35 | rm -rf .git
36 | npm install
37 | npm start
38 | ```
39 |
40 | Then visit http://localhost:8080
41 |
42 | ## Strapped Files
43 | You can configure your canvas size in the AppConstants.js file.
44 |
45 | ```js
46 | export const canvasWidth = 1920;
47 | export const canvasHeight = 1080;
48 | ```
49 |
50 | The ScaledObjectContainer will try a best fit approach. Used in Logo and Background to auto scale.
51 |
52 | RedLine.js gives a Popmotion example
53 |
54 | Thingie.js gives an animation loop update example.
55 |
56 |
57 | ## npm scripts
58 |
59 | * `npm start` - Build and start the app in development mode at http://localhost:8080
60 | * `npm run build` - Run a production build, outputs to ./build/
61 | * `npm run lint` - Lint your code
62 | * `npm run reactman` - Generate code for a DisplayObject or Store from command prompt. See [here](http://edwinwebb.github.io/reactman/).
63 |
64 | ## Static assets
65 |
66 | `import` asset files from within your JavaScript component files. To add more
67 | filetypes, look at the webpack.config.js and add a file loader.
68 |
69 | ```javascript
70 | // Filename: app.js
71 | import assetURL from './logo.png';
72 | ```
73 |
74 | ## License
75 |
76 | Copyright (c) 2017 Edwin Webb
77 |
78 | MIT (http://opensource.org/licenses/MIT)
79 |
--------------------------------------------------------------------------------