├── .gitignore
├── css
└── styles.css
├── scss
└── styles.scss
├── index.html
├── demos
├── events
│ ├── index.html
│ ├── shaders
│ │ ├── backgroundFragment.glsl
│ │ └── stageFragment.glsl
│ └── js
│ │ └── scripts.js
├── images
│ ├── index.html
│ ├── shaders
│ │ ├── backgroundFragment.glsl
│ │ └── stageFragment.glsl
│ └── js
│ │ └── scripts.js
├── animation
│ ├── index.html
│ ├── shaders
│ │ ├── backgroundFragment.glsl
│ │ └── stageFragment.glsl
│ └── js
│ │ └── scripts.js
├── background
│ ├── index.html
│ ├── shaders
│ │ └── backgroundFragment.glsl
│ └── js
│ │ └── scripts.js
├── distortion
│ ├── index.html
│ ├── shaders
│ │ ├── backgroundFragment.glsl
│ │ └── stageFragment.glsl
│ └── js
│ │ └── scripts.js
├── init-pixi
│ ├── index.html
│ └── js
│ │ └── scripts.js
├── final
│ ├── index.html
│ ├── shaders
│ │ ├── backgroundFragment.glsl
│ │ └── stageFragment.glsl
│ └── js
│ │ └── scripts.js
├── rects-final
│ ├── index.html
│ ├── shaders
│ │ ├── backgroundFragment.glsl
│ │ └── stageFragment.glsl
│ └── js
│ │ └── scripts.js
├── rects-pixi
│ ├── index.html
│ ├── shaders
│ │ ├── backgroundFragment.glsl
│ │ └── stageFragment.glsl
│ └── js
│ │ └── scripts.js
└── rects
│ ├── index.html
│ └── js
│ └── scripts.js
├── README.md
├── gulpfile.js
├── package.json
├── LICENSE
├── shaders
├── backgroundFragment.glsl
└── stageFragment.glsl
└── js
└── scripts.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | temp/
3 |
--------------------------------------------------------------------------------
/css/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | overflow: hidden;
4 | background-color: black;
5 | }
6 |
--------------------------------------------------------------------------------
/scss/styles.scss:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | overflow: hidden;
4 | background-color: black;
5 | }
6 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery using PixiJS and WebGL
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/events/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Events
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/images/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Images
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/animation/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Animation
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/background/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Background
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/distortion/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Distortion
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/init-pixi/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Init PixiJS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery using PixiJS and WebGL
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/rects-final/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Rects Final
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/rects-pixi/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Rects in PixiJS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demos/rects/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Images Gallery - Rects
8 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Images Gallery
2 |
3 | Images Gallery using PixiJS and WebGL
4 |
5 | - **Tutorial**: [https://css-tricks.com/building-an-images-gallery-using-pixijs-and-webgl/](https://css-tricks.com/building-an-images-gallery-using-pixijs-and-webgl/)
6 | - **Demo**: [https://lmgonzalves.github.io/images-gallery/](https://lmgonzalves.github.io/images-gallery/)
7 | - **Codepen**: [https://codepen.io/lmgonzalves/pen/oRdwQJ](https://codepen.io/lmgonzalves/pen/oRdwQJ)
8 |
9 | ## Credits
10 |
11 | - Inspired by a [Dribbble shot by Zhenya Rynzhuk](https://dribbble.com/shots/5490545-Blown-Art-Works-and-News-Platform-Talents-Page-Animation).
12 | - Images are from [Unsplash](https://unsplash.com), using the [Unsplash Source API](https://source.unsplash.com/).
13 |
--------------------------------------------------------------------------------
/demos/init-pixi/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Get canvas view
4 | const view = document.querySelector('.view')
5 | let width, height, app
6 |
7 | // Set dimensions
8 | function initDimensions () {
9 | width = window.innerWidth
10 | height = window.innerHeight
11 | }
12 |
13 | // Init the PixiJS Application
14 | function initApp () {
15 | // Create a PixiJS Application, using the view (canvas) provided
16 | app = new PIXI.Application({ view })
17 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
18 | app.renderer.autoDensity = true
19 | // Resize the view to match viewport dimensions
20 | app.renderer.resize(width, height)
21 | }
22 |
23 | // Init everything
24 | function init () {
25 | initDimensions()
26 | initApp()
27 | }
28 | // Initial call
29 | init()
30 |
31 | })()
32 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp')
2 | var browserSync = require('browser-sync')
3 | var sass = require('gulp-sass')
4 | var prefix = require('gulp-autoprefixer')
5 |
6 | gulp.task('serve', ['sass'], function () {
7 | browserSync.init({
8 | server: {
9 | baseDir: './'
10 | },
11 | open: false,
12 | online: false,
13 | notify: false
14 | })
15 |
16 | gulp.watch('scss/*.scss', ['sass'])
17 | gulp.watch(['**/*.html', 'js/*', 'shaders/*']).on('change', browserSync.reload)
18 | })
19 |
20 | gulp.task('sass', function () {
21 | return gulp.src('scss/*.scss')
22 | .pipe(sass({
23 | outputStyle: 'expanded',
24 | includePaths: ['scss']
25 | }))
26 | .pipe(prefix(['last 5 versions'], { cascade: true }))
27 | .pipe(gulp.dest('css'))
28 | .pipe(browserSync.reload({ stream: true }))
29 | })
30 |
31 | gulp.task('default', ['serve'])
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "images-gallery",
3 | "version": "0.0.1",
4 | "description": "Images Gallery using PixiJS and WebGL",
5 | "main": "js/scripts.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/lmgonzalves/images-gallery.git"
9 | },
10 | "scripts": {
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "keywords": [
14 | "javascript",
15 | "animation",
16 | "webgl"
17 | ],
18 | "author": "lmgonzalves",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/lmgonzalves/images-gallery/issues"
22 | },
23 | "homepage": "https://github.com/lmgonzalves/images-gallery",
24 | "devDependencies": {
25 | "browser-sync": "^2.23.6",
26 | "gulp": "^3.9.1",
27 | "gulp-autoprefixer": "^5.0.0",
28 | "gulp-rename": "^1.2.2",
29 | "gulp-sass": "^3.1.0",
30 | "gulp-uglify": "^3.0.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/demos/events/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
10 | float isGridLine (vec2 coord) {
11 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
12 | vec2 gridCoords = fract(coord / pixelsPerGrid);
13 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
14 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
15 | float isGridLine = max(gridLine.x, gridLine.y);
16 | return isGridLine;
17 | }
18 |
19 | // Main function
20 | void main () {
21 | // Coordinates for the current pixel
22 | vec2 coord = gl_FragCoord.xy;
23 | // Set `color` to black
24 | vec3 color = vec3(0.0);
25 | // If it is a grid line, change blue channel to 0.3
26 | color.b = isGridLine(coord) * 0.3;
27 | // Assing the final rgba color to `gl_FragColor`
28 | gl_FragColor = vec4(color, 1.0);
29 | }
30 |
--------------------------------------------------------------------------------
/demos/background/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
10 | float isGridLine (vec2 coord) {
11 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
12 | vec2 gridCoords = fract(coord / pixelsPerGrid);
13 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
14 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
15 | float isGridLine = max(gridLine.x, gridLine.y);
16 | return isGridLine;
17 | }
18 |
19 | // Main function
20 | void main () {
21 | // Coordinates for the current pixel
22 | vec2 coord = gl_FragCoord.xy;
23 | // Set `color` to black
24 | vec3 color = vec3(0.0);
25 | // If it is a grid line, change blue channel to 0.3
26 | color.b = isGridLine(coord) * 0.3;
27 | // Assing the final rgba color to `gl_FragColor`
28 | gl_FragColor = vec4(color, 1.0);
29 | }
30 |
--------------------------------------------------------------------------------
/demos/distortion/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
10 | float isGridLine (vec2 coord) {
11 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
12 | vec2 gridCoords = fract(coord / pixelsPerGrid);
13 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
14 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
15 | float isGridLine = max(gridLine.x, gridLine.y);
16 | return isGridLine;
17 | }
18 |
19 | // Main function
20 | void main () {
21 | // Coordinates for the current pixel
22 | vec2 coord = gl_FragCoord.xy;
23 | // Set `color` to black
24 | vec3 color = vec3(0.0);
25 | // If it is a grid line, change blue channel to 0.3
26 | color.b = isGridLine(coord) * 0.3;
27 | // Assing the final rgba color to `gl_FragColor`
28 | gl_FragColor = vec4(color, 1.0);
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 lmgonzalves
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 |
--------------------------------------------------------------------------------
/demos/animation/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | uniform vec2 uPointerDiff;
10 |
11 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
12 | float isGridLine (vec2 coord) {
13 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
14 | vec2 gridCoords = fract(coord / pixelsPerGrid);
15 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
16 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
17 | float isGridLine = max(gridLine.x, gridLine.y);
18 | return isGridLine;
19 | }
20 |
21 | // Main function
22 | void main () {
23 | // Coordinates minus the `uPointerDiff` value
24 | vec2 coord = gl_FragCoord.xy - uPointerDiff;
25 | // Set `color` to black
26 | vec3 color = vec3(0.0);
27 | // If it is a grid line, change blue channel to 0.3
28 | color.b = isGridLine(coord) * 0.3;
29 | // Assing the final rgba color to `gl_FragColor`
30 | gl_FragColor = vec4(color, 1.0);
31 | }
32 |
--------------------------------------------------------------------------------
/demos/rects-pixi/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | uniform vec2 uPointerDiff;
10 |
11 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
12 | float isGridLine (vec2 coord) {
13 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
14 | vec2 gridCoords = fract(coord / pixelsPerGrid);
15 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
16 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
17 | float isGridLine = max(gridLine.x, gridLine.y);
18 | return isGridLine;
19 | }
20 |
21 | // Main function
22 | void main () {
23 | // Coordinates minus the `uPointerDiff` value
24 | vec2 coord = gl_FragCoord.xy - uPointerDiff;
25 | // Set `color` to black
26 | vec3 color = vec3(0.0);
27 | // If it is a grid line, change blue channel to 0.3
28 | color.b = isGridLine(coord) * 0.3;
29 | // Assing the final rgba color to `gl_FragColor`
30 | gl_FragColor = vec4(color, 1.0);
31 | }
32 |
--------------------------------------------------------------------------------
/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | uniform vec2 uPointerDiff;
10 |
11 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
12 | float isGridLine (vec2 coord) {
13 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
14 | vec2 gridCoords = fract(coord / pixelsPerGrid);
15 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
16 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
17 | float isGridLine = max(gridLine.x, gridLine.y);
18 | return isGridLine;
19 | }
20 |
21 | // Main function
22 | void main () {
23 | // Coordinates minus the `uPointerDiff` value, and plus an offset
24 | vec2 coord = gl_FragCoord.xy - uPointerDiff + vec2(10.0);
25 | // Set `color` to black
26 | vec3 color = vec3(0.0);
27 | // If it is a grid line, change blue channel to 0.3
28 | color.b = isGridLine(coord) * 0.3;
29 | // Assing the final rgba color to `gl_FragColor`
30 | gl_FragColor = vec4(color, 1.0);
31 | }
32 |
--------------------------------------------------------------------------------
/demos/final/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | uniform vec2 uPointerDiff;
10 |
11 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
12 | float isGridLine (vec2 coord) {
13 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
14 | vec2 gridCoords = fract(coord / pixelsPerGrid);
15 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
16 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
17 | float isGridLine = max(gridLine.x, gridLine.y);
18 | return isGridLine;
19 | }
20 |
21 | // Main function
22 | void main () {
23 | // Coordinates minus the `uPointerDiff` value, and plus an offset
24 | vec2 coord = gl_FragCoord.xy - uPointerDiff + vec2(10.0);
25 | // Set `color` to black
26 | vec3 color = vec3(0.0);
27 | // If it is a grid line, change blue channel to 0.3
28 | color.b = isGridLine(coord) * 0.3;
29 | // Assing the final rgba color to `gl_FragColor`
30 | gl_FragColor = vec4(color, 1.0);
31 | }
32 |
--------------------------------------------------------------------------------
/demos/images/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | uniform vec2 uPointerDiff;
10 |
11 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
12 | float isGridLine (vec2 coord) {
13 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
14 | vec2 gridCoords = fract(coord / pixelsPerGrid);
15 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
16 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
17 | float isGridLine = max(gridLine.x, gridLine.y);
18 | return isGridLine;
19 | }
20 |
21 | // Main function
22 | void main () {
23 | // Coordinates minus the `uPointerDiff` value, and plus an offset
24 | vec2 coord = gl_FragCoord.xy - uPointerDiff + vec2(10.0);
25 | // Set `color` to black
26 | vec3 color = vec3(0.0);
27 | // If it is a grid line, change blue channel to 0.3
28 | color.b = isGridLine(coord) * 0.3;
29 | // Assing the final rgba color to `gl_FragColor`
30 | gl_FragColor = vec4(color, 1.0);
31 | }
32 |
--------------------------------------------------------------------------------
/demos/rects-final/shaders/backgroundFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/MdlGRr
2 |
3 | // It is required to set the float precision for fragment shaders in OpenGL ES
4 | // More info here: https://stackoverflow.com/a/28540641/4908989
5 | #ifdef GL_ES
6 | precision mediump float;
7 | #endif
8 |
9 | uniform vec2 uPointerDiff;
10 |
11 | // This function returns 1 if `coord` correspond to a grid line, 0 otherwise
12 | float isGridLine (vec2 coord) {
13 | vec2 pixelsPerGrid = vec2(50.0, 50.0);
14 | vec2 gridCoords = fract(coord / pixelsPerGrid);
15 | vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
16 | vec2 gridLine = step(gridPixelCoords, vec2(1.0));
17 | float isGridLine = max(gridLine.x, gridLine.y);
18 | return isGridLine;
19 | }
20 |
21 | // Main function
22 | void main () {
23 | // Coordinates minus the `uPointerDiff` value, and plus an offset
24 | vec2 coord = gl_FragCoord.xy - uPointerDiff + vec2(10.0);
25 | // Set `color` to black
26 | vec3 color = vec3(0.0);
27 | // If it is a grid line, change blue channel to 0.3
28 | color.b = isGridLine(coord) * 0.3;
29 | // Assing the final rgba color to `gl_FragColor`
30 | gl_FragColor = vec4(color, 1.0);
31 | }
32 |
--------------------------------------------------------------------------------
/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/events/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/final/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/images/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/animation/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/distortion/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/rects-final/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/rects-pixi/shaders/stageFragment.glsl:
--------------------------------------------------------------------------------
1 | // Adapted from: https://www.shadertoy.com/view/4lSGRw
2 |
3 | #ifdef GL_ES
4 | precision mediump float;
5 | #endif
6 |
7 | // Uniforms from Javascript
8 | uniform vec2 uResolution;
9 | uniform float uPointerDown;
10 |
11 | // The texture is defined by PixiJS
12 | varying vec2 vTextureCoord;
13 | uniform sampler2D uSampler;
14 |
15 | // Function used to get the distortion effect
16 | vec2 computeUV (vec2 uv, float k, float kcube) {
17 | vec2 t = uv - 0.5;
18 | float r2 = t.x * t.x + t.y * t.y;
19 | float f = 0.0;
20 | if (kcube == 0.0) {
21 | f = 1.0 + r2 * k;
22 | } else {
23 | f = 1.0 + r2 * (k + kcube * sqrt(r2));
24 | }
25 | vec2 nUv = f * t + 0.5;
26 | nUv.y = 1.0 - nUv.y;
27 | return nUv;
28 | }
29 |
30 | void main () {
31 | // Normalized coordinates
32 | vec2 uv = gl_FragCoord.xy / uResolution.xy;
33 |
34 | // Settings for the effect
35 | // Multiplied by `uPointerDown`, a value between 0 and 1
36 | float k = -1.0 * uPointerDown;
37 | float kcube = 0.5 * uPointerDown;
38 | float offset = 0.02 * uPointerDown;
39 |
40 | // Get each channel's color using the texture provided by PixiJS
41 | // and the `computeUV` function
42 | float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
43 | float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
44 | float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
45 |
46 | // Assing the final rgba color to `gl_FragColor`
47 | gl_FragColor = vec4(red, green, blue, 1.0);
48 | }
49 |
--------------------------------------------------------------------------------
/demos/background/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Get canvas view
4 | const view = document.querySelector('.view')
5 | // Loaded resources will be here
6 | const resources = PIXI.Loader.shared.resources
7 | let width, height, app, background
8 |
9 | // Set dimensions
10 | function initDimensions () {
11 | width = window.innerWidth
12 | height = window.innerHeight
13 | }
14 |
15 | // Init the PixiJS Application
16 | function initApp () {
17 | // Create a PixiJS Application, using the view (canvas) provided
18 | app = new PIXI.Application({ view })
19 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
20 | app.renderer.autoDensity = true
21 | // Resize the view to match viewport dimensions
22 | app.renderer.resize(width, height)
23 | }
24 |
25 | // Init the gridded background
26 | function initBackground () {
27 | // Create a new empty Sprite and define its size
28 | background = new PIXI.Sprite()
29 | background.width = width
30 | background.height = height
31 | // Get the code for the fragment shader from the loaded resources
32 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
33 | // Create a new Filter using the fragment shader
34 | // We don't need a custom vertex shader, so we set it as `undefined`
35 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader)
36 | // Assign the filter to the background Sprite
37 | background.filters = [backgroundFilter]
38 | // Add the background to the stage
39 | app.stage.addChild(background)
40 | }
41 |
42 | // Init everything
43 | function init () {
44 | initDimensions()
45 | initApp()
46 | initBackground()
47 | }
48 |
49 | // Load resources, then init the app
50 | PIXI.Loader.shared.add([
51 | 'shaders/backgroundFragment.glsl'
52 | ]).load(init)
53 |
54 | })()
55 |
--------------------------------------------------------------------------------
/demos/distortion/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Get canvas view
4 | const view = document.querySelector('.view')
5 | // Loaded resources will be here
6 | const resources = PIXI.Loader.shared.resources
7 | // Target for pointer. If down, value is 1, else value is 0
8 | // Here we set it to 1 to see the effect, but initially it will be 0
9 | let pointerDownTarget = 1
10 | let width, height, app, background, uniforms
11 |
12 | // Set dimensions
13 | function initDimensions () {
14 | width = window.innerWidth
15 | height = window.innerHeight
16 | }
17 |
18 | // Set initial values for uniforms
19 | function initUniforms () {
20 | uniforms = {
21 | uResolution: new PIXI.Point(width, height),
22 | uPointerDown: pointerDownTarget
23 | }
24 | }
25 |
26 | // Init the PixiJS Application
27 | function initApp () {
28 | // Create a PixiJS Application, using the view (canvas) provided
29 | app = new PIXI.Application({ view })
30 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
31 | app.renderer.autoDensity = true
32 | // Resize the view to match viewport dimensions
33 | app.renderer.resize(width, height)
34 |
35 | // Set the distortion filter for the entire stage
36 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
37 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
38 | app.stage.filters = [stageFilter]
39 | }
40 |
41 | // Init the gridded background
42 | function initBackground () {
43 | // Create a new empty Sprite and define its size
44 | background = new PIXI.Sprite()
45 | background.width = width
46 | background.height = height
47 | // Get the code for the fragment shader from the loaded resources
48 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
49 | // Create a new Filter using the fragment shader
50 | // We don't need a custom vertex shader, so we set it as `undefined`
51 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader)
52 | // Assign the filter to the background Sprite
53 | background.filters = [backgroundFilter]
54 | // Add the background to the stage
55 | app.stage.addChild(background)
56 | }
57 |
58 | // Init everything
59 | function init () {
60 | initDimensions()
61 | initUniforms()
62 | initApp()
63 | initBackground()
64 | }
65 |
66 | // Load resources, then init the app
67 | PIXI.Loader.shared.add([
68 | 'shaders/stageFragment.glsl',
69 | 'shaders/backgroundFragment.glsl'
70 | ]).load(init)
71 |
72 | })()
73 |
--------------------------------------------------------------------------------
/demos/events/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Get canvas view
4 | const view = document.querySelector('.view')
5 | // Loaded resources will be here
6 | const resources = PIXI.Loader.shared.resources
7 | // Target for pointer. If down, value is 1, else value is 0
8 | let pointerDownTarget = 0
9 | // Useful variables to keep track of the pointer
10 | let pointerStart = new PIXI.Point()
11 | let pointerDiffStart = new PIXI.Point()
12 | let width, height, app, background, uniforms, diffX, diffY
13 |
14 | // Set dimensions
15 | function initDimensions () {
16 | width = window.innerWidth
17 | height = window.innerHeight
18 | }
19 |
20 | // Set initial values for uniforms
21 | function initUniforms () {
22 | uniforms = {
23 | uResolution: new PIXI.Point(width, height),
24 | uPointerDiff: new PIXI.Point(),
25 | uPointerDown: pointerDownTarget
26 | }
27 | }
28 |
29 | // Init the PixiJS Application
30 | function initApp () {
31 | // Create a PixiJS Application, using the view (canvas) provided
32 | app = new PIXI.Application({ view })
33 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
34 | app.renderer.autoDensity = true
35 | // Resize the view to match viewport dimensions
36 | app.renderer.resize(width, height)
37 |
38 | // Set the distortion filter for the entire stage
39 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
40 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
41 | app.stage.filters = [stageFilter]
42 | }
43 |
44 | // Init the gridded background
45 | function initBackground () {
46 | // Create a new empty Sprite and define its size
47 | background = new PIXI.Sprite()
48 | background.width = width
49 | background.height = height
50 | // Get the code for the fragment shader from the loaded resources
51 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
52 | // Create a new Filter using the fragment shader
53 | // We don't need a custom vertex shader, so we set it as `undefined`
54 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader)
55 | // Assign the filter to the background Sprite
56 | background.filters = [backgroundFilter]
57 | // Add the background to the stage
58 | app.stage.addChild(background)
59 | }
60 |
61 | // Start listening events
62 | function initEvents () {
63 | // Make stage interactive, so it can listen to events
64 | app.stage.interactive = true
65 |
66 | // Pointer & touch events are normalized into
67 | // the `pointer*` events for handling different events
68 | app.stage
69 | .on('pointerdown', onPointerDown)
70 | .on('pointerup', onPointerUp)
71 | .on('pointerupoutside', onPointerUp)
72 | .on('pointermove', onPointerMove)
73 | }
74 |
75 | // On pointer down, save coordinates and set pointerDownTarget
76 | function onPointerDown (e) {
77 | console.log('down')
78 | const { x, y } = e.data.global
79 | pointerDownTarget = 1
80 | pointerStart.set(x, y)
81 | pointerDiffStart = uniforms.uPointerDiff.clone()
82 | }
83 |
84 | // On pointer up, set pointerDownTarget
85 | function onPointerUp () {
86 | console.log('up')
87 | pointerDownTarget = 0
88 | }
89 |
90 | // On pointer move, calculate coordinates diff
91 | function onPointerMove (e) {
92 | const { x, y } = e.data.global
93 | if (pointerDownTarget) {
94 | console.log('dragging')
95 | diffX = pointerDiffStart.x + (x - pointerStart.x)
96 | diffY = pointerDiffStart.y + (y - pointerStart.y)
97 | }
98 | }
99 |
100 | // Init everything
101 | function init () {
102 | initDimensions()
103 | initUniforms()
104 | initApp()
105 | initBackground()
106 | initEvents()
107 | }
108 |
109 | // Load resources, then init the app
110 | PIXI.Loader.shared.add([
111 | 'shaders/stageFragment.glsl',
112 | 'shaders/backgroundFragment.glsl'
113 | ]).load(init)
114 |
115 | })()
116 |
--------------------------------------------------------------------------------
/demos/rects/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Class to generate a random masonry layout, using a square grid as base
4 | class Grid {
5 |
6 | // The constructor receives all the following parameters:
7 | // - gridSize: The size (width and height) for smallest unit size
8 | // - gridColumns: Number of columns for the grid (width = gridColumns * gridSize)
9 | // - gridRows: Number of rows for the grid (height = gridRows * gridSize)
10 | // - gridMin: Min width and height limits for rectangles (in grid units)
11 | constructor(gridSize, gridColumns, gridRows, gridMin) {
12 | this.gridSize = gridSize
13 | this.gridColumns = gridColumns
14 | this.gridRows = gridRows
15 | this.gridMin = gridMin
16 | this.rects = []
17 | this.currentRects = [{ x: 0, y: 0, w: this.gridColumns, h: this.gridRows }]
18 | }
19 |
20 | // Takes the first rectangle on the list, and divides it in 2 more rectangles if possible
21 | splitCurrentRect () {
22 | if (this.currentRects.length) {
23 | const currentRect = this.currentRects.shift()
24 | const cutVertical = currentRect.w > currentRect.h
25 | const cutSide = cutVertical ? currentRect.w : currentRect.h
26 | const cutSize = cutVertical ? 'w' : 'h'
27 | const cutAxis = cutVertical ? 'x' : 'y'
28 | if (cutSide > this.gridMin * 2) {
29 | const rect1Size = randomInRange(this.gridMin, cutSide - this.gridMin)
30 | const rect1 = Object.assign({}, currentRect, { [cutSize]: rect1Size })
31 | const rect2 = Object.assign({}, currentRect, { [cutAxis]: currentRect[cutAxis] + rect1Size, [cutSize]: currentRect[cutSize] - rect1Size })
32 | drawRect(rect1)
33 | drawRect(rect2)
34 | this.currentRects.push(rect1, rect2)
35 | }
36 | else {
37 | this.rects.push(currentRect)
38 | this.splitCurrentRect()
39 | }
40 | } else {
41 | console.log('end')
42 | }
43 | }
44 |
45 | // Call `splitCurrentRect` until there is no more rectangles on the list
46 | // Then return the list of rectangles
47 | generateRects () {
48 | while (this.currentRects.length) {
49 | this.splitCurrentRect()
50 | }
51 | return this.rects
52 | }
53 | }
54 |
55 | // Generate a random integer in the range provided
56 | function randomInRange (min, max) {
57 | return Math.floor(Math.random() * (max - min + 1)) + min
58 | }
59 |
60 | // Generate a random color
61 | function randomColor () {
62 | const color = [0, 0, 0]
63 | for (let i = 0; i <= 2; i++) {
64 | if (Math.random() < 0.66666)
65 | color[i] = 32 + parseInt(Math.random() * 192)
66 | }
67 | return 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')'
68 | }
69 |
70 | // Draw a rect in the canvas
71 | function drawRect (r) {
72 | ctx.fillStyle = r.color || randomColor()
73 | ctx.fillRect(r.x * gridSize, r.y * gridSize, r.w * gridSize, r.h * gridSize)
74 | }
75 |
76 | // Clear the canvas
77 | function clearCanvas () {
78 | ctx.fillStyle = 'white'
79 | ctx.fillRect(0, 0, width, height)
80 | }
81 |
82 | // Variables and settings
83 | const next = document.getElementById('next')
84 | const all = document.getElementById('all')
85 | const reset = document.getElementById('reset')
86 | const canvas = document.getElementById('canvas')
87 | const ctx = canvas.getContext('2d')
88 | const gridSize = 50
89 | const gridColumns = 20
90 | const gridRows = 10
91 | const gridMin = 3
92 | const width = gridSize * gridColumns
93 | const height = gridSize * gridRows
94 | canvas.width = width
95 | canvas.height = height
96 |
97 | // Start a new grid
98 | let grid
99 | function start () {
100 | grid = new Grid(gridSize, gridColumns, gridRows, gridMin)
101 | }
102 | start()
103 |
104 | // Listen for click on buttons and run the algoritm
105 |
106 | next.addEventListener('click', () => {
107 | grid.splitCurrentRect()
108 | })
109 |
110 | all.addEventListener('click', () => {
111 | grid.generateRects()
112 | })
113 |
114 | reset.addEventListener('click', () => {
115 | clearCanvas()
116 | start()
117 | })
118 |
119 | })()
120 |
--------------------------------------------------------------------------------
/demos/animation/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Get canvas view
4 | const view = document.querySelector('.view')
5 | // Loaded resources will be here
6 | const resources = PIXI.Loader.shared.resources
7 | // Target for pointer. If down, value is 1, else value is 0
8 | let pointerDownTarget = 0
9 | // Useful variables to keep track of the pointer
10 | let pointerStart = new PIXI.Point()
11 | let pointerDiffStart = new PIXI.Point()
12 | let width, height, app, background, uniforms, diffX, diffY
13 |
14 | // Set dimensions
15 | function initDimensions () {
16 | width = window.innerWidth
17 | height = window.innerHeight
18 | diffX = 0
19 | diffY = 0
20 | }
21 |
22 | // Set initial values for uniforms
23 | function initUniforms () {
24 | uniforms = {
25 | uResolution: new PIXI.Point(width, height),
26 | uPointerDiff: new PIXI.Point(),
27 | uPointerDown: pointerDownTarget
28 | }
29 | }
30 |
31 | // Init the PixiJS Application
32 | function initApp () {
33 | // Create a PixiJS Application, using the view (canvas) provided
34 | app = new PIXI.Application({ view })
35 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
36 | app.renderer.autoDensity = true
37 | // Resize the view to match viewport dimensions
38 | app.renderer.resize(width, height)
39 |
40 | // Set the distortion filter for the entire stage
41 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
42 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
43 | app.stage.filters = [stageFilter]
44 | }
45 |
46 | // Init the gridded background
47 | function initBackground () {
48 | // Create a new empty Sprite and define its size
49 | background = new PIXI.Sprite()
50 | background.width = width
51 | background.height = height
52 | // Get the code for the fragment shader from the loaded resources
53 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
54 | // Create a new Filter using the fragment shader
55 | // We don't need a custom vertex shader, so we set it as `undefined`
56 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader, uniforms)
57 | // Assign the filter to the background Sprite
58 | background.filters = [backgroundFilter]
59 | // Add the background to the stage
60 | app.stage.addChild(background)
61 | }
62 |
63 | // Start listening events
64 | function initEvents () {
65 | // Make stage interactive, so it can listen to events
66 | app.stage.interactive = true
67 |
68 | // Pointer & touch events are normalized into
69 | // the `pointer*` events for handling different events
70 | app.stage
71 | .on('pointerdown', onPointerDown)
72 | .on('pointerup', onPointerUp)
73 | .on('pointerupoutside', onPointerUp)
74 | .on('pointermove', onPointerMove)
75 | }
76 |
77 | // On pointer down, save coordinates and set pointerDownTarget
78 | function onPointerDown (e) {
79 | const { x, y } = e.data.global
80 | pointerDownTarget = 1
81 | pointerStart.set(x, y)
82 | pointerDiffStart = uniforms.uPointerDiff.clone()
83 | }
84 |
85 | // On pointer up, set pointerDownTarget
86 | function onPointerUp () {
87 | pointerDownTarget = 0
88 | }
89 |
90 | // On pointer move, calculate coordinates diff
91 | function onPointerMove (e) {
92 | const { x, y } = e.data.global
93 | if (pointerDownTarget) {
94 | diffX = pointerDiffStart.x + (x - pointerStart.x)
95 | diffY = pointerDiffStart.y + (y - pointerStart.y)
96 | }
97 | }
98 |
99 | // Init everything
100 | function init () {
101 | initDimensions()
102 | initUniforms()
103 | initApp()
104 | initBackground()
105 | initEvents()
106 |
107 | // Animation loop
108 | // Code here will be executed on every animation frame
109 | app.ticker.add(() => {
110 | // Multiply the values by a coefficient to get a smooth animation
111 | uniforms.uPointerDown += (pointerDownTarget - uniforms.uPointerDown) * 0.075
112 | uniforms.uPointerDiff.x += (diffX - uniforms.uPointerDiff.x) * 0.2
113 | uniforms.uPointerDiff.y += (diffY - uniforms.uPointerDiff.y) * 0.2
114 | })
115 | }
116 |
117 | // Load resources, then init the app
118 | PIXI.Loader.shared.add([
119 | 'shaders/stageFragment.glsl',
120 | 'shaders/backgroundFragment.glsl'
121 | ]).load(init)
122 |
123 | })()
124 |
--------------------------------------------------------------------------------
/demos/rects-pixi/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Class to generate a random masonry layout, using a square grid as base
4 | class Grid {
5 |
6 | // The constructor receives all the following parameters:
7 | // - gridSize: The size (width and height) for smallest unit size
8 | // - gridColumns: Number of columns for the grid (width = gridColumns * gridSize)
9 | // - gridRows: Number of rows for the grid (height = gridRows * gridSize)
10 | // - gridMin: Min width and height limits for rectangles (in grid units)
11 | constructor(gridSize, gridColumns, gridRows, gridMin) {
12 | this.gridSize = gridSize
13 | this.gridColumns = gridColumns
14 | this.gridRows = gridRows
15 | this.gridMin = gridMin
16 | this.rects = []
17 | this.currentRects = [{ x: 0, y: 0, w: this.gridColumns, h: this.gridRows }]
18 | }
19 |
20 | // Takes the first rectangle on the list, and divides it in 2 more rectangles if possible
21 | splitCurrentRect () {
22 | if (this.currentRects.length) {
23 | const currentRect = this.currentRects.shift()
24 | const cutVertical = currentRect.w > currentRect.h
25 | const cutSide = cutVertical ? currentRect.w : currentRect.h
26 | const cutSize = cutVertical ? 'w' : 'h'
27 | const cutAxis = cutVertical ? 'x' : 'y'
28 | if (cutSide > this.gridMin * 2) {
29 | const rect1Size = randomInRange(this.gridMin, cutSide - this.gridMin)
30 | const rect1 = Object.assign({}, currentRect, { [cutSize]: rect1Size })
31 | const rect2 = Object.assign({}, currentRect, { [cutAxis]: currentRect[cutAxis] + rect1Size, [cutSize]: currentRect[cutSize] - rect1Size })
32 | this.currentRects.push(rect1, rect2)
33 | }
34 | else {
35 | this.rects.push(currentRect)
36 | this.splitCurrentRect()
37 | }
38 | }
39 | }
40 |
41 | // Call `splitCurrentRect` until there is no more rectangles on the list
42 | // Then return the list of rectangles
43 | generateRects () {
44 | while (this.currentRects.length) {
45 | this.splitCurrentRect()
46 | }
47 | return this.rects
48 | }
49 | }
50 |
51 | // Generate a random integer in the range provided
52 | function randomInRange (min, max) {
53 | return Math.floor(Math.random() * (max - min + 1)) + min
54 | }
55 |
56 | // Get canvas view
57 | const view = document.querySelector('.view')
58 | // Loaded resources will be here
59 | const resources = PIXI.Loader.shared.resources
60 | // Target for pointer. If down, value is 1, else value is 0
61 | let pointerDownTarget = 0
62 | // Useful variables to keep track of the pointer
63 | let pointerStart = new PIXI.Point()
64 | let pointerDiffStart = new PIXI.Point()
65 | let width, height, app, background, uniforms, diffX, diffY
66 |
67 | // Variables and settings for grid
68 | const gridSize = 50
69 | const gridMin = 3
70 | const imagePadding = 20
71 | let gridColumnsCount, gridRowsCount, gridColumns, gridRows, grid
72 | let widthRest, heightRest, centerX, centerY, container, rects
73 |
74 | // Set dimensions
75 | function initDimensions () {
76 | width = window.innerWidth
77 | height = window.innerHeight
78 | diffX = 0
79 | diffY = 0
80 | }
81 |
82 | // Set initial values for uniforms
83 | function initUniforms () {
84 | uniforms = {
85 | uResolution: new PIXI.Point(width, height),
86 | uPointerDiff: new PIXI.Point(),
87 | uPointerDown: pointerDownTarget
88 | }
89 | }
90 |
91 | // Initialize the random grid layout
92 | function initGrid () {
93 | // Getting columns
94 | gridColumnsCount = Math.ceil(width / gridSize)
95 | // Getting rows
96 | gridRowsCount = Math.ceil(height / gridSize)
97 | // Make the grid 5 times bigger than viewport
98 | gridColumns = gridColumnsCount * 5
99 | gridRows = gridRowsCount * 5
100 | // Create a new Grid instance with our settings
101 | grid = new Grid(gridSize, gridColumns, gridRows, gridMin)
102 | // Calculate the center position for the grid in the viewport
103 | widthRest = Math.ceil(gridColumnsCount * gridSize - width)
104 | heightRest = Math.ceil(gridRowsCount * gridSize - height)
105 | centerX = (gridColumns * gridSize / 2) - (gridColumnsCount * gridSize / 2)
106 | centerY = (gridRows * gridSize / 2) - (gridRowsCount * gridSize / 2)
107 | // Generate the list of rects
108 | rects = grid.generateRects()
109 | }
110 |
111 | // Init the PixiJS Application
112 | function initApp () {
113 | // Create a PixiJS Application, using the view (canvas) provided
114 | app = new PIXI.Application({ view })
115 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
116 | app.renderer.autoDensity = true
117 | // Resize the view to match viewport dimensions
118 | app.renderer.resize(width, height)
119 |
120 | // Set the distortion filter for the entire stage
121 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
122 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
123 | app.stage.filters = [stageFilter]
124 | }
125 |
126 | // Init the gridded background
127 | function initBackground () {
128 | // Create a new empty Sprite and define its size
129 | background = new PIXI.Sprite()
130 | background.width = width
131 | background.height = height
132 | // Get the code for the fragment shader from the loaded resources
133 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
134 | // Create a new Filter using the fragment shader
135 | // We don't need a custom vertex shader, so we set it as `undefined`
136 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader, uniforms)
137 | // Assign the filter to the background Sprite
138 | background.filters = [backgroundFilter]
139 | // Add the background to the stage
140 | app.stage.addChild(background)
141 | }
142 |
143 | // Initialize a Container element for solid rectangles and images
144 | function initContainer () {
145 | container = new PIXI.Container()
146 | app.stage.addChild(container)
147 | }
148 |
149 | // Add solid rectangles and images
150 | // So far, we will only add rectangles
151 | function initRectsAndImages () {
152 | // Create a new Graphics element to draw solid rectangles
153 | const graphics = new PIXI.Graphics()
154 | // Select the color for rectangles
155 | graphics.beginFill(0xAA22CC)
156 | // Loop over each rect in the list
157 | rects.forEach(rect => {
158 | // Draw the rectangle
159 | graphics.drawRect(
160 | rect.x * gridSize,
161 | rect.y * gridSize,
162 | rect.w * gridSize - imagePadding,
163 | rect.h * gridSize - imagePadding
164 | )
165 | })
166 | // Ends the fill action
167 | graphics.endFill()
168 | // Add the graphics (with all drawn rects) to the container
169 | container.addChild(graphics)
170 | }
171 |
172 | // Start listening events
173 | function initEvents () {
174 | // Make stage interactive, so it can listen to events
175 | app.stage.interactive = true
176 |
177 | // Pointer & touch events are normalized into
178 | // the `pointer*` events for handling different events
179 | app.stage
180 | .on('pointerdown', onPointerDown)
181 | .on('pointerup', onPointerUp)
182 | .on('pointerupoutside', onPointerUp)
183 | .on('pointermove', onPointerMove)
184 | }
185 |
186 | // On pointer down, save coordinates and set pointerDownTarget
187 | function onPointerDown (e) {
188 | const { x, y } = e.data.global
189 | pointerDownTarget = 1
190 | pointerStart.set(x, y)
191 | pointerDiffStart = uniforms.uPointerDiff.clone()
192 | }
193 |
194 | // On pointer up, set pointerDownTarget
195 | function onPointerUp () {
196 | pointerDownTarget = 0
197 | }
198 |
199 | // On pointer move, calculate coordinates diff
200 | function onPointerMove (e) {
201 | const { x, y } = e.data.global
202 | if (pointerDownTarget) {
203 | diffX = pointerDiffStart.x + (x - pointerStart.x)
204 | diffY = pointerDiffStart.y + (y - pointerStart.y)
205 | }
206 | }
207 |
208 | // Init everything
209 | function init () {
210 | initDimensions()
211 | initUniforms()
212 | initGrid()
213 | initApp()
214 | initBackground()
215 | initContainer()
216 | initRectsAndImages()
217 | initEvents()
218 |
219 | // Animation loop
220 | // Code here will be executed on every animation frame
221 | app.ticker.add(() => {
222 | // Multiply the values by a coefficient to get a smooth animation
223 | uniforms.uPointerDown += (pointerDownTarget - uniforms.uPointerDown) * 0.075
224 | uniforms.uPointerDiff.x += (diffX - uniforms.uPointerDiff.x) * 0.2
225 | uniforms.uPointerDiff.y += (diffY - uniforms.uPointerDiff.y) * 0.2
226 | // Set position for the container
227 | container.x = uniforms.uPointerDiff.x - centerX
228 | container.y = uniforms.uPointerDiff.y - centerY
229 | })
230 | }
231 |
232 | // Load resources, then init the app
233 | PIXI.Loader.shared.add([
234 | 'shaders/stageFragment.glsl',
235 | 'shaders/backgroundFragment.glsl'
236 | ]).load(init)
237 |
238 | })()
239 |
--------------------------------------------------------------------------------
/demos/rects-final/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Class to generate a random masonry layout, using a square grid as base
4 | class Grid {
5 |
6 | // The constructor receives all the following parameters:
7 | // - gridSize: The size (width and height) for smallest unit size
8 | // - gridColumns: Number of columns for the grid (width = gridColumns * gridSize)
9 | // - gridRows: Number of rows for the grid (height = gridRows * gridSize)
10 | // - gridMin: Min width and height limits for rectangles (in grid units)
11 | constructor(gridSize, gridColumns, gridRows, gridMin) {
12 | this.gridSize = gridSize
13 | this.gridColumns = gridColumns
14 | this.gridRows = gridRows
15 | this.gridMin = gridMin
16 | this.rects = []
17 | this.currentRects = [{ x: 0, y: 0, w: this.gridColumns, h: this.gridRows }]
18 | }
19 |
20 | // Takes the first rectangle on the list, and divides it in 2 more rectangles if possible
21 | splitCurrentRect () {
22 | if (this.currentRects.length) {
23 | const currentRect = this.currentRects.shift()
24 | const cutVertical = currentRect.w > currentRect.h
25 | const cutSide = cutVertical ? currentRect.w : currentRect.h
26 | const cutSize = cutVertical ? 'w' : 'h'
27 | const cutAxis = cutVertical ? 'x' : 'y'
28 | if (cutSide > this.gridMin * 2) {
29 | const rect1Size = randomInRange(this.gridMin, cutSide - this.gridMin)
30 | const rect1 = Object.assign({}, currentRect, { [cutSize]: rect1Size })
31 | const rect2 = Object.assign({}, currentRect, { [cutAxis]: currentRect[cutAxis] + rect1Size, [cutSize]: currentRect[cutSize] - rect1Size })
32 | this.currentRects.push(rect1, rect2)
33 | }
34 | else {
35 | this.rects.push(currentRect)
36 | this.splitCurrentRect()
37 | }
38 | }
39 | }
40 |
41 | // Call `splitCurrentRect` until there is no more rectangles on the list
42 | // Then return the list of rectangles
43 | generateRects () {
44 | while (this.currentRects.length) {
45 | this.splitCurrentRect()
46 | }
47 | return this.rects
48 | }
49 | }
50 |
51 | // Generate a random integer in the range provided
52 | function randomInRange (min, max) {
53 | return Math.floor(Math.random() * (max - min + 1)) + min
54 | }
55 |
56 | // Get canvas view
57 | const view = document.querySelector('.view')
58 | // Loaded resources will be here
59 | const resources = PIXI.Loader.shared.resources
60 | // Target for pointer. If down, value is 1, else value is 0
61 | let pointerDownTarget = 0
62 | // Useful variables to keep track of the pointer
63 | let pointerStart = new PIXI.Point()
64 | let pointerDiffStart = new PIXI.Point()
65 | let width, height, app, background, uniforms, diffX, diffY
66 |
67 | // Variables and settings for grid
68 | const gridSize = 50
69 | const gridMin = 3
70 | const imagePadding = 20
71 | let gridColumnsCount, gridRowsCount, gridColumns, gridRows, grid
72 | let widthRest, heightRest, centerX, centerY, container, rects
73 |
74 | // Set dimensions
75 | function initDimensions () {
76 | width = window.innerWidth
77 | height = window.innerHeight
78 | diffX = 0
79 | diffY = 0
80 | }
81 |
82 | // Set initial values for uniforms
83 | function initUniforms () {
84 | uniforms = {
85 | uResolution: new PIXI.Point(width, height),
86 | uPointerDiff: new PIXI.Point(),
87 | uPointerDown: pointerDownTarget
88 | }
89 | }
90 |
91 | // Initialize the random grid layout
92 | function initGrid () {
93 | // Getting columns
94 | gridColumnsCount = Math.ceil(width / gridSize)
95 | // Getting rows
96 | gridRowsCount = Math.ceil(height / gridSize)
97 | // Make the grid 5 times bigger than viewport
98 | gridColumns = gridColumnsCount * 5
99 | gridRows = gridRowsCount * 5
100 | // Create a new Grid instance with our settings
101 | grid = new Grid(gridSize, gridColumns, gridRows, gridMin)
102 | // Calculate the center position for the grid in the viewport
103 | widthRest = Math.ceil(gridColumnsCount * gridSize - width)
104 | heightRest = Math.ceil(gridRowsCount * gridSize - height)
105 | centerX = (gridColumns * gridSize / 2) - (gridColumnsCount * gridSize / 2)
106 | centerY = (gridRows * gridSize / 2) - (gridRowsCount * gridSize / 2)
107 | // Generate the list of rects
108 | rects = grid.generateRects()
109 | }
110 |
111 | // Init the PixiJS Application
112 | function initApp () {
113 | // Create a PixiJS Application, using the view (canvas) provided
114 | app = new PIXI.Application({ view })
115 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
116 | app.renderer.autoDensity = true
117 | // Resize the view to match viewport dimensions
118 | app.renderer.resize(width, height)
119 |
120 | // Set the distortion filter for the entire stage
121 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
122 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
123 | app.stage.filters = [stageFilter]
124 | }
125 |
126 | // Init the gridded background
127 | function initBackground () {
128 | // Create a new empty Sprite and define its size
129 | background = new PIXI.Sprite()
130 | background.width = width
131 | background.height = height
132 | // Get the code for the fragment shader from the loaded resources
133 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
134 | // Create a new Filter using the fragment shader
135 | // We don't need a custom vertex shader, so we set it as `undefined`
136 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader, uniforms)
137 | // Assign the filter to the background Sprite
138 | background.filters = [backgroundFilter]
139 | // Add the background to the stage
140 | app.stage.addChild(background)
141 | }
142 |
143 | // Initialize a Container element for solid rectangles and images
144 | function initContainer () {
145 | container = new PIXI.Container()
146 | app.stage.addChild(container)
147 | }
148 |
149 | // Add solid rectangles and images
150 | // So far, we will only add rectangles
151 | function initRectsAndImages () {
152 | // Create a new Graphics element to draw solid rectangles
153 | const graphics = new PIXI.Graphics()
154 | // Select the color for rectangles
155 | graphics.beginFill(0xAA22CC)
156 | // Loop over each rect in the list
157 | rects.forEach(rect => {
158 | // Draw the rectangle
159 | graphics.drawRect(
160 | rect.x * gridSize,
161 | rect.y * gridSize,
162 | rect.w * gridSize - imagePadding,
163 | rect.h * gridSize - imagePadding
164 | )
165 | })
166 | // Ends the fill action
167 | graphics.endFill()
168 | // Add the graphics (with all drawn rects) to the container
169 | container.addChild(graphics)
170 | }
171 |
172 | // Start listening events
173 | function initEvents () {
174 | // Make stage interactive, so it can listen to events
175 | app.stage.interactive = true
176 |
177 | // Pointer & touch events are normalized into
178 | // the `pointer*` events for handling different events
179 | app.stage
180 | .on('pointerdown', onPointerDown)
181 | .on('pointerup', onPointerUp)
182 | .on('pointerupoutside', onPointerUp)
183 | .on('pointermove', onPointerMove)
184 | }
185 |
186 | // On pointer down, save coordinates and set pointerDownTarget
187 | function onPointerDown (e) {
188 | const { x, y } = e.data.global
189 | pointerDownTarget = 1
190 | pointerStart.set(x, y)
191 | pointerDiffStart = uniforms.uPointerDiff.clone()
192 | }
193 |
194 | // On pointer up, set pointerDownTarget
195 | function onPointerUp () {
196 | pointerDownTarget = 0
197 | }
198 |
199 | // On pointer move, calculate coordinates diff
200 | function onPointerMove (e) {
201 | const { x, y } = e.data.global
202 | if (pointerDownTarget) {
203 | diffX = pointerDiffStart.x + (x - pointerStart.x)
204 | diffY = pointerDiffStart.y + (y - pointerStart.y)
205 | diffX = diffX > 0 ? Math.min(diffX, centerX + imagePadding) : Math.max(diffX, -(centerX + widthRest))
206 | diffY = diffY > 0 ? Math.min(diffY, centerY + imagePadding) : Math.max(diffY, -(centerY + heightRest))
207 | }
208 | }
209 |
210 | // Init everything
211 | function init () {
212 | initDimensions()
213 | initUniforms()
214 | initGrid()
215 | initApp()
216 | initBackground()
217 | initContainer()
218 | initRectsAndImages()
219 | initEvents()
220 |
221 | // Animation loop
222 | // Code here will be executed on every animation frame
223 | app.ticker.add(() => {
224 | // Multiply the values by a coefficient to get a smooth animation
225 | uniforms.uPointerDown += (pointerDownTarget - uniforms.uPointerDown) * 0.075
226 | uniforms.uPointerDiff.x += (diffX - uniforms.uPointerDiff.x) * 0.2
227 | uniforms.uPointerDiff.y += (diffY - uniforms.uPointerDiff.y) * 0.2
228 | // Set position for the container
229 | container.x = uniforms.uPointerDiff.x - centerX
230 | container.y = uniforms.uPointerDiff.y - centerY
231 | })
232 | }
233 |
234 | // Load resources, then init the app
235 | PIXI.Loader.shared.add([
236 | 'shaders/stageFragment.glsl',
237 | 'shaders/backgroundFragment.glsl'
238 | ]).load(init)
239 |
240 | })()
241 |
--------------------------------------------------------------------------------
/demos/images/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Class to generate a random masonry layout, using a square grid as base
4 | class Grid {
5 |
6 | // The constructor receives all the following parameters:
7 | // - gridSize: The size (width and height) for smallest unit size
8 | // - gridColumns: Number of columns for the grid (width = gridColumns * gridSize)
9 | // - gridRows: Number of rows for the grid (height = gridRows * gridSize)
10 | // - gridMin: Min width and height limits for rectangles (in grid units)
11 | constructor(gridSize, gridColumns, gridRows, gridMin) {
12 | this.gridSize = gridSize
13 | this.gridColumns = gridColumns
14 | this.gridRows = gridRows
15 | this.gridMin = gridMin
16 | this.rects = []
17 | this.currentRects = [{ x: 0, y: 0, w: this.gridColumns, h: this.gridRows }]
18 | }
19 |
20 | // Takes the first rectangle on the list, and divides it in 2 more rectangles if possible
21 | splitCurrentRect () {
22 | if (this.currentRects.length) {
23 | const currentRect = this.currentRects.shift()
24 | const cutVertical = currentRect.w > currentRect.h
25 | const cutSide = cutVertical ? currentRect.w : currentRect.h
26 | const cutSize = cutVertical ? 'w' : 'h'
27 | const cutAxis = cutVertical ? 'x' : 'y'
28 | if (cutSide > this.gridMin * 2) {
29 | const rect1Size = randomInRange(this.gridMin, cutSide - this.gridMin)
30 | const rect1 = Object.assign({}, currentRect, { [cutSize]: rect1Size })
31 | const rect2 = Object.assign({}, currentRect, { [cutAxis]: currentRect[cutAxis] + rect1Size, [cutSize]: currentRect[cutSize] - rect1Size })
32 | this.currentRects.push(rect1, rect2)
33 | }
34 | else {
35 | this.rects.push(currentRect)
36 | this.splitCurrentRect()
37 | }
38 | }
39 | }
40 |
41 | // Call `splitCurrentRect` until there is no more rectangles on the list
42 | // Then return the list of rectangles
43 | generateRects () {
44 | while (this.currentRects.length) {
45 | this.splitCurrentRect()
46 | }
47 | return this.rects
48 | }
49 | }
50 |
51 | // Generate a random integer in the range provided
52 | function randomInRange (min, max) {
53 | return Math.floor(Math.random() * (max - min + 1)) + min
54 | }
55 |
56 | // Get canvas view
57 | const view = document.querySelector('.view')
58 | // Loaded resources will be here
59 | const resources = PIXI.Loader.shared.resources
60 | // Target for pointer. If down, value is 1, else value is 0
61 | let pointerDownTarget = 0
62 | // Useful variables to keep track of the pointer
63 | let pointerStart = new PIXI.Point()
64 | let pointerDiffStart = new PIXI.Point()
65 | let width, height, app, background, uniforms, diffX, diffY
66 |
67 | // Variables and settings for grid
68 | const gridSize = 50
69 | const gridMin = 3
70 | const imagePadding = 20
71 | let gridColumnsCount, gridRowsCount, gridColumns, gridRows, grid
72 | let widthRest, heightRest, centerX, centerY, container, rects
73 | let images, imagesUrls
74 |
75 | // Set dimensions
76 | function initDimensions () {
77 | width = window.innerWidth
78 | height = window.innerHeight
79 | diffX = 0
80 | diffY = 0
81 | }
82 |
83 | // Set initial values for uniforms
84 | function initUniforms () {
85 | uniforms = {
86 | uResolution: new PIXI.Point(width, height),
87 | uPointerDiff: new PIXI.Point(),
88 | uPointerDown: pointerDownTarget
89 | }
90 | }
91 |
92 | // Initialize the random grid layout
93 | function initGrid () {
94 | // Getting columns
95 | gridColumnsCount = Math.ceil(width / gridSize)
96 | // Getting rows
97 | gridRowsCount = Math.ceil(height / gridSize)
98 | // Make the grid 5 times bigger than viewport
99 | gridColumns = gridColumnsCount * 5
100 | gridRows = gridRowsCount * 5
101 | // Create a new Grid instance with our settings
102 | grid = new Grid(gridSize, gridColumns, gridRows, gridMin)
103 | // Calculate the center position for the grid in the viewport
104 | widthRest = Math.ceil(gridColumnsCount * gridSize - width)
105 | heightRest = Math.ceil(gridRowsCount * gridSize - height)
106 | centerX = (gridColumns * gridSize / 2) - (gridColumnsCount * gridSize / 2)
107 | centerY = (gridRows * gridSize / 2) - (gridRowsCount * gridSize / 2)
108 | // Generate the list of rects
109 | rects = grid.generateRects()
110 | // For the list of images
111 | images = []
112 | // For storing the image URL and avoid duplicates
113 | imagesUrls = {}
114 | }
115 |
116 | // Init the PixiJS Application
117 | function initApp () {
118 | // Create a PixiJS Application, using the view (canvas) provided
119 | app = new PIXI.Application({ view })
120 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
121 | app.renderer.autoDensity = true
122 | // Resize the view to match viewport dimensions
123 | app.renderer.resize(width, height)
124 |
125 | // Set the distortion filter for the entire stage
126 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
127 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
128 | app.stage.filters = [stageFilter]
129 | }
130 |
131 | // Init the gridded background
132 | function initBackground () {
133 | // Create a new empty Sprite and define its size
134 | background = new PIXI.Sprite()
135 | background.width = width
136 | background.height = height
137 | // Get the code for the fragment shader from the loaded resources
138 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
139 | // Create a new Filter using the fragment shader
140 | // We don't need a custom vertex shader, so we set it as `undefined`
141 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader, uniforms)
142 | // Assign the filter to the background Sprite
143 | background.filters = [backgroundFilter]
144 | // Add the background to the stage
145 | app.stage.addChild(background)
146 | }
147 |
148 | // Initialize a Container element for solid rectangles and images
149 | function initContainer () {
150 | container = new PIXI.Container()
151 | app.stage.addChild(container)
152 | }
153 |
154 | // Load texture for an image, giving its index
155 | function loadTextureForImage (index) {
156 | // Get image Sprite
157 | const image = images[index]
158 | // Set the url to get a random image from Unsplash Source, given image dimensions
159 | const url = `https://source.unsplash.com/random/${image.width}x${image.height}`
160 | // Get the corresponding rect, to store more data needed (it is a normal Object)
161 | const rect = rects[index]
162 | // Create a new AbortController, to abort fetch if needed
163 | const { signal } = rect.controller = new AbortController()
164 | // Fetch the image
165 | fetch(url, { signal }).then(response => {
166 | // Get image URL, and if it was downloaded before, load another image
167 | // Otherwise, save image URL and set the texture
168 | const id = response.url.split('?')[0]
169 | if (imagesUrls[id]) {
170 | loadTextureForImage(index)
171 | } else {
172 | imagesUrls[id] = true
173 | image.texture = PIXI.Texture.from(response.url)
174 | rect.loaded = true
175 | }
176 | }).catch(() => {
177 | // Catch errors silently, for not showing the following error message if it is aborted:
178 | // AbortError: The operation was aborted.
179 | })
180 | }
181 |
182 | // Add solid rectangles and images
183 | function initRectsAndImages () {
184 | // Create a new Graphics element to draw solid rectangles
185 | const graphics = new PIXI.Graphics()
186 | // Select the color for rectangles
187 | graphics.beginFill(0x000000)
188 | // Loop over each rect in the list
189 | rects.forEach(rect => {
190 | // Create a new Sprite element for each image
191 | const image = new PIXI.Sprite()
192 | // Set image's position and size
193 | image.x = rect.x * gridSize
194 | image.y = rect.y * gridSize
195 | image.width = rect.w * gridSize - imagePadding
196 | image.height = rect.h * gridSize - imagePadding
197 | // Set it's alpha to 0, so it is not visible initially
198 | image.alpha = 0
199 | // Add image to the list
200 | images.push(image)
201 | // Draw the rectangle
202 | graphics.drawRect(image.x, image.y, image.width, image.height)
203 | })
204 | // Ends the fill action
205 | graphics.endFill()
206 | // Add the graphics (with all drawn rects) to the container
207 | container.addChild(graphics)
208 | // Add all image's Sprites to the container
209 | images.forEach(image => {
210 | container.addChild(image)
211 | })
212 | }
213 |
214 | // Check if rects intersects with the viewport
215 | // and loads corresponding image
216 | function checkRectsAndImages () {
217 | // Loop over rects
218 | rects.forEach((rect, index) => {
219 | // Get corresponding image
220 | const image = images[index]
221 | // Check if the rect intersects with the viewport
222 | if (rectIntersectsWithViewport(rect)) {
223 | // If rect just has been discovered
224 | // start loading image
225 | if (!rect.discovered) {
226 | rect.discovered = true
227 | loadTextureForImage(index)
228 | }
229 | // If image is loaded, increase alpha if possible
230 | if (rect.loaded && image.alpha < 1) {
231 | image.alpha += 0.01
232 | }
233 | } else { // The rect is not intersecting
234 | // If the rect was discovered before, but the
235 | // image is not loaded yet, abort the fetch
236 | if (rect.discovered && !rect.loaded) {
237 | rect.discovered = false
238 | rect.controller.abort()
239 | }
240 | // Decrease alpha if possible
241 | if (image.alpha > 0) {
242 | image.alpha -= 0.01
243 | }
244 | }
245 | })
246 | }
247 |
248 | // Check if a rect intersects the viewport
249 | function rectIntersectsWithViewport (rect) {
250 | return (
251 | rect.x * gridSize + container.x <= width &&
252 | 0 <= (rect.x + rect.w) * gridSize + container.x &&
253 | rect.y * gridSize + container.y <= height &&
254 | 0 <= (rect.y + rect.h) * gridSize + container.y
255 | )
256 | }
257 |
258 | // Start listening events
259 | function initEvents () {
260 | // Make stage interactive, so it can listen to events
261 | app.stage.interactive = true
262 |
263 | // Pointer & touch events are normalized into
264 | // the `pointer*` events for handling different events
265 | app.stage
266 | .on('pointerdown', onPointerDown)
267 | .on('pointerup', onPointerUp)
268 | .on('pointerupoutside', onPointerUp)
269 | .on('pointermove', onPointerMove)
270 | }
271 |
272 | // On pointer down, save coordinates and set pointerDownTarget
273 | function onPointerDown (e) {
274 | const { x, y } = e.data.global
275 | pointerDownTarget = 1
276 | pointerStart.set(x, y)
277 | pointerDiffStart = uniforms.uPointerDiff.clone()
278 | }
279 |
280 | // On pointer up, set pointerDownTarget
281 | function onPointerUp () {
282 | pointerDownTarget = 0
283 | }
284 |
285 | // On pointer move, calculate coordinates diff
286 | function onPointerMove (e) {
287 | const { x, y } = e.data.global
288 | if (pointerDownTarget) {
289 | diffX = pointerDiffStart.x + (x - pointerStart.x)
290 | diffY = pointerDiffStart.y + (y - pointerStart.y)
291 | diffX = diffX > 0 ? Math.min(diffX, centerX + imagePadding) : Math.max(diffX, -(centerX + widthRest))
292 | diffY = diffY > 0 ? Math.min(diffY, centerY + imagePadding) : Math.max(diffY, -(centerY + heightRest))
293 | }
294 | }
295 |
296 | // Init everything
297 | function init () {
298 | initDimensions()
299 | initUniforms()
300 | initGrid()
301 | initApp()
302 | initBackground()
303 | initContainer()
304 | initRectsAndImages()
305 | initEvents()
306 |
307 | // Animation loop
308 | // Code here will be executed on every animation frame
309 | app.ticker.add(() => {
310 | // Multiply the values by a coefficient to get a smooth animation
311 | uniforms.uPointerDown += (pointerDownTarget - uniforms.uPointerDown) * 0.075
312 | uniforms.uPointerDiff.x += (diffX - uniforms.uPointerDiff.x) * 0.2
313 | uniforms.uPointerDiff.y += (diffY - uniforms.uPointerDiff.y) * 0.2
314 | // Set position for the container
315 | container.x = uniforms.uPointerDiff.x - centerX
316 | container.y = uniforms.uPointerDiff.y - centerY
317 | // Check rects and load/cancel images as needded
318 | checkRectsAndImages()
319 | })
320 | }
321 |
322 | // Load resources, then init the app
323 | PIXI.Loader.shared.add([
324 | 'shaders/stageFragment.glsl',
325 | 'shaders/backgroundFragment.glsl'
326 | ]).load(init)
327 |
328 | })()
329 |
--------------------------------------------------------------------------------
/demos/final/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Class to generate a random masonry layout, using a square grid as base
4 | class Grid {
5 |
6 | // The constructor receives all the following parameters:
7 | // - gridSize: The size (width and height) for smallest unit size
8 | // - gridColumns: Number of columns for the grid (width = gridColumns * gridSize)
9 | // - gridRows: Number of rows for the grid (height = gridRows * gridSize)
10 | // - gridMin: Min width and height limits for rectangles (in grid units)
11 | constructor(gridSize, gridColumns, gridRows, gridMin) {
12 | this.gridSize = gridSize
13 | this.gridColumns = gridColumns
14 | this.gridRows = gridRows
15 | this.gridMin = gridMin
16 | this.rects = []
17 | this.currentRects = [{ x: 0, y: 0, w: this.gridColumns, h: this.gridRows }]
18 | }
19 |
20 | // Takes the first rectangle on the list, and divides it in 2 more rectangles if possible
21 | splitCurrentRect () {
22 | if (this.currentRects.length) {
23 | const currentRect = this.currentRects.shift()
24 | const cutVertical = currentRect.w > currentRect.h
25 | const cutSide = cutVertical ? currentRect.w : currentRect.h
26 | const cutSize = cutVertical ? 'w' : 'h'
27 | const cutAxis = cutVertical ? 'x' : 'y'
28 | if (cutSide > this.gridMin * 2) {
29 | const rect1Size = randomInRange(this.gridMin, cutSide - this.gridMin)
30 | const rect1 = Object.assign({}, currentRect, { [cutSize]: rect1Size })
31 | const rect2 = Object.assign({}, currentRect, { [cutAxis]: currentRect[cutAxis] + rect1Size, [cutSize]: currentRect[cutSize] - rect1Size })
32 | this.currentRects.push(rect1, rect2)
33 | }
34 | else {
35 | this.rects.push(currentRect)
36 | this.splitCurrentRect()
37 | }
38 | }
39 | }
40 |
41 | // Call `splitCurrentRect` until there is no more rectangles on the list
42 | // Then return the list of rectangles
43 | generateRects () {
44 | while (this.currentRects.length) {
45 | this.splitCurrentRect()
46 | }
47 | return this.rects
48 | }
49 | }
50 |
51 | // Generate a random integer in the range provided
52 | function randomInRange (min, max) {
53 | return Math.floor(Math.random() * (max - min + 1)) + min
54 | }
55 |
56 | // Get canvas view
57 | const view = document.querySelector('.view')
58 | // Loaded resources will be here
59 | const resources = PIXI.Loader.shared.resources
60 | // Target for pointer. If down, value is 1, else value is 0
61 | let pointerDownTarget = 0
62 | // Useful variables to keep track of the pointer
63 | let pointerStart = new PIXI.Point()
64 | let pointerDiffStart = new PIXI.Point()
65 | let width, height, app, background, uniforms, diffX, diffY
66 |
67 | // Variables and settings for grid
68 | const gridSize = 50
69 | const gridMin = 3
70 | const imagePadding = 20
71 | let gridColumnsCount, gridRowsCount, gridColumns, gridRows, grid
72 | let widthRest, heightRest, centerX, centerY, container, rects
73 | let images, imagesUrls
74 |
75 | // Set dimensions
76 | function initDimensions () {
77 | width = window.innerWidth
78 | height = window.innerHeight
79 | diffX = 0
80 | diffY = 0
81 | }
82 |
83 | // Set initial values for uniforms
84 | function initUniforms () {
85 | uniforms = {
86 | uResolution: new PIXI.Point(width, height),
87 | uPointerDiff: new PIXI.Point(),
88 | uPointerDown: pointerDownTarget
89 | }
90 | }
91 |
92 | // Initialize the random grid layout
93 | function initGrid () {
94 | // Getting columns
95 | gridColumnsCount = Math.ceil(width / gridSize)
96 | // Getting rows
97 | gridRowsCount = Math.ceil(height / gridSize)
98 | // Make the grid 5 times bigger than viewport
99 | gridColumns = gridColumnsCount * 5
100 | gridRows = gridRowsCount * 5
101 | // Create a new Grid instance with our settings
102 | grid = new Grid(gridSize, gridColumns, gridRows, gridMin)
103 | // Calculate the center position for the grid in the viewport
104 | widthRest = Math.ceil(gridColumnsCount * gridSize - width)
105 | heightRest = Math.ceil(gridRowsCount * gridSize - height)
106 | centerX = (gridColumns * gridSize / 2) - (gridColumnsCount * gridSize / 2)
107 | centerY = (gridRows * gridSize / 2) - (gridRowsCount * gridSize / 2)
108 | // Generate the list of rects
109 | rects = grid.generateRects()
110 | // For the list of images
111 | images = []
112 | // For storing the image URL and avoid duplicates
113 | imagesUrls = {}
114 | }
115 |
116 | // Init the PixiJS Application
117 | function initApp () {
118 | // Create a PixiJS Application, using the view (canvas) provided
119 | app = new PIXI.Application({ view })
120 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
121 | app.renderer.autoDensity = true
122 | // Resize the view to match viewport dimensions
123 | app.renderer.resize(width, height)
124 |
125 | // Set the distortion filter for the entire stage
126 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
127 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
128 | app.stage.filters = [stageFilter]
129 | }
130 |
131 | // Init the gridded background
132 | function initBackground () {
133 | // Create a new empty Sprite and define its size
134 | background = new PIXI.Sprite()
135 | background.width = width
136 | background.height = height
137 | // Get the code for the fragment shader from the loaded resources
138 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
139 | // Create a new Filter using the fragment shader
140 | // We don't need a custom vertex shader, so we set it as `undefined`
141 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader, uniforms)
142 | // Assign the filter to the background Sprite
143 | background.filters = [backgroundFilter]
144 | // Add the background to the stage
145 | app.stage.addChild(background)
146 | }
147 |
148 | // Initialize a Container element for solid rectangles and images
149 | function initContainer () {
150 | container = new PIXI.Container()
151 | app.stage.addChild(container)
152 | }
153 |
154 | // Load texture for an image, giving its index
155 | function loadTextureForImage (index) {
156 | // Get image Sprite
157 | const image = images[index]
158 | // Set the url to get a random image from Unsplash Source, given image dimensions
159 | const url = `https://source.unsplash.com/random/${image.width}x${image.height}`
160 | // Get the corresponding rect, to store more data needed (it is a normal Object)
161 | const rect = rects[index]
162 | // Create a new AbortController, to abort fetch if needed
163 | const { signal } = rect.controller = new AbortController()
164 | // Fetch the image
165 | fetch(url, { signal }).then(response => {
166 | // Get image URL, and if it was downloaded before, load another image
167 | // Otherwise, save image URL and set the texture
168 | const id = response.url.split('?')[0]
169 | if (imagesUrls[id]) {
170 | loadTextureForImage(index)
171 | } else {
172 | imagesUrls[id] = true
173 | image.texture = PIXI.Texture.from(response.url)
174 | rect.loaded = true
175 | }
176 | }).catch(() => {
177 | // Catch errors silently, for not showing the following error message if it is aborted:
178 | // AbortError: The operation was aborted.
179 | })
180 | }
181 |
182 | // Add solid rectangles and images
183 | function initRectsAndImages () {
184 | // Create a new Graphics element to draw solid rectangles
185 | const graphics = new PIXI.Graphics()
186 | // Select the color for rectangles
187 | graphics.beginFill(0x000000)
188 | // Loop over each rect in the list
189 | rects.forEach(rect => {
190 | // Create a new Sprite element for each image
191 | const image = new PIXI.Sprite()
192 | // Set image's position and size
193 | image.x = rect.x * gridSize
194 | image.y = rect.y * gridSize
195 | image.width = rect.w * gridSize - imagePadding
196 | image.height = rect.h * gridSize - imagePadding
197 | // Set it's alpha to 0, so it is not visible initially
198 | image.alpha = 0
199 | // Add image to the list
200 | images.push(image)
201 | // Draw the rectangle
202 | graphics.drawRect(image.x, image.y, image.width, image.height)
203 | })
204 | // Ends the fill action
205 | graphics.endFill()
206 | // Add the graphics (with all drawn rects) to the container
207 | container.addChild(graphics)
208 | // Add all image's Sprites to the container
209 | images.forEach(image => {
210 | container.addChild(image)
211 | })
212 | }
213 |
214 | // Check if rects intersects with the viewport
215 | // and loads corresponding image
216 | function checkRectsAndImages () {
217 | // Loop over rects
218 | rects.forEach((rect, index) => {
219 | // Get corresponding image
220 | const image = images[index]
221 | // Check if the rect intersects with the viewport
222 | if (rectIntersectsWithViewport(rect)) {
223 | // If rect just has been discovered
224 | // start loading image
225 | if (!rect.discovered) {
226 | rect.discovered = true
227 | loadTextureForImage(index)
228 | }
229 | // If image is loaded, increase alpha if possible
230 | if (rect.loaded && image.alpha < 1) {
231 | image.alpha += 0.01
232 | }
233 | } else { // The rect is not intersecting
234 | // If the rect was discovered before, but the
235 | // image is not loaded yet, abort the fetch
236 | if (rect.discovered && !rect.loaded) {
237 | rect.discovered = false
238 | rect.controller.abort()
239 | }
240 | // Decrease alpha if possible
241 | if (image.alpha > 0) {
242 | image.alpha -= 0.01
243 | }
244 | }
245 | })
246 | }
247 |
248 | // Check if a rect intersects the viewport
249 | function rectIntersectsWithViewport (rect) {
250 | return (
251 | rect.x * gridSize + container.x <= width &&
252 | 0 <= (rect.x + rect.w) * gridSize + container.x &&
253 | rect.y * gridSize + container.y <= height &&
254 | 0 <= (rect.y + rect.h) * gridSize + container.y
255 | )
256 | }
257 |
258 | // Start listening events
259 | function initEvents () {
260 | // Make stage interactive, so it can listen to events
261 | app.stage.interactive = true
262 |
263 | // Pointer & touch events are normalized into
264 | // the `pointer*` events for handling different events
265 | app.stage
266 | .on('pointerdown', onPointerDown)
267 | .on('pointerup', onPointerUp)
268 | .on('pointerupoutside', onPointerUp)
269 | .on('pointermove', onPointerMove)
270 | }
271 |
272 | // On pointer down, save coordinates and set pointerDownTarget
273 | function onPointerDown (e) {
274 | const { x, y } = e.data.global
275 | pointerDownTarget = 1
276 | pointerStart.set(x, y)
277 | pointerDiffStart = uniforms.uPointerDiff.clone()
278 | }
279 |
280 | // On pointer up, set pointerDownTarget
281 | function onPointerUp () {
282 | pointerDownTarget = 0
283 | }
284 |
285 | // On pointer move, calculate coordinates diff
286 | function onPointerMove (e) {
287 | const { x, y } = e.data.global
288 | if (pointerDownTarget) {
289 | diffX = pointerDiffStart.x + (x - pointerStart.x)
290 | diffY = pointerDiffStart.y + (y - pointerStart.y)
291 | diffX = diffX > 0 ? Math.min(diffX, centerX + imagePadding) : Math.max(diffX, -(centerX + widthRest))
292 | diffY = diffY > 0 ? Math.min(diffY, centerY + imagePadding) : Math.max(diffY, -(centerY + heightRest))
293 | }
294 | }
295 |
296 | // Init everything
297 | function init () {
298 | initDimensions()
299 | initUniforms()
300 | initGrid()
301 | initApp()
302 | initBackground()
303 | initContainer()
304 | initRectsAndImages()
305 | initEvents()
306 |
307 | // Animation loop
308 | // Code here will be executed on every animation frame
309 | app.ticker.add(() => {
310 | // Multiply the values by a coefficient to get a smooth animation
311 | uniforms.uPointerDown += (pointerDownTarget - uniforms.uPointerDown) * 0.075
312 | uniforms.uPointerDiff.x += (diffX - uniforms.uPointerDiff.x) * 0.2
313 | uniforms.uPointerDiff.y += (diffY - uniforms.uPointerDiff.y) * 0.2
314 | // Set position for the container
315 | container.x = uniforms.uPointerDiff.x - centerX
316 | container.y = uniforms.uPointerDiff.y - centerY
317 | // Check rects and load/cancel images as needded
318 | checkRectsAndImages()
319 | })
320 | }
321 |
322 | // Clean the current Application
323 | function clean () {
324 | // Stop the current animation
325 | app.ticker.stop()
326 |
327 | // Remove event listeners
328 | app.stage
329 | .off('pointerdown', onPointerDown)
330 | .off('pointerup', onPointerUp)
331 | .off('pointerupoutside', onPointerUp)
332 | .off('pointermove', onPointerMove)
333 |
334 | // Abort all fetch calls in progress
335 | rects.forEach(rect => {
336 | if (rect.discovered && !rect.loaded) {
337 | rect.controller.abort()
338 | }
339 | })
340 | }
341 |
342 | // On resize, reinit the app (clean and init)
343 | // But first debounce the calls, so we don't call init too often
344 | let resizeTimer
345 | function onResize () {
346 | if (resizeTimer) clearTimeout(resizeTimer)
347 | resizeTimer = setTimeout(() => {
348 | clean()
349 | init()
350 | }, 200)
351 | }
352 | // Listen to resize event
353 | window.addEventListener('resize', onResize)
354 |
355 | // Load resources, then init the app
356 | PIXI.Loader.shared.add([
357 | 'shaders/stageFragment.glsl',
358 | 'shaders/backgroundFragment.glsl'
359 | ]).load(init)
360 |
361 | })()
362 |
--------------------------------------------------------------------------------
/js/scripts.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Class to generate a random masonry layout, using a square grid as base
4 | class Grid {
5 |
6 | // The constructor receives all the following parameters:
7 | // - gridSize: The size (width and height) for smallest unit size
8 | // - gridColumns: Number of columns for the grid (width = gridColumns * gridSize)
9 | // - gridRows: Number of rows for the grid (height = gridRows * gridSize)
10 | // - gridMin: Min width and height limits for rectangles (in grid units)
11 | constructor(gridSize, gridColumns, gridRows, gridMin) {
12 | this.gridSize = gridSize
13 | this.gridColumns = gridColumns
14 | this.gridRows = gridRows
15 | this.gridMin = gridMin
16 | this.rects = []
17 | this.currentRects = [{ x: 0, y: 0, w: this.gridColumns, h: this.gridRows }]
18 | }
19 |
20 | // Takes the first rectangle on the list, and divides it in 2 more rectangles if possible
21 | splitCurrentRect () {
22 | if (this.currentRects.length) {
23 | const currentRect = this.currentRects.shift()
24 | const cutVertical = currentRect.w > currentRect.h
25 | const cutSide = cutVertical ? currentRect.w : currentRect.h
26 | const cutSize = cutVertical ? 'w' : 'h'
27 | const cutAxis = cutVertical ? 'x' : 'y'
28 | if (cutSide > this.gridMin * 2) {
29 | const rect1Size = randomInRange(this.gridMin, cutSide - this.gridMin)
30 | const rect1 = Object.assign({}, currentRect, { [cutSize]: rect1Size })
31 | const rect2 = Object.assign({}, currentRect, { [cutAxis]: currentRect[cutAxis] + rect1Size, [cutSize]: currentRect[cutSize] - rect1Size })
32 | this.currentRects.push(rect1, rect2)
33 | }
34 | else {
35 | this.rects.push(currentRect)
36 | this.splitCurrentRect()
37 | }
38 | }
39 | }
40 |
41 | // Call `splitCurrentRect` until there is no more rectangles on the list
42 | // Then return the list of rectangles
43 | generateRects () {
44 | while (this.currentRects.length) {
45 | this.splitCurrentRect()
46 | }
47 | return this.rects
48 | }
49 | }
50 |
51 | // Generate a random integer in the range provided
52 | function randomInRange (min, max) {
53 | return Math.floor(Math.random() * (max - min + 1)) + min
54 | }
55 |
56 | // Get canvas view
57 | const view = document.querySelector('.view')
58 | // Loaded resources will be here
59 | const resources = PIXI.Loader.shared.resources
60 | // Target for pointer. If down, value is 1, else value is 0
61 | let pointerDownTarget = 0
62 | // Useful variables to keep track of the pointer
63 | let pointerStart = new PIXI.Point()
64 | let pointerDiffStart = new PIXI.Point()
65 | let width, height
66 | let container, background
67 | let uniforms
68 | let app
69 | let diffX, diffY
70 |
71 | // Variables and settings for grid
72 | const gridSize = 50
73 | const gridMin = 3
74 | const imagePadding = 20
75 | let gridColumnsCount, gridRowsCount, gridColumns, gridRows, grid
76 | let widthRest, heightRest, centerX, centerY
77 | let rects, images, imagesUrls
78 |
79 | // Set dimensions
80 | function initDimensions () {
81 | width = window.innerWidth
82 | height = window.innerHeight
83 | diffX = 0
84 | diffY = 0
85 | }
86 |
87 | // Set initial values for uniforms
88 | function initUniforms () {
89 | uniforms = {
90 | uResolution: new PIXI.Point(width, height),
91 | uPointerDiff: new PIXI.Point(),
92 | uPointerDown: pointerDownTarget
93 | }
94 | }
95 |
96 | // Initialize the random grid layout
97 | function initGrid () {
98 | // Getting columns
99 | gridColumnsCount = Math.ceil(width / gridSize)
100 | // Getting rows
101 | gridRowsCount = Math.ceil(height / gridSize)
102 | // Make the grid 5 times bigger than viewport
103 | gridColumns = gridColumnsCount * 5
104 | gridRows = gridRowsCount * 5
105 | // Create a new Grid instance with our settings
106 | grid = new Grid(gridSize, gridColumns, gridRows, gridMin)
107 | // Calculate the center position for the grid in the viewport
108 | widthRest = Math.ceil(gridColumnsCount * gridSize - width)
109 | heightRest = Math.ceil(gridRowsCount * gridSize - height)
110 | centerX = (gridColumns * gridSize / 2) - (gridColumnsCount * gridSize / 2)
111 | centerY = (gridRows * gridSize / 2) - (gridRowsCount * gridSize / 2)
112 | // Generate the list of rects
113 | rects = grid.generateRects()
114 | // For the list of images
115 | images = []
116 | // For storing the image URL and avoid duplicates
117 | imagesUrls = {}
118 | }
119 |
120 | // Init the PixiJS Application
121 | function initApp () {
122 | // Create a PixiJS Application, using the view (canvas) provided
123 | app = new PIXI.Application({ view })
124 | // Resizes renderer view in CSS pixels to allow for resolutions other than 1
125 | app.renderer.autoDensity = true
126 | // Resize the view to match viewport size
127 | app.renderer.resize(width, height)
128 |
129 | // Set the distortion filter for the entire stage
130 | const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
131 | const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
132 | app.stage.filters = [stageFilter]
133 | }
134 |
135 | // Init the gridded background
136 | function initBackground () {
137 | // Create a new empty Sprite and define its size
138 | background = new PIXI.Sprite()
139 | background.width = width
140 | background.height = height
141 | // Get the code for the fragment shader from the loaded resources
142 | const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
143 | // Create a new Filter using the fragment shader and the uniforms
144 | // We don't need a custom vertex shader, so we set it as `undefined`
145 | const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader, uniforms)
146 | // Assign the filter to the background Sprite
147 | background.filters = [backgroundFilter]
148 | // Add the background to the stage
149 | app.stage.addChild(background)
150 | }
151 |
152 | // Initialize a Container element for solid rectangles and images
153 | function initContainer () {
154 | container = new PIXI.Container()
155 | app.stage.addChild(container)
156 | }
157 |
158 | // Load texture for an image, giving its index
159 | function loadTextureForImage (index) {
160 | // Get image Sprite
161 | const image = images[index]
162 | // Set the url to get a random image from Unsplash Source, given image dimensions
163 | const url = `https://source.unsplash.com/random/${image.width}x${image.height}`
164 | // Get the corresponding rect, to store more data needed (it is a normal Object)
165 | const rect = rects[index]
166 | // Create a new AbortController, to abort fetch if needed
167 | const { signal } = rect.controller = new AbortController()
168 | // Fetch the image
169 | fetch(url, { signal }).then(response => {
170 | // Get image URL, and if it was downloaded before, load another image
171 | // Otherwise, save image URL and set the texture
172 | const id = response.url.split('?')[0]
173 | if (imagesUrls[id]) {
174 | loadTextureForImage(index)
175 | } else {
176 | imagesUrls[id] = true
177 | image.texture = PIXI.Texture.from(response.url)
178 | rect.loaded = true
179 | }
180 | }).catch(() => {
181 | // Catch errors silently, for not showing the following error message if it is aborted:
182 | // AbortError: The operation was aborted.
183 | })
184 | }
185 |
186 | // Add solid rectangles and images
187 | function initRectsAndImages () {
188 | // Create a new Graphics element to draw solid rectangles
189 | const graphics = new PIXI.Graphics()
190 | // Select the color for rectangles
191 | graphics.beginFill(0x000000)
192 | // Loop over each rect in the list
193 | rects.forEach(rect => {
194 | // Create a new Sprite element for each image
195 | const image = new PIXI.Sprite()
196 | // Set image's position and size
197 | image.x = rect.x * gridSize
198 | image.y = rect.y * gridSize
199 | image.width = rect.w * gridSize - imagePadding
200 | image.height = rect.h * gridSize - imagePadding
201 | // Set it's alpha to 0, so it is not visible initially
202 | image.alpha = 0
203 | // Add image to the list
204 | images.push(image)
205 | // Draw the rectangle
206 | graphics.drawRect(image.x, image.y, image.width, image.height)
207 | })
208 | // Ends the fill action
209 | graphics.endFill()
210 | // Add the graphics (with all drawn rects) to the container
211 | container.addChild(graphics)
212 | // Add all image's Sprites to the container
213 | images.forEach(image => {
214 | container.addChild(image)
215 | })
216 | }
217 |
218 | // Check if rects intersects with the viewport
219 | // and loads corresponding image
220 | function checkRectsAndImages () {
221 | // Loop over rects
222 | rects.forEach((rect, index) => {
223 | // Get corresponding image
224 | const image = images[index]
225 | // Check if the rect intersects with the viewport
226 | if (rectIntersectsWithViewport(rect)) {
227 | // If rect just has been discovered
228 | // start loading image
229 | if (!rect.discovered) {
230 | rect.discovered = true
231 | loadTextureForImage(index)
232 | }
233 | // If image is loaded, increase alpha if possible
234 | if (rect.loaded && image.alpha < 1) {
235 | image.alpha += 0.01
236 | }
237 | } else { // The rect is not intersecting
238 | // If the rect was discovered before, but the
239 | // image is not loaded yet, abort the fetch
240 | if (rect.discovered && !rect.loaded) {
241 | rect.discovered = false
242 | rect.controller.abort()
243 | }
244 | // Decrease alpha if possible
245 | if (image.alpha > 0) {
246 | image.alpha -= 0.01
247 | }
248 | }
249 | })
250 | }
251 |
252 | // Check if a rect intersects the viewport
253 | function rectIntersectsWithViewport (rect) {
254 | return (
255 | rect.x * gridSize + container.x <= width &&
256 | 0 <= (rect.x + rect.w) * gridSize + container.x &&
257 | rect.y * gridSize + container.y <= height &&
258 | 0 <= (rect.y + rect.h) * gridSize + container.y
259 | )
260 | }
261 |
262 | // Start listening events
263 | function initEvents () {
264 | // Make stage interactive, so it can listen to events
265 | app.stage.interactive = true
266 |
267 | // Pointer & touch events are normalized into
268 | // the `pointer*` events for handling different events
269 | app.stage
270 | .on('pointerdown', onPointerDown)
271 | .on('pointerup', onPointerUp)
272 | .on('pointerupoutside', onPointerUp)
273 | .on('pointermove', onPointerMove)
274 | }
275 |
276 | // On pointer down, save coordinates and set pointerDownTarget
277 | function onPointerDown (e) {
278 | const { x, y } = e.data.global
279 | pointerDownTarget = 1
280 | pointerStart.set(x, y)
281 | pointerDiffStart = uniforms.uPointerDiff.clone()
282 | }
283 |
284 | // On pointer up, set pointerDownTarget
285 | function onPointerUp () {
286 | pointerDownTarget = 0
287 | }
288 |
289 | // On pointer move, calculate coordinates diff
290 | function onPointerMove (e) {
291 | const { x, y } = e.data.global
292 | if (pointerDownTarget) {
293 | diffX = pointerDiffStart.x + (x - pointerStart.x)
294 | diffY = pointerDiffStart.y + (y - pointerStart.y)
295 | diffX = diffX > 0 ? Math.min(diffX, centerX + imagePadding) : Math.max(diffX, -(centerX + widthRest))
296 | diffY = diffY > 0 ? Math.min(diffY, centerY + imagePadding) : Math.max(diffY, -(centerY + heightRest))
297 | }
298 | }
299 |
300 | // Init everything
301 | function init () {
302 | initDimensions()
303 | initUniforms()
304 | initGrid()
305 | initApp()
306 | initBackground()
307 | initContainer()
308 | initRectsAndImages()
309 | initEvents()
310 |
311 | // Animation loop
312 | // Code here will be executed on every animation frame
313 | app.ticker.add(() => {
314 | // Multiply the values by a coefficient to get a smooth animation
315 | uniforms.uPointerDown += (pointerDownTarget - uniforms.uPointerDown) * 0.075
316 | uniforms.uPointerDiff.x += (diffX - uniforms.uPointerDiff.x) * 0.2
317 | uniforms.uPointerDiff.y += (diffY - uniforms.uPointerDiff.y) * 0.2
318 | // Set position for the container
319 | container.x = uniforms.uPointerDiff.x - centerX
320 | container.y = uniforms.uPointerDiff.y - centerY
321 | // Check rects and load/cancel images as needded
322 | checkRectsAndImages()
323 | })
324 | }
325 |
326 | // Clean the current Application
327 | function clean () {
328 | // Stop the current animation
329 | app.ticker.stop()
330 |
331 | // Remove event listeners
332 | app.stage
333 | .off('pointerdown', onPointerDown)
334 | .off('pointerup', onPointerUp)
335 | .off('pointerupoutside', onPointerUp)
336 | .off('pointermove', onPointerMove)
337 |
338 | // Abort all fetch calls in progress
339 | rects.forEach(rect => {
340 | if (rect.discovered && !rect.loaded) {
341 | rect.controller.abort()
342 | }
343 | })
344 | }
345 |
346 | // On resize, reinit the app (clean and init)
347 | // But first debounce the calls, so we don't call init too often
348 | let resizeTimer
349 | function onResize () {
350 | if (resizeTimer) clearTimeout(resizeTimer)
351 | resizeTimer = setTimeout(() => {
352 | clean()
353 | init()
354 | }, 200)
355 | }
356 | // Listen to resize event
357 | window.addEventListener('resize', onResize)
358 |
359 | // Load resources, then init the app
360 | PIXI.Loader.shared.add([
361 | 'shaders/stageFragment.glsl',
362 | 'shaders/backgroundFragment.glsl'
363 | ]).load(init)
364 |
365 | })()
366 |
--------------------------------------------------------------------------------