├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── TODO.md ├── env.config.js ├── package-lock.json ├── package.json ├── project.json ├── src ├── components │ ├── rgba-material │ │ ├── index.js │ │ ├── rgba.frag │ │ └── rgba.vert │ └── texture-combine │ │ ├── index.js │ │ └── styles.js ├── index.html ├── index.js ├── lib │ ├── component.js │ ├── lit-ref.js │ └── ticker.js └── style │ ├── index.js │ └── mixins.js ├── webpack.config.js └── webpack ├── common.config.js ├── dev.config.js └── prod.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "browser": true 5 | }, 6 | 7 | "globals": { 8 | "dmaf": true, 9 | "createjs": true 10 | }, 11 | 12 | "rules": { 13 | "no-comma-dangle": 1, 14 | "no-cond-assign" : 1, 15 | "no-constant-condition": 1, 16 | "no-control-regex": 1, 17 | "no-debugger": 1, 18 | "no-dupe-keys": 1, 19 | "no-empty": 0, 20 | "no-empty-class": 1, 21 | "no-ex-assign": 1, 22 | "no-extra-boolean-cast": 0, 23 | "no-extra-parens": 0, 24 | "no-extra-semi": 1, 25 | "no-func-assign": 1, 26 | "no-inner-declarations": 0, 27 | "no-invalid-regexp": 1, 28 | "no-irregular-whitespace": 1, 29 | "no-negated-in-lhs": 1, 30 | "no-obj-calls": 1, 31 | "no-regex-spaces": 1, 32 | "no-reserved-keys": 1, 33 | "no-sparse-arrays": 1, 34 | "no-unreachable": 1, 35 | "no-use-before-define": 0, 36 | "use-isnan": 1, 37 | "valid-jsdoc": 0, 38 | "valid-typeof": 1, 39 | "space-unary-ops": [1, {"words": true, "nonwords": false}], 40 | 41 | "camelcase": 1, 42 | "curly": 0, 43 | "comma-spacing": [2, {"before": false, "after": true}], 44 | "comma-style": [2, "last"], 45 | "eol-last": 1, 46 | "func-names": 0, 47 | "func-style": 1, 48 | "key-spacing": [2, {"beforeColon": false, "afterColon": true}], 49 | "max-nested-callbacks": 0, 50 | "new-cap": 1, 51 | "new-parens": 1, 52 | "no-array-constructor": 1, 53 | "no-lonely-if": 0, 54 | "no-mixed-spaces-and-tabs": 1, 55 | "no-nested-ternary": 0, 56 | "no-new": 0, 57 | "no-new-object": 1, 58 | "no-space-before-semi": 1, 59 | "no-spaced-func": 1, 60 | "no-ternary": 0, 61 | "no-trailing-spaces": 1, 62 | "no-multiple-empty-lines": [1, {"max": 2}], 63 | "no-underscore-dangle": 0, 64 | "no-wrap-func": 1, 65 | "no-path-concat": 0, 66 | "one-var": 0, 67 | "no-unused-expressions": 0, 68 | "padded-blocks": "never", 69 | "quotes": [2, "single"], 70 | "quote-props": "as-needed", 71 | "semi": 1, 72 | "space-after-keywords": [1, "always"], 73 | "space-before-blocks": [1, "always"], 74 | "space-in-brackets": [2, "never"], 75 | "space-in-parens": [2, "never"], 76 | "space-infix-ops": 1, 77 | "space-return-throw-case": 1, 78 | "spaced-line-comment": ["always", {"exceptions": ["-","+"]}], 79 | "wrap-regex": 0, 80 | "max-params": [1, 3], 81 | "yoda": "never" 82 | } 83 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text eol=lf 3 | # Denote all files that are truly binary and should not be modified. 4 | *.png binary 5 | *.jpg binary 6 | *.gif binary 7 | *.ttf binary 8 | *.woff binary 9 | *.woff2 binary 10 | *.eot binary 11 | *.ico binary 12 | *.mp4 binary 13 | *.m4a binary 14 | *.mp3 binary 15 | *.webm binary 16 | *.ogg binary 17 | *.ogv binary 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .linaria-cache/ 3 | static/build 4 | static/assets 5 | static/index.* 6 | static/index 7 | node_modules/* 8 | npm-debug.log 9 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // Settings 3 | "passfail" : false, // Stop on first error. 4 | "maxerr" : 100, // Maximum errors before stopping. 5 | 6 | 7 | // Predefined globals whom JSHint will ignore. 8 | "browser" : true, // Standard browser globals e.g. `window`, `document`. 9 | 10 | "node" : true, 11 | 12 | "predef" : [ // Extra globals. 13 | "require", 14 | "TweenLite", 15 | "TimelineMax", 16 | ], 17 | "globals": { 18 | //GSAP Globals 19 | "Back" : false, 20 | "Bounce" : false, 21 | "Circ" : false, 22 | "Cubic" : false, 23 | "Ease" : false, 24 | "EaseLookup" : false, 25 | "Elastic" : false, 26 | "Expo" : false, 27 | "Linear" : false, 28 | "Power0" : false, 29 | "Power1" : false, 30 | "Power2" : false, 31 | "Power3" : false, 32 | "Power3" : false, 33 | "Power4" : false, 34 | "Quad" : false, 35 | "Quart" : false, 36 | "Quint" : false, 37 | "RoughEase" : false, 38 | "Sine" : false, 39 | "SlowMo" : false, 40 | "SteppedEase" : false, 41 | "Strong" : false, 42 | "Draggable" : false, 43 | "SplitText" : false, 44 | "TimelineMax" : false, 45 | "VelocityTracker" : false, 46 | "CSSPlugin" : false, 47 | "ThrowPropsPlugin" : false, 48 | "BezierPlugin" : false 49 | }, 50 | 51 | 52 | // Development. 53 | "debug" : false, // Allow debugger statements e.g. browser breakpoints. 54 | "devel" : true, // Allow development statements e.g. `console.log();`. 55 | 56 | 57 | // EcmaScript 5. 58 | "es5" : true, // Allow EcmaScript 5 syntax. 59 | "strict" : false, // Require `use strict` pragma in every file. 60 | "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). 61 | 62 | 63 | // The Good Parts. 64 | "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). 65 | "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. 66 | "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.). 67 | "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. 68 | "curly" : false, // Require {} for every new block or scope. 69 | "eqeqeq" : false, // Require triple equals i.e. `===`. 70 | "eqnull" : false, // Tolerate use of `== null`. 71 | "evil" : false, // Tolerate use of `eval`. 72 | "expr" : false, // Tolerate `ExpressionStatement` as Programs. 73 | "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`. 74 | "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 75 | "latedef" : false, // Prohibit variable use before definition. 76 | "loopfunc" : false, // Allow functions to be defined within loops. 77 | "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. 78 | "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions. 79 | "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. 80 | "scripturl" : true, // Tolerate script-targeted URLs. 81 | "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. 82 | "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. 83 | "undef" : true, // Require all non-global variables be declared before they are used. 84 | 85 | 86 | // Persone styling prefrences. 87 | "newcap" : false, // Require capitalization of all constructor functions e.g. `new F()`. 88 | "noempty" : false, // Prohibit use of empty blocks. 89 | "nonew" : false, // Prohibit use of constructors for side-effects. 90 | "nomen" : false, // Prohibit use of initial or trailing underbars in names. 91 | "onevar" : false, // Allow only one `var` statement per function. 92 | "plusplus" : false, // Prohibit use of `++` & `--`. 93 | "sub" : true, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. 94 | "trailing" : false, // Prohibit trailing whitespaces. 95 | "white" : false // Check against strict whitespace and indentation rules. 96 | } 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Florian Morel 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | texture-combine 3 | === 4 | 5 | ### A tool to combine different RGB/A channels into a single texture 6 | 7 | > :warning: WIP dead simple for now, more features (and styling) to come 8 | 9 | ![texture-combine](https://i.imgur.com/sy1QhIN.jpg) 10 | 11 | 12 | ### Usage 13 | ``` 14 | git clone https://github.com/ayamflow/texture-combine 15 | cd texture-combine 16 | npm i 17 | npm start 18 | ``` 19 | 20 | Then drag each image onto their respective square (RGB and optionally an Alpha image, which will use the red channel). 21 | Once done, right click on the result image and select "Save image". -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Stylelint for emotion 2 | https://github.com/webpack-contrib/stylelint-webpack-plugin 3 | linaria fonts?? 4 | 5 | ES5+ES6 6 | https://philipwalton.com/articles/deploying-es2015-code-in-production-today/ 7 | 8 | Store - Flow/VueX/redux/unistore? 9 | 10 | tweens 11 | https://github.com/juliangarnier/anime 12 | https://github.com/nextapps-de/fat 13 | 14 | ////////// 15 | noise/vhs glitch 16 | 17 | http://www.shaderslab.com/images/tvnoise2.jpg?crc=4021489216 18 | 19 | scratches 20 | http://www.nutty.ca/?page_id=352&link=old_film 21 | 22 | https://www.shadertoy.com/view/4dBGzK 23 | https://www.shadertoy.com/view/ldjGzV 24 | https://www.shadertoy.com/view/MlfSWr 25 | 26 | 1/ logo noise 27 | 2/ loading noise 28 | 3/ dragging noise 29 | 30 | videoTexture webgl1 31 | 32 | 33 | ===== 34 | Features 35 | 36 | Home 37 | === 38 | video states: loading/dragging 39 | - add 2 shaders √ 40 | - video playing logic (match currenttime to text %) 41 | - playing √ 42 | - drag √ 43 | - percent and& text width don't match √ 44 | - fast forward to a diff project OK but jumps back to 0 sometimes 45 | - scroll: debounce/throttle/timeout before actually seeking 46 | - fixing jump between videos 47 | - loading logic 48 | - seeking logic 49 | 50 | - video: draw in POT canvas 51 | 52 | on drag: 53 | - pause 54 | - find video id 55 | 56 | hover during animateIn yields black sphere 57 | 58 | -> by default: loading event 59 | on seek: remove loading, add playing 60 | seeking done: remove playing, add loading 61 | 62 | 63 | limit drag/scroll speed 64 | 65 | Misc 66 | === 67 | Mouse shadow: add trail 68 | - improve mouse velocity (appears/fades too fast) 69 | 70 | on visibility API stop/resume video? 71 | - merge texts geoms? 72 | - gpu detect + tweaks 73 | 74 | 75 | 76 | glitch/vhs 77 | 78 | https://www.shadertoy.com/view/XtyXzW 79 | 80 | https://www.shadertoy.com/view/Md3SRM 81 | 82 | https://www.shadertoy.com/view/MtfSz2 83 | 84 | https://www.shadertoy.com/view/WsBGRW 85 | 86 | 87 | gifs references 88 | https://giphy.com/gifs/glitch-vhs-max-capacity-yugSj8GSC0wXm 89 | https://giphy.com/gifs/lamborghini-lambo-countach-l3UcrZHrGW2CjHXqM 90 | https://giphy.com/gifs/vhs-art-11v8gJRwB2fE3u 91 | https://giphy.com/gifs/dance-power-rangers-hap-ki-do-1M2SPVruuneFO 92 | https://giphy.com/gifs/loop-glitch-art-65mCMJDdABFYs 93 | https://giphy.com/gifs/80s-vhs-4JQsNAUUCU3OU -------------------------------------------------------------------------------- /env.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dev: { 3 | baseURL: '/', 4 | assetsURL: '/assets/' 5 | }, 6 | prod: { 7 | baseURL: '/', 8 | assetsURL: '/assets/' 9 | }, 10 | staging: { 11 | baseURL: '/eien/v2/', 12 | assetsURL: '/eien/v2/assets/' 13 | } 14 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "texture-combine", 3 | "version": "0.0.1", 4 | "description": "A tool to combine different RGB/A channels into a single texture", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --open --env.development", 8 | "build": "npx webpack --env.production", 9 | "build:staging": "npx webpack --env.env=staging --env.production" 10 | }, 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@babel/cli": "^7.4.4", 15 | "@babel/core": "^7.4.5", 16 | "@babel/node": "^7.4.5", 17 | "@babel/plugin-proposal-function-bind": "^7.2.0", 18 | "@babel/plugin-transform-runtime": "^7.4.4", 19 | "@babel/preset-env": "^7.4.5", 20 | "@babel/register": "^7.4.4", 21 | "@babel/runtime": "^7.4.5", 22 | "babel-loader": "^8.0.6", 23 | "babel-minify": "^0.5.0", 24 | "clean-webpack-plugin": "^1.0.1", 25 | "copy-webpack-plugin": "^5.0.3", 26 | "css-hot-loader": "^1.4.4", 27 | "css-loader": "^2.1.1", 28 | "file-loader": "^3.0.1", 29 | "glslify": "^7.0.0", 30 | "glslify-fancy-imports": "^1.0.1", 31 | "glslify-hex": "^2.1.1", 32 | "glslify-import": "^3.1.0", 33 | "html-loader": "^0.5.5", 34 | "html-webpack-plugin": "^3.2.0", 35 | "ify-loader": "^1.1.0", 36 | "lint-staged": "^8.1.7", 37 | "mini-css-extract-plugin": "^0.5.0", 38 | "prettier": "^1.17.1", 39 | "raw-loader": "^1.0.0", 40 | "url-loader": "^1.1.2", 41 | "webpack": "^4.33.0", 42 | "webpack-bundle-analyzer": "^3.3.2", 43 | "webpack-cli": "^3.3.2", 44 | "webpack-dev-server": "^3.6.0", 45 | "webpack-merge": "^4.2.1" 46 | }, 47 | "dependencies": { 48 | "css-reset": "github:watsondg/css-reset", 49 | "linaria": "^1.3.1", 50 | "lit-element": "^2.1.0", 51 | "lit-html": "^1.1.0", 52 | "ogl": "^0.0.11", 53 | "size": "github:ayamflow/size" 54 | }, 55 | "browserify": { 56 | "transform": [ 57 | "glslify" 58 | ] 59 | }, 60 | "glslify": { 61 | "transform": [ 62 | "glslify-hex", 63 | "glslify-import", 64 | "glslify-fancy-imports" 65 | ] 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "meta": { 3 | "title": "Texture Combine", 4 | "description": "Combine custom layers into RGB/A textures", 5 | "twitter": "https://www.twitter.com/ayamflow", 6 | "siteURL": "https://ayamflow.fr" 7 | } 8 | } -------------------------------------------------------------------------------- /src/components/rgba-material/index.js: -------------------------------------------------------------------------------- 1 | import {Program, Texture} from 'ogl' 2 | const glslify = require('glslify') 3 | 4 | const CHANNELS = { 5 | RED: 'r', 6 | GREEN: 'g', 7 | BLUE: 'b', 8 | ALPHA: 'a' 9 | } 10 | 11 | export default class RGBAMaterial extends Program { 12 | constructor(gl) { 13 | super(gl, { 14 | uniforms: { 15 | r: { 16 | value: new Texture(gl) 17 | }, 18 | g: { 19 | value: new Texture(gl) 20 | }, 21 | b: { 22 | value: new Texture(gl) 23 | }, 24 | a: { 25 | value: new Texture(gl) 26 | }, 27 | useAlpha: { 28 | value: 0.0 29 | } 30 | }, 31 | vertex: glslify('./rgba.vert'), 32 | fragment: glslify('./rgba.frag'), 33 | transparent: true 34 | }) 35 | } 36 | 37 | setChannel(channel, image) { 38 | let texture = this.uniforms[channel].value 39 | texture.image = image 40 | } 41 | 42 | toggleAlpha(value) { 43 | this.uniforms.useAlpha = value === true ? 1.0 : 0.0; 44 | } 45 | 46 | set r(image) { 47 | this.setChannel(CHANNELS.RED, image) 48 | } 49 | 50 | set g(image) { 51 | this.setChannel(CHANNELS.GREEN, image) 52 | } 53 | 54 | set b(image) { 55 | this.setChannel(CHANNELS.BLUE, image) 56 | } 57 | 58 | set a(image) { 59 | this.setChannel(CHANNELS.ALPHA, image) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/rgba-material/rgba.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | uniform sampler2D r; 4 | uniform sampler2D g; 5 | uniform sampler2D b; 6 | uniform sampler2D a; 7 | uniform float useAlpha; 8 | varying vec2 vUv; 9 | 10 | void main() { 11 | gl_FragColor = vec4( 12 | texture2D(r, vUv).r, 13 | texture2D(g, vUv).g, 14 | texture2D(b, vUv).b, 15 | useAlpha == 1.0 ? texture2D(a, vUv).r : 1.0 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/rgba-material/rgba.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 position; 2 | attribute vec2 uv; 3 | varying vec2 vUv; 4 | 5 | void main() { 6 | vUv = uv; 7 | gl_Position = vec4(position, 0, 1); 8 | } 9 | -------------------------------------------------------------------------------- /src/components/texture-combine/index.js: -------------------------------------------------------------------------------- 1 | import {Component, html} from 'lib/component' 2 | import {Renderer, Geometry, Mesh } from 'ogl' 3 | import {ref} from 'lib/lit-ref' 4 | import Ticker from 'lib/ticker' 5 | import * as styles from './styles' 6 | import RGBAMaterial from '../rgba-material' 7 | 8 | const RENDER_SIZE = 1024 9 | 10 | export default class TextureCombine extends Component { 11 | init() { 12 | this.onUpdate = this.onUpdate.bind(this) 13 | this.onFileLoaded = this.onFileLoaded.bind(this) 14 | } 15 | 16 | render() { 17 | const layers = [ 18 | { channel: 'r', name: 'red'}, 19 | { channel: 'g', name: 'green'}, 20 | { channel: 'b', name: 'blue'}, 21 | { channel: 'a', name: 'alpha'}, 22 | ] 23 | 24 | return html` 25 |
26 |
27 | ${layers.map(layer => html` 28 |
34 | `)} 35 |
36 | 37 |
38 | ` 39 | } 40 | 41 | onRender() { 42 | this.initRenderer() 43 | 44 | window.addEventListener('drop', this.preventDefault) 45 | window.addEventListener('dragover', this.preventDefault) 46 | 47 | this.fileReader = new FileReader() 48 | this.fileReader.onload = this.onFileLoaded 49 | 50 | this.isBusy = false 51 | this.start() 52 | } 53 | 54 | initRenderer() { 55 | this.renderer = new Renderer({ 56 | canvas: this.refs.canvas, 57 | alpha: true, 58 | dpr: 1, 59 | preserveDrawingBuffer: true, 60 | width: RENDER_SIZE, 61 | height: RENDER_SIZE, 62 | }) 63 | const gl = this.renderer.gl 64 | const geometry = new Geometry(gl, { 65 | position: {size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3])}, 66 | uv: {size: 2, data: new Float32Array([0, 0, 2, 0, 0, 2])}, 67 | }) 68 | this.program = new RGBAMaterial(gl) 69 | this.mesh = new Mesh(gl, {geometry, program: this.program}) 70 | } 71 | 72 | preventDefault(event) { 73 | event.preventDefault() 74 | } 75 | 76 | onDragOver(event) { 77 | event.preventDefault() 78 | if (this.isBusy) return 79 | event.target.classList.add('is-drop-hover') 80 | } 81 | 82 | onDrop(event) { 83 | event.preventDefault() 84 | if (this.isBusy) return 85 | 86 | let file = event.dataTransfer.files[0] 87 | if (!file) return 88 | 89 | this.isBusy = true 90 | this.currentLayer = event.target 91 | let url = this.fileReader.readAsDataURL(file) 92 | } 93 | 94 | onDragLeave(event) { 95 | event.preventDefault() 96 | event.target.classList.remove('is-drop-hover') 97 | } 98 | 99 | async onFileLoaded(event) { 100 | let url = event.currentTarget.result 101 | 102 | 103 | let canvas = document.createElement('canvas') 104 | canvas.width = RENDER_SIZE 105 | canvas.height = RENDER_SIZE 106 | let context = canvas.getContext('2d') 107 | let img = new Image() 108 | img.src = url 109 | img.onload = () => { 110 | context.drawImage(img, 0, 0, RENDER_SIZE, RENDER_SIZE) 111 | canvas.complete = true 112 | this.currentLayer.innerHTML = `` 113 | this.updateResult(canvas) 114 | this.isBusy = false 115 | this.currentLayer = null 116 | } 117 | } 118 | 119 | updateResult(canvas) { 120 | let channel = this.currentLayer.getAttribute('data-channel') 121 | this.program.setChannel(channel, canvas) 122 | } 123 | 124 | start() { 125 | Ticker.add(this.onUpdate) 126 | } 127 | 128 | stop() { 129 | Ticker.remove(this.onUpdate) 130 | } 131 | 132 | onUpdate() { 133 | this.renderer.render({scene: this.mesh}) 134 | } 135 | } 136 | 137 | customElements.define('texture-combine', TextureCombine) 138 | -------------------------------------------------------------------------------- /src/components/texture-combine/styles.js: -------------------------------------------------------------------------------- 1 | import {css} from 'linaria' 2 | 3 | export const app = css` 4 | .canvas { 5 | width: 200px !important; 6 | height: 200px !important; 7 | border: 10px solid #34495e; 8 | display: inline-block; 9 | margin: 100px; 10 | } 11 | 12 | .layers { 13 | display: inline-block; 14 | width: 500px; 15 | white-space: initial; 16 | } 17 | 18 | .layer { 19 | width: 200px; 20 | height: 200px; 21 | border: 1px solid white; 22 | overflow: hidden; 23 | 24 | display: inline-block; 25 | margin: 20px; 26 | 27 | position: relative; 28 | &:before { 29 | content: attr(data-label); 30 | position: absolute; 31 | left: 0; 32 | right: 0; 33 | top: calc(50% - 10px); 34 | padding: 3px; 35 | background-color: rgba(black, 0.2); 36 | height: 20px; 37 | text-align: center; 38 | color: White; 39 | text-transform: uppercase; 40 | } 41 | 42 | &.is-drop-hover { 43 | background-color: #292929; 44 | } 45 | 46 | img { 47 | max-width: 100%; 48 | max-height: 100%; 49 | } 50 | } 51 | 52 | .layer--red { 53 | border-color: #c0392b; 54 | } 55 | 56 | .layer--green { 57 | border-color: #27ae60; 58 | } 59 | 60 | .layer--blue { 61 | border-color: #2980b9 62 | } 63 | 64 | .layer--alpha { 65 | border-color: #7f8c8d; 66 | } 67 | ` 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <%= htmlWebpackPlugin.options.title %> 24 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import TextureCombine from 'components/texture-combine' 2 | let $main = document.body.querySelector('main') 3 | $main.appendChild(new TextureCombine()) -------------------------------------------------------------------------------- /src/lib/component.js: -------------------------------------------------------------------------------- 1 | import {LitElement} from 'lit-element' 2 | import size from 'size' 3 | 4 | export class Component extends LitElement { 5 | createRenderRoot() { 6 | return this 7 | } 8 | 9 | constructor(options = {}) { 10 | super() 11 | this.options = Object.assign({ 12 | autoDestroy: true, 13 | autoEnter: false, 14 | }, options) 15 | this.init(options) 16 | } 17 | 18 | init(options = {}) {} 19 | 20 | async firstUpdated() { 21 | await 0 22 | this.onRender() 23 | 24 | this.onResize = this.onResize.bind(this) 25 | size.addListener(this.onResize) 26 | this.onResize(size.width, size.height) 27 | 28 | await 0 29 | this.dispatchEvent(new Event('ready')) 30 | if (this.options.autoEnter) this.enter() 31 | } 32 | 33 | onRender() {} 34 | 35 | onResize(width, height) {} 36 | 37 | enter(params = {}) { 38 | return new Promise((resolve, reject) => { 39 | return this.animateIn(params, () => { 40 | this._afterEnter() 41 | resolve() 42 | }) 43 | }) 44 | } 45 | 46 | animateIn(params, done) { 47 | done() 48 | } 49 | 50 | _afterEnter() { 51 | this.dispatchEvent(new Event('afterEnter')) 52 | } 53 | 54 | leave(params = {}) { 55 | return new Promise((resolve, reject) => { 56 | this.animateOut(params, () => { 57 | this._afterLeave() 58 | resolve() 59 | }) 60 | }) 61 | } 62 | 63 | animateOut(params, done) { 64 | done() 65 | } 66 | 67 | _afterLeave() { 68 | this.dispatchEvent(new Event('afterLeave')) 69 | if (this.options.autoDestroy) this.destroy() 70 | } 71 | 72 | onDestroy() {} 73 | 74 | destroy() { 75 | size.removeListener(this.onResize) 76 | this.onDestroy() 77 | this.remove() 78 | } 79 | } 80 | 81 | export {html} from 'lit-element' 82 | -------------------------------------------------------------------------------- /src/lib/lit-ref.js: -------------------------------------------------------------------------------- 1 | import {directive} from 'lit-html' 2 | 3 | /* 4 |
(part) => { 8 | el.refs = el.refs || {} 9 | if (el.refs[id]) { 10 | if (!(el.refs[id] instanceof Array)) el.refs[id] = [el.refs[id]] 11 | el.refs[id].push(part.committer.element) 12 | } else { 13 | el.refs[id] = part.committer.element 14 | } 15 | }) 16 | -------------------------------------------------------------------------------- /src/lib/ticker.js: -------------------------------------------------------------------------------- 1 | class Ticker { 2 | constructor() { 3 | this.rafId = -1 4 | this.elapsed = 0 5 | this.renders = [] 6 | this.update = this.update.bind(this) 7 | } 8 | 9 | start() { 10 | this.elapsed = performance.now() 11 | this.rafId = requestAnimationFrame(this.update) 12 | } 13 | 14 | stop() { 15 | cancelAnimationFrame(this.rafId) 16 | } 17 | 18 | update(now) { 19 | this.rafId = requestAnimationFrame(this.update) 20 | const dt = now - this.elapsed 21 | this.elapsed = now 22 | 23 | for (let i = 0; i < this.renders.length; i++) { 24 | this.renders[i](dt) 25 | } 26 | } 27 | 28 | add(cb) { 29 | if (this.renders.indexOf(cb) > -1) return 30 | if (!this.renders.length) this.start() 31 | this.renders.push(cb) 32 | } 33 | 34 | remove(cb) { 35 | let index = this.renders.indexOf(cb) 36 | if (index < 0) return 37 | this.renders.splice(index, 1) 38 | if (!this.renders.length) this.stop() 39 | } 40 | } 41 | 42 | export default new Ticker() 43 | -------------------------------------------------------------------------------- /src/style/index.js: -------------------------------------------------------------------------------- 1 | import {css} from 'linaria' 2 | 3 | css` 4 | :global() { 5 | @import '~css-reset/index.css'; 6 | 7 | html, body { 8 | width: 100%; 9 | height: 100%; 10 | } 11 | 12 | html { 13 | background-color: #fafbfc; 14 | } 15 | 16 | body { 17 | overscroll-behavior: none; 18 | & > main { 19 | height: 100%; 20 | } 21 | } 22 | 23 | .u-hidden { 24 | visibility: hidden; 25 | pointer-events: none; 26 | } 27 | 28 | .u-inactive { 29 | pointer-events: none; 30 | } 31 | 32 | .u-disabled { 33 | display: none; 34 | } 35 | 36 | .u-loading { 37 | cursor: wait; 38 | } 39 | } 40 | ` 41 | -------------------------------------------------------------------------------- /src/style/mixins.js: -------------------------------------------------------------------------------- 1 | export const hover = (content) => { 2 | return ` 3 | @media (any-hover: none) { 4 | &:active { 5 | ${content} 6 | } 7 | } 8 | @media (any-hover: hover) { 9 | &:hover { 10 | ${content} 11 | } 12 | } 13 | ` 14 | } 15 | 16 | export const mobile = `@media (max-width: 800px), (max-height: 600px)` 17 | export const isMobile = () => innerWidth < 800 || innerHeight < 600 -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const dev = require("./webpack/dev.config.js"); 2 | const prod = require("./webpack/prod.config.js"); 3 | 4 | module.exports = function(env) { 5 | return env.development ? dev(env) : prod(env); 6 | }; 7 | -------------------------------------------------------------------------------- /webpack/common.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const path = require('path') 3 | const CleanWebpackPlugin = require('clean-webpack-plugin') 4 | const HtmlWebpackPlugin = require('html-webpack-plugin') 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 6 | const CopyPlugin = require('copy-webpack-plugin') 7 | const project = require('../project.json') 8 | const destination = path.resolve(__dirname, '../static') 9 | 10 | module.exports = function(options = {}) { 11 | let env = options.env || (options.development ? 'dev' : 'prod') 12 | let ENV = require(path.resolve(__dirname, '../env.config.js'))[env] 13 | ENV.env = env 14 | 15 | return { 16 | entry: './src/index.js', 17 | output: { 18 | filename: 'main.js', 19 | publicPath: ENV.baseURL, 20 | path: destination, 21 | }, 22 | plugins: [ 23 | new CleanWebpackPlugin(['static'], { 24 | root: path.join(destination, '../'), 25 | }), 26 | new webpack.DefinePlugin({ 27 | ENV: JSON.stringify(ENV) 28 | }), 29 | new MiniCssExtractPlugin({ 30 | filename: 'styles.css', 31 | }), 32 | new HtmlWebpackPlugin( 33 | Object.assign( 34 | { 35 | hash: true, 36 | template: path.resolve(__dirname, '../src/index.html'), 37 | filename: destination + '/index.html', 38 | }, 39 | project.meta 40 | ) 41 | ), 42 | new CopyPlugin([ 43 | { 44 | from: path.resolve(__dirname, '../assets/**/*'), 45 | to: destination, 46 | ignore: ['*.woff', '*.woff2'], 47 | }, 48 | ]) 49 | ], 50 | module: { 51 | rules: [ 52 | { 53 | test: /\.js$/, 54 | exclude: /node_modules\/(?!(lit-html|lit-element)\/).*/, 55 | use: [ 56 | {loader: 'babel-loader'}, 57 | { 58 | loader: 'linaria/loader', 59 | options: {sourceMap: options.dev}, 60 | }, 61 | ], 62 | }, 63 | { 64 | test: /\.(html)$/, 65 | exclude: /src\/index\.html/, 66 | use: {loader: 'html-loader'}, 67 | }, 68 | { 69 | test: /\.(woff|woff2|mp4|jpeg|jpg|png|gif)$/, 70 | use: { 71 | loader: 'file-loader', 72 | options: { 73 | emitFile: true, 74 | outputPath: './', 75 | }, 76 | }, 77 | }, 78 | { 79 | test: /\.js$/, 80 | exclude: /(node_modules)/, 81 | enforce: 'post', 82 | use: { 83 | loader: 'ify-loader', 84 | }, 85 | }, 86 | { 87 | test: /\.(glsl|frag|vert)$/, 88 | exclude: /(node_modules)/, 89 | use: { 90 | loader: 'raw-loader', 91 | }, 92 | }, 93 | ], 94 | }, 95 | resolve: { 96 | alias: { 97 | sections: path.resolve(__dirname, '../src/sections/'), 98 | components: path.resolve(__dirname, '../src/components/'), 99 | lib: path.resolve(__dirname, '../src/lib/'), 100 | style: path.resolve(__dirname, '../src/style/'), 101 | assets: path.resolve(__dirname, '../assets/'), 102 | 'gl-tools': path.resolve( 103 | __dirname, 104 | '../src/components/gl-tools/' 105 | ), 106 | }, 107 | }, 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /webpack/dev.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const merge = require('webpack-merge') 3 | const common = require('./common.config.js') 4 | const path = require('path') 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 6 | 7 | module.exports = function(options = {}) { 8 | return merge(common(options), { 9 | mode: 'development', 10 | devtool: 'cheap-module-eval-source-map', 11 | devServer: { 12 | hot: true, 13 | open: false, 14 | overlay: true, 15 | stats: { 16 | chunks: false, 17 | errors: false, 18 | errorDetails: false, 19 | hash: false, 20 | version: false, 21 | timings: false, 22 | // assets: false, 23 | modules: false, 24 | reasons: false, 25 | children: false, 26 | source: false, 27 | publicPath: false, 28 | }, 29 | contentBase: 'static', 30 | publicPath: '/', 31 | historyApiFallback: true, 32 | clientLogLevel: 'error', 33 | }, 34 | plugins: [new webpack.HotModuleReplacementPlugin()], 35 | module: { 36 | rules: [ 37 | { 38 | test: /\.css$/, 39 | use: [ 40 | 'css-hot-loader', 41 | MiniCssExtractPlugin.loader, 42 | { 43 | loader: 'css-loader', 44 | options: {sourceMap: true, modules: false}, 45 | }, 46 | ], 47 | }, 48 | ], 49 | }, 50 | optimization: { 51 | usedExports: true, 52 | }, 53 | }) 54 | } -------------------------------------------------------------------------------- /webpack/prod.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const merge = require('webpack-merge') 3 | const common = require('./common.config.js') 4 | const path = require('path') 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 6 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') 7 | 8 | module.exports = function (options = {}) { 9 | return merge(common(options), { 10 | mode: 'production', 11 | devtool: 'source-map', 12 | output: { 13 | filename: 'main.js', 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.css$/, 19 | use: [ 20 | MiniCssExtractPlugin.loader, 21 | { 22 | loader: 'css-loader', 23 | options: {sourceMap: true, modules: false}, 24 | }, 25 | ], 26 | }, 27 | ], 28 | }, 29 | plugins: [ 30 | new BundleAnalyzerPlugin.BundleAnalyzerPlugin() 31 | ] 32 | }) 33 | } 34 | 35 | /* 36 | module.exports = [ 37 | merge(common({dev: false}), { 38 | mode: 'production', 39 | devtool: 'source-map', 40 | output: { 41 | filename: 'main.js', 42 | }, 43 | module: { 44 | rules: [ 45 | { 46 | test: /\.m?js$/, 47 | use: { 48 | loader: 'babel-loader', 49 | options: { 50 | presets: [ 51 | [ 52 | '@babel/preset-env', 53 | { 54 | modules: false, 55 | useBuiltIns: 'usage', 56 | targets: { 57 | browsers: [ 58 | 'Chrome >= 60', 59 | 'Safari >= 10.1', 60 | 'iOS >= 10.3', 61 | 'Firefox >= 54', 62 | 'Edge >= 15', 63 | ], 64 | }, 65 | }, 66 | ], 67 | ], 68 | }, 69 | }, 70 | }, 71 | { 72 | test: /\.css$/, 73 | use: [ 74 | MiniCssExtractPlugin.loader, 75 | { 76 | loader: 'css-loader', 77 | options: { sourceMap: false, modules: false } 78 | }, 79 | ], 80 | }, 81 | ], 82 | }, 83 | }), 84 | // merge(common({dev: false}), { 85 | // mode: 'production', 86 | // devtool: 'source-map', 87 | // output: { 88 | // filename: 'main.es5.js', 89 | // }, 90 | // module: { 91 | // rules: [ 92 | // { 93 | // test: /\.m?js$/, 94 | // use: { 95 | // loader: 'babel-loader', 96 | // options: { 97 | // presets: [ 98 | // [ 99 | // '@babel/preset-env', { 100 | // modules: false, 101 | // useBuiltIns: 'usage', 102 | // targets: { 103 | // browsers: [ 104 | // '> 1%', 105 | // 'last 2 versions', 106 | // 'Firefox ESR', 107 | // ] 108 | // } 109 | // }] 110 | // ] 111 | // } 112 | // } 113 | // }, 114 | // { 115 | // test: /\.css$/, 116 | // use: [ 117 | // MiniCssExtractPlugin.loader, { 118 | // loader: 'css-loader', 119 | // options: { 120 | // sourceMap: false, 121 | // }, 122 | // }], 123 | // } 124 | // ], 125 | // }, 126 | // }) 127 | ] 128 | */ 129 | --------------------------------------------------------------------------------