├── .babelrc.json ├── .editorconfig ├── .eslintignore ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── build ├── proton.d.ts ├── proton.js ├── proton.min.js ├── proton.min.js.map ├── proton.web.js ├── proton.web.min.js └── proton.web.min.js.map ├── eslintrc.json ├── example ├── behaviour │ ├── attraction │ │ ├── attraction1.html │ │ └── attraction2.html │ ├── collision │ │ ├── collision.html │ │ └── collisionBall.html │ ├── color │ │ └── color.html │ ├── custom │ │ ├── circle.html │ │ └── js │ │ │ └── color.min.js │ ├── cyclone │ │ └── cyclone.html │ ├── force │ │ ├── force.html │ │ ├── image │ │ │ ├── star.png │ │ │ ├── star1.png │ │ │ ├── star2.png │ │ │ └── star3.png │ │ └── star.html │ ├── gravitywell │ │ └── linebug.html │ ├── randomDirft │ │ ├── image │ │ │ └── bone.png │ │ ├── randomdirft1.html │ │ ├── randomdirft2.html │ │ └── randomdirft3.html │ ├── repulsion │ │ ├── fireflies.html │ │ ├── image │ │ │ ├── bg.jpg │ │ │ ├── particle.png │ │ │ └── star.gif │ │ └── repulsion.html │ └── rotate │ │ ├── image │ │ └── arrow.png │ │ └── rotate.html ├── emitter │ ├── destroy │ │ ├── image │ │ │ ├── 8.png │ │ │ ├── chrome.png │ │ │ ├── fox.png │ │ │ └── safari.png │ │ └── testDestroy.html │ └── followEmitter │ │ ├── followEmitter.html │ │ ├── image │ │ └── bubble.png │ │ └── mouseDown.html ├── game │ ├── colliejs │ │ ├── collie.html │ │ ├── image │ │ │ ├── bird.png │ │ │ ├── blood.png │ │ │ ├── blood2.png │ │ │ ├── ground2.png │ │ │ ├── sky.png │ │ │ └── yame_walk2.png │ │ └── js │ │ │ └── collie.min.js │ ├── crafty │ │ ├── asteroids.html │ │ ├── images │ │ │ ├── bg.png │ │ │ ├── bomb.png │ │ │ ├── fighter.png │ │ │ ├── fire.png │ │ │ └── meteorolite.png │ │ └── js │ │ │ ├── crafty.js │ │ │ └── game.js │ ├── easeljs │ │ ├── easeljs.html │ │ └── image │ │ │ ├── c1.png │ │ │ ├── c2.png │ │ │ ├── c3.png │ │ │ ├── c4.png │ │ │ ├── ground.png │ │ │ ├── hill1.png │ │ │ ├── hill2.png │ │ │ ├── p1.png │ │ │ ├── p2.png │ │ │ ├── p3.png │ │ │ ├── p4.png │ │ │ ├── p5.png │ │ │ ├── sky.png │ │ │ └── spritesheet_grant.png │ ├── phaser │ │ ├── imgs │ │ │ ├── bg.jpg │ │ │ ├── dot.png │ │ │ ├── dot的副本.png │ │ │ └── logo.png │ │ └── phaser.html │ ├── pixijs │ │ ├── assets │ │ │ ├── Pixie.atlas │ │ │ ├── Pixie.json │ │ │ ├── Pixie.png │ │ │ ├── iP4_BGtile.jpg │ │ │ └── iP4_ground.png │ │ ├── image │ │ │ ├── gold_anim.json │ │ │ ├── gold_anim.png │ │ │ ├── rain.png │ │ │ └── smoke.png │ │ ├── index.html │ │ └── pixi-game.html │ └── quarkjs │ │ ├── images │ │ ├── body_walk.png │ │ ├── head_idle.png │ │ └── rain.png │ │ ├── js │ │ ├── Rain.js │ │ ├── Squirrel.js │ │ └── quark.base-1.0.0.min.js │ │ └── squirrel.html ├── helloworld │ └── emitter │ │ └── emitter.html ├── index.html ├── initialize │ ├── imagetarget │ │ ├── Spitfire.html │ │ ├── fire2.html │ │ ├── image │ │ │ ├── particle.png │ │ │ ├── pt.jpg │ │ │ └── rock.png │ │ ├── imagetarget.html │ │ └── spitfires.html │ └── position │ │ └── bg-particle.html ├── lib │ ├── TweenLite.min.js │ ├── color.min.js │ ├── easeljs-0.8.2.min.js │ ├── jquery-1.9.1.min.js │ ├── phaser.min.js │ ├── pixi-spine-4.1.js │ ├── pixi-spine.js │ ├── pixi.js │ ├── pixi.min.js │ ├── preloadjs-0.6.2.min.js │ ├── profiler.js │ ├── requestAnimationFrame.min.js │ └── stats.min.js ├── render │ ├── custom │ │ ├── custom-renderer.html │ │ └── popup.html │ ├── dom │ │ ├── canvasVSdom.html │ │ ├── domrender.html │ │ ├── image │ │ │ ├── 8.png │ │ │ ├── chrome.png │ │ │ ├── fox.png │ │ │ └── safari.png │ │ ├── jpgmove.html │ │ └── webglVSdom.html │ ├── easeljs │ │ ├── easeljs.html │ │ └── image │ │ │ └── daisy.png │ ├── pixel │ │ ├── google.html │ │ ├── google │ │ │ ├── logo1.png │ │ │ └── logo2.png │ │ ├── image │ │ │ ├── glaxy.jpg │ │ │ ├── logo1.png │ │ │ ├── logo2.png │ │ │ └── logo3.png │ │ ├── js │ │ │ ├── PxLoader.js │ │ │ ├── PxLoaderImage.js │ │ │ └── PxLoaderSound.js │ │ ├── pixelrender.html │ │ └── thousand.html │ ├── pixi │ │ ├── image │ │ │ ├── bunny.png │ │ │ ├── eggHead.png │ │ │ ├── flowerTop.png │ │ │ ├── helmlok.png │ │ │ └── particle.png │ │ ├── pixi-mulirender.html │ │ └── pixirender.html │ └── webgl │ │ ├── image │ │ └── particle.png │ │ └── webglrender.html ├── sparks │ ├── bigfire │ │ ├── bigfire.html │ │ └── image │ │ │ └── fire.png │ ├── bomb │ │ ├── bomb.html │ │ └── image │ │ │ └── particle.png │ ├── drugs │ │ ├── drugs.html │ │ └── image │ │ │ └── particle.png │ ├── eightDiagrams │ │ ├── eightDiagrams.html │ │ └── image │ │ │ └── particle.png │ ├── firework │ │ ├── bg.jpg │ │ └── fireworks.html │ └── sun │ │ ├── fireball.html │ │ ├── image │ │ └── particle.png │ │ └── sun.html └── zone │ ├── circlezone │ ├── circlezone.html │ └── image │ │ ├── n.png │ │ ├── o.png │ │ ├── p.png │ │ ├── r.png │ │ └── t.png │ ├── imagezone │ ├── image │ │ └── logo.png │ └── imagezone.html │ ├── linezone │ └── bound.html │ └── pointzone │ ├── js │ └── color.min.js │ └── pointzone.html ├── package-lock.json ├── package.json ├── rollup.config.js ├── script └── makeexamplepage.js ├── src ├── behaviour │ ├── Alpha.js │ ├── Attraction.js │ ├── Behaviour.js │ ├── Collision.js │ ├── Color.js │ ├── CrossZone.js │ ├── Cyclone.js │ ├── Force.js │ ├── Gravity.js │ ├── GravityWell.js │ ├── RandomDrift.js │ ├── Repulsion.js │ ├── Rotate.js │ └── Scale.js ├── core │ ├── Particle.js │ ├── Pool.js │ ├── Proton.js │ └── Settings.js ├── debug │ ├── Debug.js │ └── Stats.js ├── emitter │ ├── BehaviourEmitter.js │ ├── Emitter.js │ └── FollowEmitter.js ├── events │ └── EventDispatcher.js ├── index.js ├── initialize │ ├── Body.js │ ├── Initialize.js │ ├── InitializeUtil.js │ ├── Life.js │ ├── Mass.js │ ├── Position.js │ ├── Radius.js │ ├── Rate.js │ └── Velocity.js ├── math │ ├── ArraySpan.js │ ├── Integration.js │ ├── Mat3.js │ ├── MathUtil.js │ ├── Polar2D.js │ ├── Rectangle.js │ ├── Span.js │ ├── Vector2D.js │ └── ease.js ├── render │ ├── BaseRenderer.js │ ├── CanvasRenderer.js │ ├── CustomRenderer.js │ ├── DomRenderer.js │ ├── EaselRenderer.js │ ├── PixelRenderer.js │ ├── PixiRenderer.js │ └── WebGLRenderer.js ├── utils │ ├── ColorUtil.js │ ├── DomUtil.js │ ├── ImgUtil.js │ ├── MStack.js │ ├── PropUtil.js │ ├── Puid.js │ ├── Rgb.js │ ├── Types.js │ ├── Util.js │ └── WebGLUtil.js └── zone │ ├── CircleZone.js │ ├── ImageZone.js │ ├── LineZone.js │ ├── PointZone.js │ ├── RectZone.js │ └── Zone.js └── tsconfig.json /.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false, 7 | "targets": ">0.25%", 8 | "loose": true, 9 | "bugfixes": true 10 | } 11 | ] 12 | ], 13 | "plugins": [ 14 | [ 15 | "@babel/plugin-proposal-class-properties", 16 | { 17 | "loose": true 18 | } 19 | ] 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | max_line_length = 120 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | build 3 | example -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [16.x, 18.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run lint 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDEs and editors 2 | .idea 3 | .project 4 | .classpath 5 | .c9/ 6 | *.launch 7 | .settings/ 8 | *.sublime-workspace 9 | 10 | # IDE - VSCode 11 | .vscode/* 12 | !.vscode/settings.json 13 | !.vscode/tasks.json 14 | !.vscode/launch.json 15 | !.vscode/extensions.json 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | # next.js build output 25 | .next 26 | 27 | # Lerna 28 | lerna-debug.log 29 | 30 | # System Files 31 | .DS_Store 32 | Thumbs.db 33 | 34 | # node modules 35 | node_modules 36 | 37 | # documentation 38 | build/docs/ 39 | 40 | .DS_Store 41 | source/build/ 42 | static/ 43 | yarn.lock 44 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | DS_Store 2 | git/ 3 | node_modules/ 4 | example/ 5 | examples/ 6 | .github/ 7 | images/ 8 | static/ 9 | script/ 10 | source/ 11 | source/build/ 12 | 13 | # testing 14 | /coverage 15 | /.github 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | .code.yml 24 | 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | .babelrc.json 30 | .*.swp 31 | .git 32 | .hg 33 | .npmrc 34 | .lock-wscript 35 | .svn 36 | .wafpickle-* 37 | .travis.yml 38 | .eslintignore 39 | .editorconfig 40 | config.gypi 41 | CVS 42 | npm-debug.log 43 | bower.json 44 | 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | script: 5 | - npm run travis 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Proton authors. 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 | -------------------------------------------------------------------------------- /example/behaviour/color/color.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | proton.js-behaviour-custom 5 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /example/behaviour/custom/js/color.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/color.js/blob/master/color.js*/ 2 | "use strict";var Color=(function(){var g="string",f=function f(n,m,h,i){var j=this,l=arguments.length,k=function(p){return parseInt(p,16)};if(l<3){if(typeof n===g){n=n.substr(n.indexOf("#")+1);var o=n.length===3;n=k(n);o&&(n=(((n&3840)*4352)|((n&240)*272)|((n&15)*17)))}l===2&&(i=m);m=(n&65280)/256;h=n&255;n=n>>>16}if(!(j instanceof f)){return new f(n,m,h,i)}this.channels=[typeof n===g&&k(n)||n,typeof m===g&&k(m)||m,typeof h===g&&k(h)||h,(typeof i!==g&&typeof i!=="number")&&1||typeof i===g&&parseFloat(i)||i]},d=f.prototype,b="undefined",a="toLowerCase",c=Math,e;f.RGBtoHSL=function(p){var i=p[0],n=p[1],q=p[2];i/=255;n/=255;q/=255;var t=c.max(i,n,q),k=c.min(i,n,q),m,u,j=(t+k)/2;if(t===k){m=u=0}else{var o=t-k;u=j>0.5?o/(2-t-k):o/(t+k);switch(t){case i:m=(n-q)/o+(n1){h-=1}if(h<1/6){return r+(l-r)*6*h}if(h<1/2){return l}if(h<2/3){return r+(l-r)*(2/3-h)*6}return r};if(w===0){i=t=u=n}else{var j=n<0.5?n*(1+w):n+w-n*w,k=2*n-j;i=m(k,j,o+1/3);t=m(k,j,o);u=m(k,j,o-1/3)}return[i*255,t*255,u*255]};f.rgb=function(k,j,h,i){return new f(k,j,h,typeof i!==b?i:1)};f.hsl=function(o,n,j,i){var k=f.HSLtoRGB([o,n,j]),m=c.ceil;return new f(m(k[0]),m(k[1]),m(k[2]),typeof i!==b?i:1)};f.TO_STRING_METHOD="hexTriplet";f.parse=function(h){h=h.replace(/^\s+/g,"")[a]();if(h[0]==="#"){return new f(h)}var k=h.substr(0,3),j;h=h.replace(/[^\d,.]/g,"").split(",");j=h.length;while(j--){h[j]=h[j]&&parseFloat(h[j])||0}switch(k){case"rgb":return f.rgb.apply(f,h);case"hsl":h[0]/=360;h[1]/=100;h[2]/=100;return f.hsl.apply(f,h)}return null};(f.clearColors=function(){e={transparent:[0,0,0,0]}})();f.define=function(h,i){e[h[a]()]=i};f.get=function(h){h=h[a]();if(Object.prototype.hasOwnProperty.call(e,h)){return f.apply(null,[].concat(e[h]))}return null};f.del=function(h){return delete e[h[a]()]};f.random=function(k,j){typeof k===g&&(k=f.get(k))&&(k=k.getValue());typeof j===g&&(j=f.get(j))&&(j=j.getValue());var i=c.floor,h=c.random;j=(j||16777215)+1;if(!isNaN(k)){return new f(i((h()*(j-k))+k))}return new f(i(h()*j))};d.toString=function(){return this[f.TO_STRING_METHOD]()};d.valueOf=d.getValue=function(){var h=this.channels;return((h[0]*65536)|(h[1]*256)|h[2])};d.setValue=function(h){this.channels.splice(0,3,h>>>16,(h&65280)/256,h&255)};d.hexTriplet=("01".substr(-1)==="1"?function(){return"#"+("00000"+this.getValue().toString(16)).substr(-6)}:function(){var h=this.getValue().toString(16);return"#"+(new Array(h.length<6?6-h.length+1:0)).join("0")+h});d.css=function(){var h=this;return h.channels[3]===1?h.hexTriplet():h.rgba()};d.rgbData=function(){return this.channels.slice(0,3)};d.hslData=function(){return f.RGBtoHSL(this.rgbData())};d.rgb=function(){return"rgb("+this.rgbData().join(",")+")"};d.rgba=function(){return"rgba("+this.channels.join(",")+")"};d.hsl=function(){var h=this.hslData();return"hsl("+h[0]*360+","+(h[1]*100)+"%,"+(h[2]*100)+"%)"};d.hsla=function(){var h=this.hslData();return"hsla("+h[0]*360+","+(h[1]*100)+"%,"+(h[2]*100)+"%,"+this.channels[3]+")"};return f}()); 3 | -------------------------------------------------------------------------------- /example/behaviour/cyclone/cyclone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | proton.js-Cyclone 5 | 10 | 11 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /example/behaviour/force/image/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/force/image/star.png -------------------------------------------------------------------------------- /example/behaviour/force/image/star1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/force/image/star1.png -------------------------------------------------------------------------------- /example/behaviour/force/image/star2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/force/image/star2.png -------------------------------------------------------------------------------- /example/behaviour/force/image/star3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/force/image/star3.png -------------------------------------------------------------------------------- /example/behaviour/randomDirft/image/bone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/randomDirft/image/bone.png -------------------------------------------------------------------------------- /example/behaviour/randomDirft/randomdirft1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | proton.js-behaviour-RandomDirft1 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /example/behaviour/randomDirft/randomdirft3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | proton.js-behaviour-RandomDirft3 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /example/behaviour/repulsion/image/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/repulsion/image/bg.jpg -------------------------------------------------------------------------------- /example/behaviour/repulsion/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/repulsion/image/particle.png -------------------------------------------------------------------------------- /example/behaviour/repulsion/image/star.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/repulsion/image/star.gif -------------------------------------------------------------------------------- /example/behaviour/rotate/image/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/behaviour/rotate/image/arrow.png -------------------------------------------------------------------------------- /example/emitter/destroy/image/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/emitter/destroy/image/8.png -------------------------------------------------------------------------------- /example/emitter/destroy/image/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/emitter/destroy/image/chrome.png -------------------------------------------------------------------------------- /example/emitter/destroy/image/fox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/emitter/destroy/image/fox.png -------------------------------------------------------------------------------- /example/emitter/destroy/image/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/emitter/destroy/image/safari.png -------------------------------------------------------------------------------- /example/emitter/followEmitter/followEmitter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | proton.js-emitter-FlowEmitter 5 | 10 | 11 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/emitter/followEmitter/image/bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/emitter/followEmitter/image/bubble.png -------------------------------------------------------------------------------- /example/emitter/followEmitter/mouseDown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | proton.js-emitter-FlowEmitter 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /example/game/colliejs/image/bird.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/colliejs/image/bird.png -------------------------------------------------------------------------------- /example/game/colliejs/image/blood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/colliejs/image/blood.png -------------------------------------------------------------------------------- /example/game/colliejs/image/blood2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/colliejs/image/blood2.png -------------------------------------------------------------------------------- /example/game/colliejs/image/ground2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/colliejs/image/ground2.png -------------------------------------------------------------------------------- /example/game/colliejs/image/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/colliejs/image/sky.png -------------------------------------------------------------------------------- /example/game/colliejs/image/yame_walk2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/colliejs/image/yame_walk2.png -------------------------------------------------------------------------------- /example/game/crafty/asteroids.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Crafty-Proton 10 | 11 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /example/game/crafty/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/crafty/images/bg.png -------------------------------------------------------------------------------- /example/game/crafty/images/bomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/crafty/images/bomb.png -------------------------------------------------------------------------------- /example/game/crafty/images/fighter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/crafty/images/fighter.png -------------------------------------------------------------------------------- /example/game/crafty/images/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/crafty/images/fire.png -------------------------------------------------------------------------------- /example/game/crafty/images/meteorolite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/crafty/images/meteorolite.png -------------------------------------------------------------------------------- /example/game/easeljs/image/c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/c1.png -------------------------------------------------------------------------------- /example/game/easeljs/image/c2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/c2.png -------------------------------------------------------------------------------- /example/game/easeljs/image/c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/c3.png -------------------------------------------------------------------------------- /example/game/easeljs/image/c4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/c4.png -------------------------------------------------------------------------------- /example/game/easeljs/image/ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/ground.png -------------------------------------------------------------------------------- /example/game/easeljs/image/hill1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/hill1.png -------------------------------------------------------------------------------- /example/game/easeljs/image/hill2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/hill2.png -------------------------------------------------------------------------------- /example/game/easeljs/image/p1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/p1.png -------------------------------------------------------------------------------- /example/game/easeljs/image/p2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/p2.png -------------------------------------------------------------------------------- /example/game/easeljs/image/p3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/p3.png -------------------------------------------------------------------------------- /example/game/easeljs/image/p4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/p4.png -------------------------------------------------------------------------------- /example/game/easeljs/image/p5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/p5.png -------------------------------------------------------------------------------- /example/game/easeljs/image/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/sky.png -------------------------------------------------------------------------------- /example/game/easeljs/image/spritesheet_grant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/easeljs/image/spritesheet_grant.png -------------------------------------------------------------------------------- /example/game/phaser/imgs/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/phaser/imgs/bg.jpg -------------------------------------------------------------------------------- /example/game/phaser/imgs/dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/phaser/imgs/dot.png -------------------------------------------------------------------------------- /example/game/phaser/imgs/dot的副本.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/phaser/imgs/dot的副本.png -------------------------------------------------------------------------------- /example/game/phaser/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/phaser/imgs/logo.png -------------------------------------------------------------------------------- /example/game/pixijs/assets/Pixie.atlas: -------------------------------------------------------------------------------- 1 | Pixie.png 2 | format: RGBA8888 3 | filter: Linear,Linear 4 | repeat: none 5 | L_foot 6 | rotate: false 7 | xy: 1048, 355 8 | size: 68, 51 9 | orig: 68, 51 10 | offset: 0, 0 11 | index: -1 12 | L_hand 13 | rotate: true 14 | xy: 916, 355 15 | size: 55, 64 16 | orig: 55, 64 17 | offset: 0, 0 18 | index: -1 19 | L_lower_arm 20 | rotate: false 21 | xy: 1273, 226 22 | size: 70, 31 23 | orig: 70, 31 24 | offset: 0, 0 25 | index: -1 26 | L_lower_leg 27 | rotate: false 28 | xy: 1201, 345 29 | size: 57, 65 30 | orig: 57, 65 31 | offset: 0, 0 32 | index: -1 33 | L_upper_arm 34 | rotate: true 35 | xy: 1399, 2 36 | size: 78, 74 37 | orig: 78, 74 38 | offset: 0, 0 39 | index: -1 40 | L_upper_leg 41 | rotate: false 42 | xy: 1351, 259 43 | size: 124, 71 44 | orig: 124, 71 45 | offset: 0, 0 46 | index: -1 47 | R_foot 48 | rotate: false 49 | xy: 1319, 345 50 | size: 68, 51 51 | orig: 68, 51 52 | offset: 0, 0 53 | index: -1 54 | R_hand 55 | rotate: true 56 | xy: 982, 355 57 | size: 55, 64 58 | orig: 55, 64 59 | offset: 0, 0 60 | index: -1 61 | R_lower_arm 62 | rotate: false 63 | xy: 1345, 226 64 | size: 70, 31 65 | orig: 70, 31 66 | offset: 0, 0 67 | index: -1 68 | R_lower_leg 69 | rotate: false 70 | xy: 1260, 345 71 | size: 57, 65 72 | orig: 57, 65 73 | offset: 0, 0 74 | index: -1 75 | R_upper_arm 76 | rotate: true 77 | xy: 1399, 82 78 | size: 78, 74 79 | orig: 78, 74 80 | offset: 0, 0 81 | index: -1 82 | R_upper_leg 83 | rotate: false 84 | xy: 1389, 332 85 | size: 124, 71 86 | orig: 124, 71 87 | offset: 0, 0 88 | index: -1 89 | backHair 90 | rotate: true 91 | xy: 1096, 2 92 | size: 261, 175 93 | orig: 261, 175 94 | offset: 0, 0 95 | index: -1 96 | foreWing 97 | rotate: false 98 | xy: 1096, 265 99 | size: 253, 78 100 | orig: 253, 78 101 | offset: 0, 0 102 | index: -1 103 | frontHair 104 | rotate: true 105 | xy: 617, 2 106 | size: 396, 297 107 | orig: 396, 297 108 | offset: 0, 0 109 | index: -1 110 | groinal 111 | rotate: true 112 | xy: 1515, 296 113 | size: 103, 84 114 | orig: 103, 84 115 | offset: 0, 0 116 | index: -1 117 | head 118 | rotate: false 119 | xy: 2, 2 120 | size: 613, 408 121 | orig: 613, 408 122 | offset: 0, 0 123 | index: -1 124 | jaw 125 | rotate: true 126 | xy: 1273, 2 127 | size: 222, 124 128 | orig: 222, 124 129 | offset: 0, 0 130 | index: -1 131 | midHair 132 | rotate: true 133 | xy: 916, 2 134 | size: 351, 178 135 | orig: 351, 178 136 | offset: 0, 0 137 | index: -1 138 | neck 139 | rotate: true 140 | xy: 1118, 345 141 | size: 65, 81 142 | orig: 65, 81 143 | offset: 0, 0 144 | index: -1 145 | rearWing 146 | rotate: false 147 | xy: 1477, 148 148 | size: 136, 146 149 | orig: 136, 146 150 | offset: 0, 0 151 | index: -1 152 | vest 153 | rotate: false 154 | xy: 1477, 2 155 | size: 139, 144 156 | orig: 139, 144 157 | offset: 0, 0 158 | index: -1 159 | -------------------------------------------------------------------------------- /example/game/pixijs/assets/Pixie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/pixijs/assets/Pixie.png -------------------------------------------------------------------------------- /example/game/pixijs/assets/iP4_BGtile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/pixijs/assets/iP4_BGtile.jpg -------------------------------------------------------------------------------- /example/game/pixijs/assets/iP4_ground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/pixijs/assets/iP4_ground.png -------------------------------------------------------------------------------- /example/game/pixijs/image/gold_anim.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "gold_1.png": 4 | { 5 | "frame": {"x":1,"y":1,"w":84,"h":84}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":84,"h":84}, 9 | "sourceSize": {"w":84,"h":84}, 10 | "pivot": {"x":0.5,"y":0.5} 11 | }, 12 | "gold_2.png": 13 | { 14 | "frame": {"x":1,"y":86,"w":66,"h":84}, 15 | "rotated": false, 16 | "trimmed": true, 17 | "spriteSourceSize": {"x":9,"y":0,"w":66,"h":84}, 18 | "sourceSize": {"w":84,"h":84}, 19 | "pivot": {"x":0.5,"y":0.5} 20 | }, 21 | "gold_3.png": 22 | { 23 | "frame": {"x":68,"y":86,"w":50,"h":84}, 24 | "rotated": false, 25 | "trimmed": true, 26 | "spriteSourceSize": {"x":17,"y":0,"w":50,"h":84}, 27 | "sourceSize": {"w":84,"h":84}, 28 | "pivot": {"x":0.5,"y":0.5} 29 | }, 30 | "gold_4.png": 31 | { 32 | "frame": {"x":86,"y":1,"w":15,"h":84}, 33 | "rotated": false, 34 | "trimmed": true, 35 | "spriteSourceSize": {"x":34,"y":0,"w":15,"h":84}, 36 | "sourceSize": {"w":84,"h":84}, 37 | "pivot": {"x":0.5,"y":0.5} 38 | }, 39 | "gold_5.png": 40 | { 41 | "frame": {"x":68,"y":171,"w":50,"h":84}, 42 | "rotated": false, 43 | "trimmed": true, 44 | "spriteSourceSize": {"x":17,"y":0,"w":50,"h":84}, 45 | "sourceSize": {"w":84,"h":84}, 46 | "pivot": {"x":0.5,"y":0.5} 47 | }, 48 | "gold_6.png": 49 | { 50 | "frame": {"x":1,"y":171,"w":66,"h":84}, 51 | "rotated": false, 52 | "trimmed": true, 53 | "spriteSourceSize": {"x":9,"y":0,"w":66,"h":84}, 54 | "sourceSize": {"w":84,"h":84}, 55 | "pivot": {"x":0.5,"y":0.5} 56 | }}, 57 | "meta": { 58 | "app": "http://www.codeandweb.com/texturepacker", 59 | "version": "1.0", 60 | "image": "gold_anim.png", 61 | "format": "RGBA8888", 62 | "size": {"w":128,"h":256}, 63 | "scale": "1", 64 | "smartupdate": "$TexturePacker:SmartUpdate:da6353dcec564c38e81610d1de48d2a3:7fa5a79bbb30632cbe73f0fd8029dfc6:0772c5e092b65556291c493f15308808$" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example/game/pixijs/image/gold_anim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/pixijs/image/gold_anim.png -------------------------------------------------------------------------------- /example/game/pixijs/image/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/pixijs/image/rain.png -------------------------------------------------------------------------------- /example/game/pixijs/image/smoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/pixijs/image/smoke.png -------------------------------------------------------------------------------- /example/game/quarkjs/images/body_walk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/quarkjs/images/body_walk.png -------------------------------------------------------------------------------- /example/game/quarkjs/images/head_idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/quarkjs/images/head_idle.png -------------------------------------------------------------------------------- /example/game/quarkjs/images/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/game/quarkjs/images/rain.png -------------------------------------------------------------------------------- /example/game/quarkjs/js/Rain.js: -------------------------------------------------------------------------------- 1 | var Rain = function(props) { 2 | Rain.superClass.constructor.call(this, props); 3 | this.init(); 4 | }; 5 | 6 | Q.inherit(Rain, Q.DisplayObjectContainer); 7 | Rain.prototype.init = function() { 8 | var bmp1 = new Quark.Bitmap({ 9 | image : Q.getDOM("rain"), 10 | 11 | }); 12 | bmp1.rotation = 0; 13 | bmp1.x = 0; 14 | bmp1.y = 0; 15 | bmp1.enabled = true; 16 | this.addChild(bmp1); 17 | }; 18 | -------------------------------------------------------------------------------- /example/game/quarkjs/js/Squirrel.js: -------------------------------------------------------------------------------- 1 | var Squirrel = function(props) { 2 | Squirrel.superClass.constructor.call(this, props); 3 | this.init(); 4 | }; 5 | Q.inherit(Squirrel, Q.DisplayObjectContainer); 6 | 7 | Squirrel.prototype.init = function() { 8 | //松鼠的头部,是一个MovieClip类型。 9 | this.head = new Q.MovieClip({ 10 | id : "head", 11 | image : Q.getDOM("headIdle"), 12 | useFrames : true, 13 | interval : 2, 14 | x : 5, 15 | y : 0 16 | }); 17 | this.head.addFrame([{ 18 | rect : [0, 0, 66, 56] 19 | }, { 20 | rect : [69, 0, 66, 56] 21 | }, { 22 | rect : [138, 0, 66, 56] 23 | }, { 24 | rect : [207, 0, 66, 56] 25 | }]); 26 | 27 | //松鼠的身体,也是一个MovieClip类型。 28 | this.body = new Q.MovieClip({ 29 | id : "body", 30 | image : Q.getDOM('bodyWalk'), 31 | useFrames : true, 32 | interval : 2, 33 | x : 0, 34 | y : 25 35 | }); 36 | this.body.addFrame([{ 37 | rect : [0, 0, 108, 66] 38 | }, { 39 | rect : [109, 0, 108, 66] 40 | }, { 41 | rect : [218, 0, 108, 66] 42 | }, { 43 | rect : [327, 0, 108, 66] 44 | }, { 45 | rect : [436, 0, 108, 66] 46 | }, { 47 | rect : [545, 0, 108, 66] 48 | }, { 49 | rect : [0, 70, 108, 66] 50 | }, { 51 | rect : [109, 70, 108, 66] 52 | }, { 53 | rect : [218, 70, 108, 66] 54 | }, { 55 | rect : [327, 70, 108, 66] 56 | }, { 57 | rect : [436, 70, 108, 66] 58 | }]); 59 | 60 | //由头部和身体组成了一只松鼠。 61 | this.addChild(this.body, this.head); 62 | 63 | //初始化数据。 64 | this.eventChildren = false; 65 | this.jumping = false; 66 | this.speedY = this.currentSpeedY = 8; 67 | this.originY = this.y; 68 | }; 69 | 70 | //控制松鼠的跳跃 71 | Squirrel.prototype.jump = function(e) { 72 | if (!this.jumping) { 73 | this.jumping = true; 74 | this.currentSpeedY = this.speedY; 75 | } 76 | }; 77 | 78 | //松鼠的更新函数,此方法会不断的被quark系统调用而产生跳跃动画。 79 | Squirrel.prototype.update = function() { 80 | if (this.jumping) { 81 | this.currentSpeedY -= 0.3; 82 | this.y -= this.currentSpeedY; 83 | if (this.originY <= this.y) { 84 | this.y = this.originY; 85 | this.jumping = false; 86 | } 87 | } 88 | }; -------------------------------------------------------------------------------- /example/helloworld/emitter/emitter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | proton.js-emitter-Emitter 5 | 10 | 11 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /example/initialize/imagetarget/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/initialize/imagetarget/image/particle.png -------------------------------------------------------------------------------- /example/initialize/imagetarget/image/pt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/initialize/imagetarget/image/pt.jpg -------------------------------------------------------------------------------- /example/initialize/imagetarget/image/rock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/initialize/imagetarget/image/rock.png -------------------------------------------------------------------------------- /example/initialize/imagetarget/imagetarget.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | proton.js-emitter-ImageTarget 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /example/lib/color.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/color.js/blob/master/color.js*/ 2 | "use strict";var Color=(function(){var g="string",f=function f(n,m,h,i){var j=this,l=arguments.length,k=function(p){return parseInt(p,16)};if(l<3){if(typeof n===g){n=n.substr(n.indexOf("#")+1);var o=n.length===3;n=k(n);o&&(n=(((n&3840)*4352)|((n&240)*272)|((n&15)*17)))}l===2&&(i=m);m=(n&65280)/256;h=n&255;n=n>>>16}if(!(j instanceof f)){return new f(n,m,h,i)}this.channels=[typeof n===g&&k(n)||n,typeof m===g&&k(m)||m,typeof h===g&&k(h)||h,(typeof i!==g&&typeof i!=="number")&&1||typeof i===g&&parseFloat(i)||i]},d=f.prototype,b="undefined",a="toLowerCase",c=Math,e;f.RGBtoHSL=function(p){var i=p[0],n=p[1],q=p[2];i/=255;n/=255;q/=255;var t=c.max(i,n,q),k=c.min(i,n,q),m,u,j=(t+k)/2;if(t===k){m=u=0}else{var o=t-k;u=j>0.5?o/(2-t-k):o/(t+k);switch(t){case i:m=(n-q)/o+(n1){h-=1}if(h<1/6){return r+(l-r)*6*h}if(h<1/2){return l}if(h<2/3){return r+(l-r)*(2/3-h)*6}return r};if(w===0){i=t=u=n}else{var j=n<0.5?n*(1+w):n+w-n*w,k=2*n-j;i=m(k,j,o+1/3);t=m(k,j,o);u=m(k,j,o-1/3)}return[i*255,t*255,u*255]};f.rgb=function(k,j,h,i){return new f(k,j,h,typeof i!==b?i:1)};f.hsl=function(o,n,j,i){var k=f.HSLtoRGB([o,n,j]),m=c.ceil;return new f(m(k[0]),m(k[1]),m(k[2]),typeof i!==b?i:1)};f.TO_STRING_METHOD="hexTriplet";f.parse=function(h){h=h.replace(/^\s+/g,"")[a]();if(h[0]==="#"){return new f(h)}var k=h.substr(0,3),j;h=h.replace(/[^\d,.]/g,"").split(",");j=h.length;while(j--){h[j]=h[j]&&parseFloat(h[j])||0}switch(k){case"rgb":return f.rgb.apply(f,h);case"hsl":h[0]/=360;h[1]/=100;h[2]/=100;return f.hsl.apply(f,h)}return null};(f.clearColors=function(){e={transparent:[0,0,0,0]}})();f.define=function(h,i){e[h[a]()]=i};f.get=function(h){h=h[a]();if(Object.prototype.hasOwnProperty.call(e,h)){return f.apply(null,[].concat(e[h]))}return null};f.del=function(h){return delete e[h[a]()]};f.random=function(k,j){typeof k===g&&(k=f.get(k))&&(k=k.getValue());typeof j===g&&(j=f.get(j))&&(j=j.getValue());var i=c.floor,h=c.random;j=(j||16777215)+1;if(!isNaN(k)){return new f(i((h()*(j-k))+k))}return new f(i(h()*j))};d.toString=function(){return this[f.TO_STRING_METHOD]()};d.valueOf=d.getValue=function(){var h=this.channels;return((h[0]*65536)|(h[1]*256)|h[2])};d.setValue=function(h){this.channels.splice(0,3,h>>>16,(h&65280)/256,h&255)};d.hexTriplet=("01".substr(-1)==="1"?function(){return"#"+("00000"+this.getValue().toString(16)).substr(-6)}:function(){var h=this.getValue().toString(16);return"#"+(new Array(h.length<6?6-h.length+1:0)).join("0")+h});d.css=function(){var h=this;return h.channels[3]===1?h.hexTriplet():h.rgba()};d.rgbData=function(){return this.channels.slice(0,3)};d.hslData=function(){return f.RGBtoHSL(this.rgbData())};d.rgb=function(){return"rgb("+this.rgbData().join(",")+")"};d.rgba=function(){return"rgba("+this.channels.join(",")+")"};d.hsl=function(){var h=this.hslData();return"hsl("+h[0]*360+","+(h[1]*100)+"%,"+(h[2]*100)+"%)"};d.hsla=function(){var h=this.hslData();return"hsla("+h[0]*360+","+(h[1]*100)+"%,"+(h[2]*100)+"%,"+this.channels[3]+")"};return f}()); 3 | -------------------------------------------------------------------------------- /example/lib/requestAnimationFrame.min.js: -------------------------------------------------------------------------------- 1 | !function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;cc.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); 4 | k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= 5 | "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= 6 | a+"px",m=b,r=0);return b},update:function(){l=this.end()}}}; 7 | -------------------------------------------------------------------------------- /example/render/custom/custom-renderer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | proton.js-render-custom 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /example/render/custom/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | popup 6 | 7 | 8 | 23 | 24 | 25 | 26 |
27 | 29 | 30 | -------------------------------------------------------------------------------- /example/render/dom/image/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/dom/image/8.png -------------------------------------------------------------------------------- /example/render/dom/image/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/dom/image/chrome.png -------------------------------------------------------------------------------- /example/render/dom/image/fox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/dom/image/fox.png -------------------------------------------------------------------------------- /example/render/dom/image/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/dom/image/safari.png -------------------------------------------------------------------------------- /example/render/dom/jpgmove.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | proton.js-render-domrender 6 | 7 | 8 | 26 | 27 | 28 | 29 |
30 | 31 |
32 | 33 | 34 | 35 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /example/render/easeljs/image/daisy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/easeljs/image/daisy.png -------------------------------------------------------------------------------- /example/render/pixel/google/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixel/google/logo1.png -------------------------------------------------------------------------------- /example/render/pixel/google/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixel/google/logo2.png -------------------------------------------------------------------------------- /example/render/pixel/image/glaxy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixel/image/glaxy.jpg -------------------------------------------------------------------------------- /example/render/pixel/image/logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixel/image/logo1.png -------------------------------------------------------------------------------- /example/render/pixel/image/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixel/image/logo2.png -------------------------------------------------------------------------------- /example/render/pixel/image/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixel/image/logo3.png -------------------------------------------------------------------------------- /example/render/pixel/js/PxLoaderImage.js: -------------------------------------------------------------------------------- 1 | // @depends PxLoader.js 2 | /** 3 | * PxLoader plugin to load images 4 | */ 5 | 6 | function PxLoaderImage(url, tags, priority) { 7 | var self = this, 8 | loader = null; 9 | 10 | this.img = new Image(); 11 | this.tags = tags; 12 | this.priority = priority; 13 | 14 | var onReadyStateChange = function() { 15 | if (self.img.readyState == 'complete') { 16 | removeEventHandlers(); 17 | loader.onLoad(self); 18 | } 19 | }; 20 | 21 | var onLoad = function() { 22 | removeEventHandlers(); 23 | loader.onLoad(self); 24 | }; 25 | 26 | var onError = function() { 27 | removeEventHandlers(); 28 | loader.onError(self); 29 | }; 30 | 31 | var removeEventHandlers = function() { 32 | self.unbind('load', onLoad); 33 | self.unbind('readystatechange', onReadyStateChange); 34 | self.unbind('error', onError); 35 | }; 36 | 37 | this.start = function(pxLoader) { 38 | // we need the loader ref so we can notify upon completion 39 | loader = pxLoader; 40 | 41 | // NOTE: Must add event listeners before the src is set. We 42 | // also need to use the readystatechange because sometimes 43 | // load doesn't fire when an image is in the cache. 44 | self.bind('load', onLoad); 45 | self.bind('readystatechange', onReadyStateChange); 46 | self.bind('error', onError); 47 | 48 | self.img.src = url; 49 | }; 50 | 51 | // called by PxLoader to check status of image (fallback in case 52 | // the event listeners are not triggered). 53 | this.checkStatus = function() { 54 | if (self.img.complete) { 55 | removeEventHandlers(); 56 | loader.onLoad(self); 57 | } 58 | }; 59 | 60 | // called by PxLoader when it is no longer waiting 61 | this.onTimeout = function() { 62 | removeEventHandlers(); 63 | if (self.img.complete) { 64 | loader.onLoad(self); 65 | } else { 66 | loader.onTimeout(self); 67 | } 68 | }; 69 | 70 | // returns a name for the resource that can be used in logging 71 | this.getName = function() { 72 | return url; 73 | }; 74 | 75 | // cross-browser event binding 76 | this.bind = function(eventName, eventHandler) { 77 | if (self.img.addEventListener) { 78 | self.img.addEventListener(eventName, eventHandler, false); 79 | } else if (self.img.attachEvent) { 80 | self.img.attachEvent('on' + eventName, eventHandler); 81 | } 82 | }; 83 | 84 | // cross-browser event un-binding 85 | this.unbind = function(eventName, eventHandler) { 86 | if (self.img.removeEventListener) { 87 | self.img.removeEventListener(eventName, eventHandler, false); 88 | } else if (self.img.detachEvent) { 89 | self.img.detachEvent('on' + eventName, eventHandler); 90 | } 91 | }; 92 | 93 | } 94 | 95 | // add a convenience method to PxLoader for adding an image 96 | PxLoader.prototype.addImage = function(url, tags, priority) { 97 | var imageLoader = new PxLoaderImage(url, tags, priority); 98 | this.add(imageLoader); 99 | 100 | // return the img element to the caller 101 | return imageLoader.img; 102 | }; -------------------------------------------------------------------------------- /example/render/pixel/js/PxLoaderSound.js: -------------------------------------------------------------------------------- 1 | // @depends PxLoader.js 2 | /** 3 | * PxLoader plugin to load sound using SoundManager2 4 | */ 5 | 6 | function PxLoaderSound(id, url, tags, priority) { 7 | var self = this, 8 | loader = null; 9 | 10 | this.tags = tags; 11 | this.priority = priority; 12 | this.sound = soundManager['createSound']({ 13 | 'id': id, 14 | 'url': url, 15 | 'autoLoad': false, 16 | 'onload': function() { 17 | loader.onLoad(self); 18 | }, 19 | 20 | // HTML5-only event: Fires when a browser has chosen to stop downloading. 21 | // "The user agent is intentionally not currently fetching media data, 22 | // but does not have the entire media resource downloaded." 23 | 'onsuspend': function() { 24 | loader.onTimeout(self); 25 | }, 26 | 27 | // Fires at a regular interval when a sound is loading and new data 28 | // has been received. 29 | 'whileloading': function() { 30 | var bytesLoaded = this['bytesLoaded'], 31 | bytesTotal = this['bytesTotal']; 32 | 33 | // TODO: provide percentage complete updates to loader? 34 | // see if we have loaded the file 35 | if (bytesLoaded > 0 && (bytesLoaded === bytesTotal)) { 36 | loader.onLoad(self); 37 | } 38 | } 39 | }); 40 | 41 | this.start = function(pxLoader) { 42 | // we need the loader ref so we can notify upon completion 43 | loader = pxLoader; 44 | 45 | // On iOS, soundManager2 uses a global audio object so we can't 46 | // preload multiple sounds. We'll have to hope they load quickly 47 | // when we need to play them. Unfortunately, SM2 doesn't expose 48 | // a property to indicate its using a global object. For now we'll 49 | // use the same test they do: only when on an iDevice 50 | var iDevice = navigator.userAgent.match(/(ipad|iphone|ipod)/i); 51 | if (iDevice) { 52 | loader.onTimeout(self); 53 | } else { 54 | this.sound['load'](); 55 | } 56 | }; 57 | 58 | this.checkStatus = function() { 59 | switch(self.sound['readyState']) { 60 | case 0: 61 | // uninitialised 62 | break; 63 | case 1: 64 | // loading 65 | break; 66 | case 2: 67 | // failed/error 68 | loader.onError(self); 69 | break; 70 | case 3: 71 | // loaded/success 72 | loader.onLoad(self); 73 | break; 74 | } 75 | }; 76 | 77 | this.onTimeout = function() { 78 | loader.onTimeout(self); 79 | }; 80 | 81 | this.getName = function() { 82 | return url; 83 | } 84 | } 85 | 86 | // add a convenience method to PxLoader for adding a sound 87 | PxLoader.prototype.addSound = function(id, url, tags, priority) { 88 | var soundLoader = new PxLoaderSound(id, url, tags, priority); 89 | this.add(soundLoader); 90 | return soundLoader.sound; 91 | }; -------------------------------------------------------------------------------- /example/render/pixi/image/bunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixi/image/bunny.png -------------------------------------------------------------------------------- /example/render/pixi/image/eggHead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixi/image/eggHead.png -------------------------------------------------------------------------------- /example/render/pixi/image/flowerTop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixi/image/flowerTop.png -------------------------------------------------------------------------------- /example/render/pixi/image/helmlok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixi/image/helmlok.png -------------------------------------------------------------------------------- /example/render/pixi/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/pixi/image/particle.png -------------------------------------------------------------------------------- /example/render/webgl/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/render/webgl/image/particle.png -------------------------------------------------------------------------------- /example/sparks/bigfire/bigfire.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sparks-bigfire 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /example/sparks/bigfire/image/fire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/sparks/bigfire/image/fire.png -------------------------------------------------------------------------------- /example/sparks/bomb/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/sparks/bomb/image/particle.png -------------------------------------------------------------------------------- /example/sparks/drugs/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/sparks/drugs/image/particle.png -------------------------------------------------------------------------------- /example/sparks/eightDiagrams/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/sparks/eightDiagrams/image/particle.png -------------------------------------------------------------------------------- /example/sparks/firework/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/sparks/firework/bg.jpg -------------------------------------------------------------------------------- /example/sparks/sun/fireball.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sparks-fireball 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /example/sparks/sun/image/particle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/sparks/sun/image/particle.png -------------------------------------------------------------------------------- /example/zone/circlezone/image/n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/zone/circlezone/image/n.png -------------------------------------------------------------------------------- /example/zone/circlezone/image/o.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/zone/circlezone/image/o.png -------------------------------------------------------------------------------- /example/zone/circlezone/image/p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/zone/circlezone/image/p.png -------------------------------------------------------------------------------- /example/zone/circlezone/image/r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/zone/circlezone/image/r.png -------------------------------------------------------------------------------- /example/zone/circlezone/image/t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/zone/circlezone/image/t.png -------------------------------------------------------------------------------- /example/zone/imagezone/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drawcall/Proton/3792204c4ccd204203aadf1afc9d88eb3abdbd5b/example/zone/imagezone/image/logo.png -------------------------------------------------------------------------------- /example/zone/pointzone/js/color.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/color.js/blob/master/color.js*/ 2 | "use strict";var Color=(function(){var g="string",f=function f(n,m,h,i){var j=this,l=arguments.length,k=function(p){return parseInt(p,16)};if(l<3){if(typeof n===g){n=n.substr(n.indexOf("#")+1);var o=n.length===3;n=k(n);o&&(n=(((n&3840)*4352)|((n&240)*272)|((n&15)*17)))}l===2&&(i=m);m=(n&65280)/256;h=n&255;n=n>>>16}if(!(j instanceof f)){return new f(n,m,h,i)}this.channels=[typeof n===g&&k(n)||n,typeof m===g&&k(m)||m,typeof h===g&&k(h)||h,(typeof i!==g&&typeof i!=="number")&&1||typeof i===g&&parseFloat(i)||i]},d=f.prototype,b="undefined",a="toLowerCase",c=Math,e;f.RGBtoHSL=function(p){var i=p[0],n=p[1],q=p[2];i/=255;n/=255;q/=255;var t=c.max(i,n,q),k=c.min(i,n,q),m,u,j=(t+k)/2;if(t===k){m=u=0}else{var o=t-k;u=j>0.5?o/(2-t-k):o/(t+k);switch(t){case i:m=(n-q)/o+(n1){h-=1}if(h<1/6){return r+(l-r)*6*h}if(h<1/2){return l}if(h<2/3){return r+(l-r)*(2/3-h)*6}return r};if(w===0){i=t=u=n}else{var j=n<0.5?n*(1+w):n+w-n*w,k=2*n-j;i=m(k,j,o+1/3);t=m(k,j,o);u=m(k,j,o-1/3)}return[i*255,t*255,u*255]};f.rgb=function(k,j,h,i){return new f(k,j,h,typeof i!==b?i:1)};f.hsl=function(o,n,j,i){var k=f.HSLtoRGB([o,n,j]),m=c.ceil;return new f(m(k[0]),m(k[1]),m(k[2]),typeof i!==b?i:1)};f.TO_STRING_METHOD="hexTriplet";f.parse=function(h){h=h.replace(/^\s+/g,"")[a]();if(h[0]==="#"){return new f(h)}var k=h.substr(0,3),j;h=h.replace(/[^\d,.]/g,"").split(",");j=h.length;while(j--){h[j]=h[j]&&parseFloat(h[j])||0}switch(k){case"rgb":return f.rgb.apply(f,h);case"hsl":h[0]/=360;h[1]/=100;h[2]/=100;return f.hsl.apply(f,h)}return null};(f.clearColors=function(){e={transparent:[0,0,0,0]}})();f.define=function(h,i){e[h[a]()]=i};f.get=function(h){h=h[a]();if(Object.prototype.hasOwnProperty.call(e,h)){return f.apply(null,[].concat(e[h]))}return null};f.del=function(h){return delete e[h[a]()]};f.random=function(k,j){typeof k===g&&(k=f.get(k))&&(k=k.getValue());typeof j===g&&(j=f.get(j))&&(j=j.getValue());var i=c.floor,h=c.random;j=(j||16777215)+1;if(!isNaN(k)){return new f(i((h()*(j-k))+k))}return new f(i(h()*j))};d.toString=function(){return this[f.TO_STRING_METHOD]()};d.valueOf=d.getValue=function(){var h=this.channels;return((h[0]*65536)|(h[1]*256)|h[2])};d.setValue=function(h){this.channels.splice(0,3,h>>>16,(h&65280)/256,h&255)};d.hexTriplet=("01".substr(-1)==="1"?function(){return"#"+("00000"+this.getValue().toString(16)).substr(-6)}:function(){var h=this.getValue().toString(16);return"#"+(new Array(h.length<6?6-h.length+1:0)).join("0")+h});d.css=function(){var h=this;return h.channels[3]===1?h.hexTriplet():h.rgba()};d.rgbData=function(){return this.channels.slice(0,3)};d.hslData=function(){return f.RGBtoHSL(this.rgbData())};d.rgb=function(){return"rgb("+this.rgbData().join(",")+")"};d.rgba=function(){return"rgba("+this.channels.join(",")+")"};d.hsl=function(){var h=this.hslData();return"hsl("+h[0]*360+","+(h[1]*100)+"%,"+(h[2]*100)+"%)"};d.hsla=function(){var h=this.hslData();return"hsla("+h[0]*360+","+(h[1]*100)+"%,"+(h[2]*100)+"%,"+this.channels[3]+")"};return f}()); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proton-engine", 3 | "version": "7.1.5", 4 | "description": "Proton is a simple and powerful javascript particle animation engine.", 5 | "keywords": [ 6 | "particle", 7 | "particles", 8 | "canvas", 9 | "canvas-particle", 10 | "webgl" 11 | ], 12 | "main": "./build/proton.min.js", 13 | "types": "./build/proton.d.ts", 14 | "scripts": { 15 | "start": "concurrently --names \"ROLLUP,HTTP\" -c \"bgBlue.bold,bgGreen.bold\" \"NODE_ENV=dev rollup -c --bundleConfigAsCjs -w -m inline\" \"serve --listen 3001\"", 16 | "page": "node ./script/makeexamplepage", 17 | "lint": "eslint ./src --config=eslintrc.json", 18 | "travis": "npm run lint", 19 | "build": "NODE_ENV=pub rollup -c --bundleConfigAsCjs" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/drawcall/Proton.git" 24 | }, 25 | "authors": [ 26 | "drawcall " 27 | ], 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/drawcall/Proton/issues" 31 | }, 32 | "homepage": "http://drawcall.github.io/Proton", 33 | "devDependencies": { 34 | "@babel/core": "^7.15.5", 35 | "@babel/eslint-parser": "^7.15.4", 36 | "@babel/plugin-proposal-class-properties": "^7.14.5", 37 | "@babel/preset-env": "^7.15.6", 38 | "@rollup/plugin-babel": "^6.0.3", 39 | "@rollup/plugin-typescript": "^11.1.2", 40 | "@rollup/plugin-terser": "^0.4.3", 41 | "concurrently": "^3.5.1", 42 | "eslint": "^7.32.0", 43 | "eslint-plugin-import": "^2.24.2", 44 | "eslint-plugin-promise": "^5.1.0", 45 | "eslint-plugin-standard": "^5.0.0", 46 | "rollup": "^3.29.4", 47 | "rollup-plugin-dts": "^5.3.1", 48 | "rollup-plugin-license": "^3.0.1", 49 | "serve": "^14.1.2", 50 | "tslib": "^2.8.1", 51 | "typescript": "^5.7.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from "@rollup/plugin-babel"; 2 | import terser from "@rollup/plugin-terser"; 3 | import license from "rollup-plugin-license"; 4 | import typescript from "@rollup/plugin-typescript"; 5 | import { dts } from "rollup-plugin-dts"; 6 | import pkjson from "./package.json"; 7 | import babelrc from "./.babelrc.json"; 8 | 9 | const INPUT_FILE = "src/index.js"; 10 | const IS_DEV = process.env.NODE_ENV === "dev"; 11 | const CURRENT_YEAR = new Date().getFullYear(); 12 | 13 | const createBanner = () => `/*! 14 | * Proton v${pkjson.version} 15 | * https://github.com/drawcall/Proton 16 | * 17 | * Copyright 2013-${CURRENT_YEAR}, drawcall 18 | * Licensed under the MIT license 19 | * http://www.opensource.org/licenses/mit-license 20 | * 21 | */`; 22 | 23 | const removeExportsPlugin = { 24 | name: "remove-exports", 25 | transform(code, id) { 26 | if (id.endsWith(INPUT_FILE)) { 27 | console.log("remove-exports: Removing exports from", id); 28 | return code.replace(/export\s*{[\s\S]*?};/g, ""); 29 | } 30 | return null; 31 | }, 32 | }; 33 | 34 | const createBabelPlugin = () => 35 | babel({ 36 | exclude: "node_modules/**", 37 | babelHelpers: "bundled", 38 | babelrc: false, 39 | ...babelrc, 40 | }); 41 | 42 | const createConfig = (outputFile, isWeb, plugins) => ({ 43 | input: INPUT_FILE, 44 | output: { 45 | file: outputFile, 46 | format: isWeb ? "iife" : "umd", 47 | name: "Proton", 48 | exports: isWeb ? "auto" : "named", 49 | sourcemap: true, 50 | ...(isWeb ? { extend: true } : {}), 51 | }, 52 | plugins, 53 | }); 54 | 55 | const devConfigs = [ 56 | createConfig("build/proton.js", false, [createBabelPlugin()]), 57 | createConfig("build/proton.web.js", true, [removeExportsPlugin, createBabelPlugin()]), 58 | ]; 59 | 60 | const prodConfigs = [ 61 | createConfig("build/proton.min.js", false, [ 62 | createBabelPlugin(), 63 | typescript({ tsconfig: "./tsconfig.json" }), 64 | terser(), 65 | license({ banner: createBanner() }), 66 | ]), 67 | createConfig("build/proton.web.min.js", true, [ 68 | removeExportsPlugin, 69 | createBabelPlugin(), 70 | typescript({ tsconfig: "./tsconfig.json" }), 71 | terser(), 72 | license({ banner: createBanner() }), 73 | ]), 74 | ]; 75 | 76 | const dtsConfig = { 77 | input: INPUT_FILE, 78 | output: [{ file: "build/proton.d.ts", format: "es" }], 79 | plugins: [dts({ respectExternal: true })], 80 | }; 81 | 82 | export default [...(IS_DEV ? devConfigs : prodConfigs), dtsConfig]; 83 | -------------------------------------------------------------------------------- /script/makeexamplepage.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const src = "./example"; 5 | const filesArr = []; 6 | 7 | // write content to html 8 | function writePage() { 9 | readFiles(src); 10 | 11 | const page = src + "/index.html"; 12 | const content = getContent(filesArr); 13 | const data = template.replace("<% code %>", content); 14 | 15 | fs.writeFile(page, data, (err) => { 16 | if (err) throw err; 17 | console.log("It's saved!"); 18 | }); 19 | } 20 | 21 | function getContent(filesArr) { 22 | let content = ``; 23 | filesArr.forEach((val) => { 24 | content += ` 27 | `; 28 | }); 29 | 30 | return content; 31 | } 32 | 33 | // read all html file 34 | function readFiles(src, parent) { 35 | const files = fs.readdirSync(src); 36 | 37 | files.forEach(function (filename) { 38 | const filepath = path.join(src, filename); 39 | const stats = fs.statSync(filepath); 40 | 41 | if (stats.isFile()) { 42 | if (/\.html$/.test(filename) && parent) { 43 | filesArr.push({ 44 | filename: filename, 45 | path: filepath.replace(/^example/gi, "."), 46 | parent: parent, 47 | }); 48 | } 49 | } else if (stats.isDirectory()) { 50 | readFiles(filepath, filename); 51 | } 52 | }); 53 | } 54 | 55 | const template = ` 56 | 57 | 58 | 59 | proton.js / examples 60 | 61 | 62 | 65 | 66 | 67 |

Proton / example

68 |
69 | <% code %> 70 |
71 | 72 | 73 | `; 74 | 75 | writePage(); 76 | -------------------------------------------------------------------------------- /src/behaviour/Alpha.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import Span from "../math/Span"; 3 | import Behaviour from "./Behaviour"; 4 | 5 | /** 6 | * Alpha behaviour for controlling particle opacity over time. 7 | * @extends Behaviour 8 | */ 9 | export default class Alpha extends Behaviour { 10 | /** 11 | * @type {boolean} 12 | * @private 13 | */ 14 | same; 15 | 16 | /** 17 | * @type {Span} 18 | * @private 19 | */ 20 | a; 21 | 22 | /** 23 | * @type {Span} 24 | * @private 25 | */ 26 | b; 27 | 28 | /** 29 | * @type {string} 30 | */ 31 | name; 32 | 33 | /** 34 | * Creates a new Alpha instance. 35 | * @param {number|Span} [a=1] - The initial alpha value or range. 36 | * @param {number|Span} [b] - The final alpha value or range. If not provided, it will be the same as 'a'. 37 | * @param {number} [life=Infinity] - This behaviour's life. 38 | * @param {string} [easing='easeLinear'] - This behaviour's easing function. 39 | */ 40 | constructor(a, b, life, easing) { 41 | super(life, easing); 42 | 43 | this.reset(a, b); 44 | this.name = "Alpha"; 45 | } 46 | 47 | /** 48 | * Resets this behaviour's parameters. 49 | * @param {number|Span} [a=1] - The initial alpha value or range. 50 | * @param {number|Span} [b] - The final alpha value or range. If not provided, it will be the same as 'a'. 51 | * @param {number} [life] - This behaviour's life. 52 | * @param {string} [easing] - This behaviour's easing function. 53 | */ 54 | reset(a, b, life, easing) { 55 | this.same = b === null || b === undefined; 56 | this.a = Span.setSpanValue(Util.initValue(a, 1)); 57 | this.b = Span.setSpanValue(b); 58 | 59 | life && super.reset(life, easing); 60 | } 61 | 62 | /** 63 | * Initializes the particle's alpha values. 64 | * @param {Particle} particle - The particle to initialize. 65 | */ 66 | initialize(particle) { 67 | particle.data.alphaA = this.a.getValue(); 68 | 69 | if (this.same) particle.data.alphaB = particle.data.alphaA; 70 | else particle.data.alphaB = this.b.getValue(); 71 | } 72 | 73 | /** 74 | * Applies the alpha behaviour to the particle. 75 | * @param {Particle} particle - The particle to apply the behaviour to. 76 | * @param {number} time - The current simulation time. 77 | * @param {number} index - The index of the particle. 78 | */ 79 | applyBehaviour(particle, time, index) { 80 | this.calculate(particle, time, index); 81 | 82 | particle.alpha = particle.data.alphaB + (particle.data.alphaA - particle.data.alphaB) * this.energy; 83 | 84 | if (particle.alpha < 0.001) particle.alpha = 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/behaviour/Attraction.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import Vector2D from "../math/Vector2D"; 3 | import Behaviour from "./Behaviour"; 4 | 5 | /** 6 | * Attraction behavior for particles. 7 | * This behaviour makes particles follow a specific target position. 8 | * @extends Behaviour 9 | */ 10 | export default class Attraction extends Behaviour { 11 | /** 12 | * Creates an instance of Attraction. 13 | * @param {Vector2D} targetPosition - The attraction point coordinates. 14 | * @param {number} [force=100] - The strength of the attraction force. 15 | * @param {number} [radius=1000] - The radius of influence for the attraction. 16 | * @param {number} [life=Infinity] - The life span of this behaviour. 17 | * @param {string} [easing='ease.easeLinear'] - The easing function for this behaviour. 18 | */ 19 | constructor(targetPosition, force, radius, life, easing) { 20 | super(life, easing); 21 | 22 | /** 23 | * The target position for attraction. 24 | * @type {Vector2D} 25 | */ 26 | this.targetPosition = Util.initValue(targetPosition, new Vector2D()); 27 | 28 | /** 29 | * The radius of influence for the attraction. 30 | * @type {number} 31 | */ 32 | this.radius = Util.initValue(radius, 1000); 33 | 34 | /** 35 | * The strength of the attraction force. 36 | * @type {number} 37 | */ 38 | this.force = Util.initValue(this.normalizeValue(force), 100); 39 | 40 | /** 41 | * The squared radius (for optimization). 42 | * @type {number} 43 | */ 44 | this.radiusSq = this.radius * this.radius; 45 | 46 | /** 47 | * The attraction force vector. 48 | * @type {Vector2D} 49 | */ 50 | this.attractionForce = new Vector2D(); 51 | 52 | /** 53 | * The squared length of the attraction force. 54 | * @type {number} 55 | */ 56 | this.lengthSq = 0; 57 | 58 | /** 59 | * The name of the behaviour. 60 | * @type {string} 61 | */ 62 | this.name = "Attraction"; 63 | } 64 | 65 | /** 66 | * Resets the behaviour's parameters. 67 | * @param {Vector2D} targetPosition - The new attraction point coordinates. 68 | * @param {number} [force=100] - The new strength of the attraction force. 69 | * @param {number} [radius=1000] - The new radius of influence for the attraction. 70 | * @param {number} [life=Infinity] - The new life span of this behaviour. 71 | * @param {string} [easing='ease.easeLinear'] - The new easing function for this behaviour. 72 | */ 73 | reset(targetPosition, force, radius, life, easing) { 74 | this.targetPosition = Util.initValue(targetPosition, new Vector2D()); 75 | this.radius = Util.initValue(radius, 1000); 76 | this.force = Util.initValue(this.normalizeValue(force), 100); 77 | this.radiusSq = this.radius * this.radius; 78 | this.attractionForce = new Vector2D(); 79 | this.lengthSq = 0; 80 | 81 | life && super.reset(life, easing); 82 | } 83 | 84 | /** 85 | * Applies this behaviour to a particle. 86 | * @param {Particle} particle - The particle to apply the behaviour to. 87 | * @param {number} time - The current simulation time. 88 | * @param {number} index - The index of the particle. 89 | */ 90 | applyBehaviour(particle, time, index) { 91 | this.calculate(particle, time, index); 92 | 93 | this.attractionForce.copy(this.targetPosition); 94 | this.attractionForce.sub(particle.p); 95 | this.lengthSq = this.attractionForce.lengthSq(); 96 | 97 | if (this.lengthSq > 0.00004 && this.lengthSq < this.radiusSq) { 98 | this.attractionForce.normalize(); 99 | this.attractionForce.multiplyScalar(1 - this.lengthSq / this.radiusSq); 100 | this.attractionForce.multiplyScalar(this.force); 101 | 102 | particle.a.add(this.attractionForce); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/behaviour/Color.js: -------------------------------------------------------------------------------- 1 | import ColorUtil from "../utils/ColorUtil"; 2 | import ArraySpan from "../math/ArraySpan"; 3 | import Behaviour from "./Behaviour"; 4 | 5 | export default class Color extends Behaviour { 6 | /** 7 | * @memberof! Proton# 8 | * @augments Proton.Behaviour 9 | * @constructor 10 | * @alias Proton.Color 11 | * 12 | * @param {Proton.ArraySpan | String} [a] the string should be a hex e.g. #000000 for black 13 | * @param {Proton.ArraySpan | String} [b] the string should be a hex e.g. #000000 for black 14 | * @param {Number} [life=Infinity] this behaviour's life 15 | * @param {String} [easing=easeLinear] this behaviour's easing 16 | * 17 | * @property {String} name The Behaviour name 18 | */ 19 | constructor(a, b, life, easing) { 20 | super(life, easing); 21 | 22 | this.reset(a, b); 23 | this.name = "Color"; 24 | } 25 | 26 | /** 27 | * Reset this behaviour's parameters 28 | * 29 | * @method reset 30 | * @memberof Proton#Proton.Color 31 | * @instance 32 | * 33 | * @param {Proton.ArraySpan | String} a the string should be a hex e.g. #000000 for black 34 | * @param {Proton.ArraySpan | String} b the string should be a hex e.g. #000000 for black 35 | * @param {Number} [life=Infinity] this behaviour's life 36 | * @param {String} [easing=easeLinear] this behaviour's easing 37 | */ 38 | reset(a, b, life, easing) { 39 | this.a = ArraySpan.createArraySpan(a); 40 | this.b = ArraySpan.createArraySpan(b); 41 | life && super.reset(life, easing); 42 | } 43 | 44 | /** 45 | * Initialize the behaviour's parameters for all particles 46 | * 47 | * @method initialize 48 | * @memberof Proton#Proton.Color 49 | * @instance 50 | * 51 | * @param {Proton.Particle} particle 52 | */ 53 | initialize(particle) { 54 | particle.color = this.a.getValue(); 55 | particle.data.colorA = ColorUtil.hexToRgb(particle.color); 56 | 57 | if (this.b) particle.data.colorB = ColorUtil.hexToRgb(this.b.getValue()); 58 | } 59 | 60 | /** 61 | * Apply this behaviour for all particles every time 62 | * 63 | * @method applyBehaviour 64 | * @memberof Proton#Proton.Color 65 | * @instance 66 | * 67 | * @param {Proton.Particle} particle 68 | * @param {Number} the integrate time 1/ms 69 | * @param {Int} the particle index 70 | */ 71 | applyBehaviour(particle, time, index) { 72 | if (this.b) { 73 | this.calculate(particle, time, index); 74 | 75 | particle.rgb.r = particle.data.colorB.r + (particle.data.colorA.r - particle.data.colorB.r) * this.energy; 76 | particle.rgb.g = particle.data.colorB.g + (particle.data.colorA.g - particle.data.colorB.g) * this.energy; 77 | particle.rgb.b = particle.data.colorB.b + (particle.data.colorA.b - particle.data.colorB.b) * this.energy; 78 | 79 | particle.rgb.r = particle.rgb.r << 0; 80 | particle.rgb.g = particle.rgb.g << 0; 81 | particle.rgb.b = particle.rgb.b << 0; 82 | } else { 83 | particle.rgb.r = particle.data.colorA.r; 84 | particle.rgb.g = particle.data.colorA.g; 85 | particle.rgb.b = particle.data.colorA.b; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/behaviour/CrossZone.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import Behaviour from "./Behaviour"; 3 | 4 | export default class CrossZone extends Behaviour { 5 | /** 6 | * Defines what happens if the particles come to the end of the specified zone 7 | * 8 | * @memberof! Proton# 9 | * @augments Proton.Behaviour 10 | * @constructor 11 | * @alias Proton.CrossZone 12 | * 13 | * @param {Proton.Zone} zone can be any Proton.Zone - e.g. Proton.RectZone() 14 | * @param {String} [crossType=dead] what happens if the particles pass the zone - allowed strings: dead | bound | cross 15 | * @param {Number} [life=Infinity] this behaviour's life 16 | * @param {String} [easing=ease.easeLinear] this behaviour's easing 17 | * 18 | * @property {String} name The Behaviour name 19 | */ 20 | constructor(zone, crossType, life, easing) { 21 | super(life, easing); 22 | 23 | this.reset(zone, crossType); 24 | this.name = "CrossZone"; 25 | } 26 | 27 | /** 28 | * Reset this behaviour's parameters 29 | * 30 | * @method reset 31 | * @memberof Proton#Proton.CrossZone 32 | * @instance 33 | * 34 | * @param {Proton.Zone} zone can be any Proton.Zone - e.g. Proton.RectZone() 35 | * @param {String} [crossType=dead] what happens if the particles pass the zone - allowed strings: dead | bound | cross 36 | * @param {Number} [life=Infinity] this behaviour's life 37 | * @param {String} [easing=easeLinear] this behaviour's easing 38 | */ 39 | reset(zone, crossType, life, easing) { 40 | this.zone = zone; 41 | this.zone.crossType = Util.initValue(crossType, "dead"); 42 | 43 | life && super.reset(life, easing); 44 | } 45 | 46 | /** 47 | * Apply this behaviour for all particles every time 48 | * 49 | * @method applyBehaviour 50 | * @memberof Proton#Proton.CrossZone 51 | * @instance 52 | * 53 | * @param {Proton.Particle} particle 54 | * @param {Number} the integrate time 1/ms 55 | * @param {Int} the particle index 56 | */ 57 | applyBehaviour(particle, time, index) { 58 | this.calculate(particle, time, index); 59 | this.zone.crossing(particle); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/behaviour/Cyclone.js: -------------------------------------------------------------------------------- 1 | import MathUtil from "../math/MathUtil"; 2 | import Vector2D from "../math/Vector2D"; 3 | import Span from "../math/Span"; 4 | import Behaviour from "./Behaviour"; 5 | 6 | const CHANGING = "changing"; 7 | 8 | export default class Cyclone extends Behaviour { 9 | /** 10 | * @memberof! Proton# 11 | * @augments Proton.Behaviour 12 | * @constructor 13 | * @alias Proton.Cyclone 14 | * 15 | * @param {Number} angle 16 | * @param {Number} force 17 | * @param {Number} [life=Infinity] this behaviour's life 18 | * @param {String} [easing=ease.easeLinear] this behaviour's easing 19 | * 20 | * @property {String} name The Behaviour name 21 | */ 22 | constructor(angle, force, life, easing) { 23 | super(life, easing); 24 | this.setAngleAndForce(angle, force); 25 | this.name = "Cyclone"; 26 | } 27 | 28 | setAngleAndForce(angle, force) { 29 | this.force = CHANGING; 30 | this.angle = MathUtil.PI / 2; 31 | 32 | if (angle === "right") { 33 | this.angle = MathUtil.PI / 2; 34 | } else if (angle === "left") { 35 | this.angle = -MathUtil.PI / 2; 36 | } else if (angle === "random") { 37 | this.angle = "random"; 38 | } else if (angle instanceof Span) { 39 | this.angle = "span"; 40 | this.span = angle; 41 | } else if (angle) { 42 | this.angle = angle; 43 | } 44 | 45 | if ( 46 | String(force).toLowerCase() === "changing" || 47 | String(force).toLowerCase() === "chang" || 48 | String(force).toLowerCase() === "auto" 49 | ) { 50 | this.force = CHANGING; 51 | } else if (force) { 52 | this.force = force; 53 | } 54 | } 55 | 56 | /** 57 | * Reset this behaviour's parameters 58 | * 59 | * @method reset 60 | * @memberof Proton#Proton.Cyclone 61 | * @instance 62 | * 63 | * @param {Number} angle 64 | * @param {Number} force 65 | * @param {Number} [life=Infinity] this behaviour's life 66 | * @param {String} [easing=ease.easeLinear] this behaviour's easing 67 | */ 68 | reset(angle, force, life, easing) { 69 | this.angle = MathUtil.PI / 2; 70 | this.setAngleAndForce(angle, force); 71 | life && super.reset(life, easing); 72 | } 73 | 74 | initialize(particle) { 75 | if (this.angle === "random") { 76 | particle.data.cangle = MathUtil.randomAToB(-MathUtil.PI, MathUtil.PI); 77 | } else if (this.angle === "span") { 78 | particle.data.cangle = this.span.getValue(); 79 | } 80 | 81 | particle.data.cyclone = new Vector2D(0, 0); 82 | } 83 | 84 | /** 85 | * Apply this behaviour for all particles every time 86 | * 87 | * @method applyBehaviour 88 | * @memberof Proton#Proton.Cyclone 89 | * @instance 90 | * 91 | * @param {Proton.Particle} particle 92 | * @param {Number} the integrate time 1/ms 93 | * @param {Int} the particle index 94 | */ 95 | applyBehaviour(particle, time, index) { 96 | this.calculate(particle, time, index); 97 | 98 | let length; 99 | let gradient = particle.v.getGradient(); 100 | if (this.angle === "random" || this.angle === "span") { 101 | gradient += particle.data.cangle; 102 | } else { 103 | gradient += this.angle; 104 | } 105 | 106 | if (this.force === CHANGING) { 107 | length = particle.v.length() / 100; 108 | } else { 109 | length = this.force; 110 | } 111 | 112 | particle.data.cyclone.x = length * Math.cos(gradient); 113 | particle.data.cyclone.y = length * Math.sin(gradient); 114 | particle.data.cyclone = this.normalizeForce(particle.data.cyclone); 115 | particle.a.add(particle.data.cyclone); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/behaviour/Force.js: -------------------------------------------------------------------------------- 1 | import Vector2D from "../math/Vector2D"; 2 | import Behaviour from "./Behaviour"; 3 | 4 | export default class Force extends Behaviour { 5 | /** 6 | * @memberof! Proton# 7 | * @augments Proton.Behaviour 8 | * @constructor 9 | * @alias Proton.Force 10 | * 11 | * @param {Number} fx 12 | * @param {Number} fy 13 | * @param {Number} [life=Infinity] this behaviour's life 14 | * @param {String} [easing=ease.easeLinear] this behaviour's easing 15 | * 16 | * @property {String} name The Behaviour name 17 | */ 18 | constructor(fx, fy, life, easing) { 19 | super(life, easing); 20 | 21 | this.force = this.normalizeForce(new Vector2D(fx, fy)); 22 | this.name = "Force"; 23 | } 24 | 25 | /** 26 | * Reset this behaviour's parameters 27 | * 28 | * @method reset 29 | * @memberof Proton#Proton.Force 30 | * @instance 31 | * 32 | * @param {Number} fx 33 | * @param {Number} fy 34 | * @param {Number} [life=Infinity] this behaviour's life 35 | * @param {String} [easing=ease.easeLinear] this behaviour's easing 36 | */ 37 | reset(fx, fy, life, easing) { 38 | this.force = this.normalizeForce(new Vector2D(fx, fy)); 39 | 40 | life && super.reset(life, easing); 41 | } 42 | 43 | /** 44 | * Apply this behaviour for all particles every time 45 | * 46 | * @method applyBehaviour 47 | * @memberof Proton#Proton.Force 48 | * @instance 49 | * 50 | * @param {Proton.Particle} particle 51 | * @param {Number} the integrate time 1/ms 52 | * @param {Int} the particle index 53 | */ 54 | applyBehaviour(particle, time, index) { 55 | this.calculate(particle, time, index); 56 | particle.a.add(this.force); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/behaviour/Gravity.js: -------------------------------------------------------------------------------- 1 | import Force from "./Force"; 2 | 3 | export default class Gravity extends Force { 4 | /** 5 | * @memberof! Proton# 6 | * @augments Proton#Proton.Force 7 | * @constructor 8 | * @alias Proton.Gravity 9 | * 10 | * @param {Number} g Gravity 11 | * @param {Number} [life=Infinity] this behaviour's life 12 | * @param {String} [easing=ease.easeLinear] this behaviour's easing 13 | * 14 | * @property {String} name The Behaviour name 15 | */ 16 | constructor(g, life, easing) { 17 | super(0, g, life, easing); 18 | this.name = "Gravity"; 19 | } 20 | 21 | /** 22 | * Reset this behaviour's parameters 23 | * 24 | * @method reset 25 | * @memberof Proton#Proton.Gravity 26 | * @instance 27 | * 28 | * @param {Number} g Gravity 29 | * @param {Number} [life=Infinity] this behaviour's life 30 | * @param {String} [easing=ease.easeLinear] this behaviour's easing 31 | */ 32 | reset(g, life, easing) { 33 | super.reset(0, g, life, easing); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/behaviour/GravityWell.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import Vector2D from "../math/Vector2D"; 3 | import Behaviour from "./Behaviour"; 4 | 5 | export default class GravityWell extends Behaviour { 6 | /** 7 | * @memberof! Proton# 8 | * @augments Behaviour 9 | * @constructor 10 | * @alias GravityWell 11 | * 12 | * @param {Vector2D} [centerPoint=new Vector2D] The point in the center 13 | * @param {Number} [force=100] The force 14 | * @param {Number} [life=Infinity] this behaviour's life 15 | * @param {String} [easing=easeLinear] this behaviour's easing 16 | * 17 | * @property {String} name The Behaviour name 18 | */ 19 | constructor(centerPoint, force, life, easing) { 20 | super(life, easing); 21 | 22 | this.distanceVec = new Vector2D(); 23 | this.centerPoint = Util.initValue(centerPoint, new Vector2D()); 24 | this.force = Util.initValue(this.normalizeValue(force), 100); 25 | 26 | this.name = "GravityWell"; 27 | } 28 | 29 | /** 30 | * Reset this behaviour's parameters 31 | * 32 | * @method reset 33 | * @memberof Proton#GravityWell 34 | * @instance 35 | * 36 | * @param {Vector2D} [centerPoint=new Vector2D] The point in the center 37 | * @param {Number} [force=100] The force 38 | * @param {Number} [life=Infinity] this behaviour's life 39 | * @param {String} [easing=easeLinear] this behaviour's easing 40 | */ 41 | reset(centerPoint, force, life, easing) { 42 | this.distanceVec = new Vector2D(); 43 | this.centerPoint = Util.initValue(centerPoint, new Vector2D()); 44 | this.force = Util.initValue(this.normalizeValue(force), 100); 45 | 46 | life && super.reset(life, easing); 47 | } 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | initialize(particle) {} 53 | 54 | /** 55 | * Apply this behaviour for all particles every time 56 | * 57 | * @method applyBehaviour 58 | * @memberof Proton#GravityWell 59 | * @instance 60 | * 61 | * @param {Particle} particle 62 | * @param {Number} the integrate time 1/ms 63 | * @param {Int} the particle index 64 | */ 65 | applyBehaviour(particle, time, index) { 66 | this.distanceVec.set(this.centerPoint.x - particle.p.x, this.centerPoint.y - particle.p.y); 67 | const distanceSq = this.distanceVec.lengthSq(); 68 | 69 | if (distanceSq !== 0) { 70 | const distance = this.distanceVec.length(); 71 | const factor = (this.force * time) / (distanceSq * distance); 72 | 73 | particle.v.x += factor * this.distanceVec.x; 74 | particle.v.y += factor * this.distanceVec.y; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/behaviour/RandomDrift.js: -------------------------------------------------------------------------------- 1 | import Vector2D from "../math/Vector2D"; 2 | import MathUtil from "../math/MathUtil"; 3 | import Behaviour from "./Behaviour"; 4 | 5 | export default class RandomDrift extends Behaviour { 6 | /** 7 | * @memberof! Proton# 8 | * @augments Behaviour 9 | * @constructor 10 | * @alias RandomDrift 11 | * 12 | * @param {Number} driftX X value of the new Vector2D 13 | * @param {Number} driftY Y value of the new Vector2D 14 | * @param {Number} delay How much delay the drift should have 15 | * @param {Number} [life=Infinity] this behaviour's life 16 | * @param {String} [easing=easeLinear] this behaviour's easing 17 | * 18 | * @property {Number} time The time of the drift 19 | * @property {String} name The Behaviour name 20 | */ 21 | constructor(driftX, driftY, delay, life, easing) { 22 | super(life, easing); 23 | 24 | this.reset(driftX, driftY, delay); 25 | this.time = 0; 26 | this.name = "RandomDrift"; 27 | } 28 | 29 | /** 30 | * Reset this behaviour's parameters 31 | * 32 | * @method reset 33 | * @memberof Proton#RandomDrift 34 | * @instance 35 | * 36 | * @param {Number} driftX X value of the new Vector2D 37 | * @param {Number} driftY Y value of the new Vector2D 38 | * @param {Number} delay How much delay the drift should have 39 | * @param {Number} [life=Infinity] this behaviour's life 40 | * @param {String} [easing=easeLinear] this behaviour's easing 41 | */ 42 | reset(driftX, driftY, delay, life, easing) { 43 | this.panFoce = new Vector2D(driftX, driftY); 44 | this.panFoce = this.normalizeForce(this.panFoce); 45 | this.delay = delay; 46 | 47 | life && super.reset(life, easing); 48 | } 49 | 50 | initialize(particle) { 51 | particle.data.time = 0; 52 | } 53 | 54 | /** 55 | * Apply this behaviour for all particles every time 56 | * 57 | * @method applyBehaviour 58 | * @memberof Proton#RandomDrift 59 | * @instance 60 | * 61 | * @param {Particle} particle 62 | * @param {Number} time the integrate time 1/ms 63 | * @param {Int} index the particle index 64 | */ 65 | applyBehaviour(particle, time, index) { 66 | this.calculate(particle, time, index); 67 | particle.data.time += time; 68 | 69 | if (particle.data.time >= this.delay) { 70 | particle.a.addXY( 71 | MathUtil.randomAToB(-this.panFoce.x, this.panFoce.x), 72 | MathUtil.randomAToB(-this.panFoce.y, this.panFoce.y) 73 | ); 74 | 75 | particle.data.time = 0; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/behaviour/Repulsion.js: -------------------------------------------------------------------------------- 1 | import Attraction from "./Attraction"; 2 | 3 | /** 4 | * The opposite of Attraction - turns the force 5 | * 6 | * @class 7 | * @extends Proton.Attraction 8 | * @memberof! Proton# 9 | * @alias Proton.Repulsion 10 | */ 11 | export default class Repulsion extends Attraction { 12 | /** 13 | * Creates a new Repulsion behaviour instance 14 | * 15 | * @constructor 16 | * @param {Proton.Vector2D} targetPosition - The repulsion point coordinates 17 | * @param {number} [force=100] - The strength of the repulsion force 18 | * @param {number} [radius=1000] - The radius of influence for the repulsion 19 | * @param {number} [life=Infinity] - The behaviour's life 20 | * @param {string} [easing='easeLinear'] - The behaviour's easing function 21 | */ 22 | constructor(targetPosition, force, radius, life, easing) { 23 | super(targetPosition, force, radius, life, easing); 24 | 25 | /** 26 | * The strength of the repulsion force 27 | * @type {number} 28 | */ 29 | this.force *= -1; 30 | 31 | /** 32 | * The name of the behaviour 33 | * @type {string} 34 | */ 35 | this.name = "Repulsion"; 36 | } 37 | 38 | /** 39 | * Reset this behaviour's parameters 40 | * 41 | * @param {Proton.Vector2D} targetPosition - The new repulsion point coordinates 42 | * @param {number} [force=100] - The new strength of the repulsion force 43 | * @param {number} [radius=1000] - The new radius of influence for the repulsion 44 | * @param {number} [life=Infinity] - The new behaviour's life 45 | * @param {string} [easing='easeLinear'] - The new behaviour's easing function 46 | */ 47 | reset(targetPosition, force, radius, life, easing) { 48 | super.reset(targetPosition, force, radius, life, easing); 49 | this.force *= -1; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/behaviour/Rotate.js: -------------------------------------------------------------------------------- 1 | import Span from "../math/Span"; 2 | import Util from "../utils/Util"; 3 | import Behaviour from "./Behaviour"; 4 | 5 | /** 6 | * Rotate behaviour for controlling particle rotation. 7 | * @extends Behaviour 8 | */ 9 | export default class Rotate extends Behaviour { 10 | /** 11 | * @type {boolean} 12 | * @private 13 | */ 14 | same; 15 | 16 | /** 17 | * @type {Span} 18 | * @private 19 | */ 20 | a; 21 | 22 | /** 23 | * @type {Span} 24 | * @private 25 | */ 26 | b; 27 | 28 | /** 29 | * @type {string} 30 | * @private 31 | */ 32 | style; 33 | 34 | /** 35 | * @type {string} 36 | */ 37 | name; 38 | 39 | /** 40 | * Creates a new Rotate instance. 41 | * @param {string|number|Span} [influence='Velocity'] - The rotation's influence or initial rotation. 42 | * @param {string|number|Span} [b] - The final rotation value or range. 43 | * @param {string} [style='to'] - The style of rotation ('to' or 'add'). 44 | * @param {number} [life=Infinity] - This behaviour's life. 45 | * @param {string} [easing='easeLinear'] - This behaviour's easing function. 46 | */ 47 | constructor(influence, b, style, life, easing) { 48 | super(life, easing); 49 | 50 | this.reset(influence, b, style); 51 | this.name = "Rotate"; 52 | } 53 | 54 | /** 55 | * Resets this behaviour's parameters. 56 | * @param {string|number|Span} [a='Velocity'] - The rotation's influence or initial rotation. 57 | * @param {string|number|Span} [b] - The final rotation value or range. 58 | * @param {string} [style='to'] - The style of rotation ('to' or 'add'). 59 | * @param {number} [life] - This behaviour's life. 60 | * @param {string} [easing] - This behaviour's easing function. 61 | */ 62 | reset(a, b, style, life, easing) { 63 | this.same = b === null || b === undefined; 64 | 65 | this.a = Span.setSpanValue(Util.initValue(a, "Velocity")); 66 | this.b = Span.setSpanValue(Util.initValue(b, 0)); 67 | this.style = Util.initValue(style, "to"); 68 | 69 | life && super.reset(life, easing); 70 | } 71 | 72 | /** 73 | * Initializes the behaviour's parameters for a particle. 74 | * @param {object} particle - The particle to initialize. 75 | * @param {number} particle.rotation - The particle's rotation. 76 | * @param {object} particle.data - The particle's data object. 77 | */ 78 | initialize(particle) { 79 | particle.rotation = this.a.getValue(); 80 | particle.data.rotationA = this.a.getValue(); 81 | 82 | if (!this.same) particle.data.rotationB = this.b.getValue(); 83 | } 84 | 85 | /** 86 | * Applies this behaviour to a particle. 87 | * @param {object} particle - The particle to apply the behaviour to. 88 | * @param {number} time - The integrate time (1/ms). 89 | * @param {number} index - The particle index. 90 | */ 91 | applyBehaviour(particle, time, index) { 92 | this.calculate(particle, time, index); 93 | 94 | if (!this.same) { 95 | if (this.style === "to" || this.style === "TO" || this.style === "_") { 96 | particle.rotation += 97 | particle.data.rotationB + (particle.data.rotationA - particle.data.rotationB) * this.energy; 98 | } else { 99 | particle.rotation += particle.data.rotationB; 100 | } 101 | } else if (this.a.a === "V" || this.a.a === "Velocity" || this.a.a === "v") { 102 | // beta... 103 | particle.rotation = particle.getDirection(); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/behaviour/Scale.js: -------------------------------------------------------------------------------- 1 | import Span from "../math/Span"; 2 | import Util from "../utils/Util"; 3 | import Behaviour from "./Behaviour"; 4 | 5 | /** 6 | * Scale behaviour for controlling particle size over time. 7 | * @extends Behaviour 8 | */ 9 | export default class Scale extends Behaviour { 10 | /** 11 | * @type {boolean} 12 | * @private 13 | */ 14 | same; 15 | 16 | /** 17 | * @type {string} 18 | */ 19 | name; 20 | 21 | /** 22 | * Creates a new Scale instance. 23 | * @param {number|Span} [a=1] - The initial scale value or range. 24 | * @param {number|Span} [b] - The final scale value or range. If not provided, it will be the same as 'a'. 25 | * @param {number} [life=Infinity] - This behaviour's life. 26 | * @param {string} [easing='easeLinear'] - This behaviour's easing function. 27 | */ 28 | constructor(a, b, life, easing) { 29 | super(life, easing); 30 | 31 | this.reset(a, b); 32 | this.name = "Scale"; 33 | } 34 | 35 | /** 36 | * Resets this behaviour's parameters. 37 | * @param {number|Span} a - The initial scale value or range. 38 | * @param {number|Span} [b] - The final scale value or range. If not provided, it will be the same as 'a'. 39 | * @param {number} [life] - This behaviour's life. 40 | * @param {string} [easing] - This behaviour's easing function. 41 | */ 42 | reset(a, b, life, easing) { 43 | this.same = b === null || b === undefined; 44 | this.a = Span.setSpanValue(Util.initValue(a, 1)); 45 | this.b = Span.setSpanValue(b); 46 | 47 | life && super.reset(life, easing); 48 | } 49 | 50 | /** 51 | * Initializes the particle's scale values. 52 | * @param {Particle} particle - The particle to initialize. 53 | */ 54 | initialize(particle) { 55 | particle.data.scaleA = this.a.getValue(); 56 | particle.data.oldRadius = particle.radius; 57 | particle.data.scaleB = this.same ? particle.data.scaleA : this.b.getValue(); 58 | } 59 | 60 | /** 61 | * Applies the scale behaviour to the particle. 62 | * @param {Particle} particle - The particle to apply the behaviour to. 63 | * @param {number} time - The current simulation time. 64 | * @param {number} index - The index of the particle. 65 | */ 66 | applyBehaviour(particle, time, index) { 67 | this.calculate(particle, time, index); 68 | particle.scale = particle.data.scaleB + (particle.data.scaleA - particle.data.scaleB) * this.energy; 69 | 70 | if (particle.scale < 0.0001) particle.scale = 0; 71 | particle.radius = particle.data.oldRadius * particle.scale; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/core/Pool.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Pool is the cache pool of the proton engine, it is very important. 3 | * 4 | * get(target, params, uid) 5 | * Class 6 | * uid = Puid.getId -> Puid save target cache 7 | * target.__puid = uid 8 | * 9 | * body 10 | * uid = Puid.getId -> Puid save target cache 11 | * 12 | * 13 | * expire(target) 14 | * cache[target.__puid] push target 15 | * 16 | */ 17 | import Util from "../utils/Util"; 18 | import Puid from "../utils/Puid"; 19 | 20 | export default class Pool { 21 | /** 22 | * @memberof! Proton# 23 | * @constructor 24 | * @alias Proton.Pool 25 | * 26 | * @todo add description 27 | * @todo add description of properties 28 | * 29 | * @property {Number} total 30 | * @property {Object} cache 31 | */ 32 | constructor(num) { 33 | this.total = 0; 34 | this.cache = {}; 35 | } 36 | 37 | /** 38 | * @todo add description 39 | * 40 | * @method get 41 | * @memberof Proton#Proton.Pool 42 | * 43 | * @param {Object|Function} target 44 | * @param {Object} [params] just add if `target` is a function 45 | * 46 | * @return {Object} 47 | */ 48 | get(target, params, uid) { 49 | let p; 50 | uid = uid || target.__puid || Puid.getId(target); 51 | 52 | if (this.cache[uid] && this.cache[uid].length > 0) { 53 | p = this.cache[uid].pop(); 54 | } else { 55 | p = this.createOrClone(target, params); 56 | } 57 | 58 | p.__puid = target.__puid || uid; 59 | return p; 60 | } 61 | 62 | /** 63 | * @todo add description 64 | * 65 | * @method set 66 | * @memberof Proton#Proton.Pool 67 | * 68 | * @param {Object} target 69 | * 70 | * @return {Object} 71 | */ 72 | expire(target) { 73 | return this.getCache(target.__puid).push(target); 74 | } 75 | 76 | /** 77 | * Creates a new class instance 78 | * 79 | * @todo add more documentation 80 | * 81 | * @method create 82 | * @memberof Proton#Proton.Pool 83 | * 84 | * @param {Object|Function} target any Object or Function 85 | * @param {Object} [params] just add if `target` is a function 86 | * 87 | * @return {Object} 88 | */ 89 | createOrClone(target, params) { 90 | this.total++; 91 | 92 | if (this.create) { 93 | return this.create(target, params); 94 | } else if (typeof target === "function") { 95 | return Util.classApply(target, params); 96 | } else { 97 | return target.clone(); 98 | } 99 | } 100 | 101 | /** 102 | * @todo add description - what is in the cache? 103 | * 104 | * @method getCount 105 | * @memberof Proton#Proton.Pool 106 | * 107 | * @return {Number} 108 | */ 109 | getCount() { 110 | let count = 0; 111 | for (let id in this.cache) count += this.cache[id].length; 112 | return count++; 113 | } 114 | 115 | /** 116 | * Destroyes all items from Pool.cache 117 | * 118 | * @method destroy 119 | * @memberof Proton#Proton.Pool 120 | */ 121 | destroy() { 122 | for (let id in this.cache) { 123 | this.cache[id].length = 0; 124 | delete this.cache[id]; 125 | } 126 | } 127 | 128 | /** 129 | * Returns Pool.cache 130 | * 131 | * @method getCache 132 | * @memberof Proton#Proton.Pool 133 | * @private 134 | * 135 | * @param {Number} uid the unique id 136 | * 137 | * @return {Object} 138 | */ 139 | getCache(uid = "default") { 140 | if (!this.cache[uid]) this.cache[uid] = []; 141 | return this.cache[uid]; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/core/Settings.js: -------------------------------------------------------------------------------- 1 | class Settings { 2 | constructor() { 3 | this.measure = 100; 4 | } 5 | 6 | // get measure() { 7 | // return this._measure; 8 | // } 9 | 10 | // set measure(value) { 11 | // if (typeof value === "number" && value > 0) { 12 | // this._measure = value; 13 | // } else { 14 | // console.warn("MEASURE must be a positive number"); 15 | // } 16 | // } 17 | } 18 | 19 | export default new Settings(); 20 | -------------------------------------------------------------------------------- /src/debug/Debug.js: -------------------------------------------------------------------------------- 1 | import ColorUtil from "../utils/ColorUtil"; 2 | import CircleZone from "../zone/CircleZone"; 3 | import PointZone from "../zone/PointZone"; 4 | import LineZone from "../zone/LineZone"; 5 | import RectZone from "../zone/RectZone"; 6 | 7 | export default { 8 | addEventListener(proton, func) { 9 | proton.addEventListener("PROTON_UPDATE_AFTER", () => func()); 10 | }, 11 | 12 | getStyle(color = "#ff0000") { 13 | const rgb = ColorUtil.hexToRgb(color); 14 | return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.5)`; 15 | }, 16 | 17 | drawZone(proton, canvas, zone, clear) { 18 | const context = canvas.getContext("2d"); 19 | const style = this.getStyle(); 20 | 21 | this.addEventListener(proton, () => { 22 | if (clear) context.clearRect(0, 0, canvas.width, canvas.height); 23 | 24 | if (zone instanceof PointZone) { 25 | context.beginPath(); 26 | context.fillStyle = style; 27 | context.arc(zone.x, zone.y, 10, 0, Math.PI * 2, true); 28 | context.fill(); 29 | context.closePath(); 30 | } else if (zone instanceof LineZone) { 31 | context.beginPath(); 32 | context.strokeStyle = style; 33 | context.moveTo(zone.x1, zone.y1); 34 | context.lineTo(zone.x2, zone.y2); 35 | context.stroke(); 36 | context.closePath(); 37 | } else if (zone instanceof RectZone) { 38 | context.beginPath(); 39 | context.strokeStyle = style; 40 | context.drawRect(zone.x, zone.y, zone.width, zone.height); 41 | context.stroke(); 42 | context.closePath(); 43 | } else if (zone instanceof CircleZone) { 44 | context.beginPath(); 45 | context.strokeStyle = style; 46 | context.arc(zone.x, zone.y, zone.radius, 0, Math.PI * 2, true); 47 | context.stroke(); 48 | context.closePath(); 49 | } 50 | }); 51 | }, 52 | 53 | drawEmitter(proton, canvas, emitter, clear) { 54 | const context = canvas.getContext("2d"); 55 | const style = this.getStyle(); 56 | 57 | this.addEventListener(proton, () => { 58 | if (clear) context.clearRect(0, 0, canvas.width, canvas.height); 59 | 60 | context.beginPath(); 61 | context.fillStyle = style; 62 | context.arc(emitter.p.x, emitter.p.y, 10, 0, Math.PI * 2, true); 63 | context.fill(); 64 | context.closePath(); 65 | }); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /src/emitter/BehaviourEmitter.js: -------------------------------------------------------------------------------- 1 | import Emitter from "./Emitter"; 2 | 3 | export default class BehaviourEmitter extends Emitter { 4 | /** 5 | * The BehaviourEmitter class inherits from Proton.Emitter 6 | * 7 | * use the BehaviourEmitter you can add behaviours to self; 8 | * @class Proton.BehaviourEmitter 9 | * @constructor 10 | * @param {Object} conf the parameters object; 11 | */ 12 | constructor(conf) { 13 | super(conf); 14 | 15 | this.selfBehaviours = []; 16 | } 17 | 18 | /** 19 | * add the Behaviour to emitter; 20 | * 21 | * you can use Behaviours array:emitter.addSelfBehaviour(Behaviour1,Behaviour2,Behaviour3); 22 | * @method addSelfBehaviour 23 | * @param {Proton.Behaviour} behaviour like this new Proton.Color('random') 24 | */ 25 | addSelfBehaviour(...rest) { 26 | let i, 27 | length = rest.length; 28 | 29 | for (i = 0; i < length; i++) { 30 | let behaviour = rest[i]; 31 | this.selfBehaviours.push(behaviour); 32 | behaviour.initialize(this); 33 | } 34 | } 35 | 36 | /** 37 | * remove the Behaviour for self 38 | * @method removeSelfBehaviour 39 | * @param {Proton.Behaviour} behaviour a behaviour 40 | */ 41 | removeSelfBehaviour(behaviour) { 42 | const index = this.selfBehaviours.indexOf(behaviour); 43 | if (index > -1) this.selfBehaviours.splice(index, 1); 44 | } 45 | 46 | update(time) { 47 | super.update(time); 48 | 49 | if (!this.sleep) { 50 | const length = this.selfBehaviours.length; 51 | let i; 52 | 53 | for (i = 0; i < length; i++) { 54 | this.selfBehaviours[i].applyBehaviour(this, time, i); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/emitter/FollowEmitter.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import Emitter from "./Emitter"; 3 | 4 | export default class FollowEmitter extends Emitter { 5 | /** 6 | * The FollowEmitter class inherits from Proton.Emitter 7 | * 8 | * use the FollowEmitter will emit particle when mousemoving 9 | * 10 | * @class Proton.FollowEmitter 11 | * @constructor 12 | * @param {Element} mouseTarget mouseevent's target; 13 | * @param {Number} ease the easing of following speed; 14 | * @default 0.7 15 | * @param {Object} conf the parameters object; 16 | */ 17 | constructor(mouseTarget, ease, conf) { 18 | super(conf); 19 | 20 | this.mouseTarget = Util.initValue(mouseTarget, window); 21 | this.ease = Util.initValue(ease, 0.7); 22 | 23 | this._allowEmitting = false; 24 | this.initEventHandler(); 25 | } 26 | 27 | initEventHandler() { 28 | this.mousemoveHandler = e => this.mousemove.call(this, e); 29 | this.mousedownHandler = e => this.mousedown.call(this, e); 30 | this.mouseupHandler = e => this.mouseup.call(this, e); 31 | this.mouseTarget.addEventListener("mousemove", this.mousemoveHandler, false); 32 | } 33 | 34 | /** 35 | * start emit particle 36 | * @method emit 37 | */ 38 | emit() { 39 | this._allowEmitting = true; 40 | } 41 | 42 | /** 43 | * stop emiting 44 | * @method stop 45 | */ 46 | stop() { 47 | this._allowEmitting = false; 48 | } 49 | 50 | mousemove(e) { 51 | if (e.layerX || e.layerX === 0) { 52 | this.p.x += (e.layerX - this.p.x) * this.ease; 53 | this.p.y += (e.layerY - this.p.y) * this.ease; 54 | } else if (e.offsetX || e.offsetX === 0) { 55 | this.p.x += (e.offsetX - this.p.x) * this.ease; 56 | this.p.y += (e.offsetY - this.p.y) * this.ease; 57 | } 58 | 59 | if (this._allowEmitting) super.emit("once"); 60 | } 61 | 62 | /** 63 | * Destory this Emitter 64 | * @method destroy 65 | */ 66 | destroy() { 67 | super.destroy(); 68 | this.mouseTarget.removeEventListener("mousemove", this.mousemoveHandler, false); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/events/EventDispatcher.js: -------------------------------------------------------------------------------- 1 | /* 2 | * EventDispatcher 3 | * This code reference since http://createjs.com/. 4 | * 5 | **/ 6 | 7 | export default class EventDispatcher { 8 | constructor() { 9 | this._listeners = null; 10 | } 11 | 12 | static bind(target) { 13 | target.prototype.dispatchEvent = EventDispatcher.prototype.dispatchEvent; 14 | target.prototype.hasEventListener = EventDispatcher.prototype.hasEventListener; 15 | target.prototype.addEventListener = EventDispatcher.prototype.addEventListener; 16 | target.prototype.removeEventListener = EventDispatcher.prototype.removeEventListener; 17 | target.prototype.removeAllEventListeners = EventDispatcher.prototype.removeAllEventListeners; 18 | } 19 | 20 | addEventListener(type, listener) { 21 | if (!this._listeners) { 22 | this._listeners = {}; 23 | } else { 24 | this.removeEventListener(type, listener); 25 | } 26 | 27 | if (!this._listeners[type]) this._listeners[type] = []; 28 | this._listeners[type].push(listener); 29 | 30 | return listener; 31 | } 32 | 33 | removeEventListener(type, listener) { 34 | if (!this._listeners) return; 35 | if (!this._listeners[type]) return; 36 | 37 | const arr = this._listeners[type]; 38 | const length = arr.length; 39 | 40 | for (let i = 0; i < length; i++) { 41 | if (arr[i] === listener) { 42 | if (length === 1) { 43 | delete this._listeners[type]; 44 | } 45 | 46 | // allows for faster checks. 47 | else { 48 | arr.splice(i, 1); 49 | } 50 | 51 | break; 52 | } 53 | } 54 | } 55 | 56 | removeAllEventListeners(type) { 57 | if (!type) this._listeners = null; 58 | else if (this._listeners) delete this._listeners[type]; 59 | } 60 | 61 | dispatchEvent(type, args) { 62 | let result = false; 63 | const listeners = this._listeners; 64 | 65 | if (type && listeners) { 66 | let arr = listeners[type]; 67 | if (!arr) return result; 68 | 69 | // arr = arr.slice(); 70 | // to avoid issues with items being removed or added during the dispatch 71 | 72 | let handler; 73 | let i = arr.length; 74 | while (i--) { 75 | handler = arr[i]; 76 | result = result || handler(args); 77 | } 78 | } 79 | 80 | return !!result; 81 | } 82 | 83 | hasEventListener(type) { 84 | const listeners = this._listeners; 85 | return !!(listeners && listeners[type]); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/initialize/Body.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import ArraySpan from "../math/ArraySpan"; 3 | import Initialize from "./Initialize"; 4 | 5 | /** 6 | * Body class for initializing particle bodies. 7 | * @extends Initialize 8 | */ 9 | export default class Body extends Initialize { 10 | /** 11 | * @type {ArraySpan} 12 | * @private 13 | */ 14 | image; 15 | 16 | /** 17 | * @type {string} 18 | */ 19 | name; 20 | 21 | /** 22 | * Creates a new Body instance. 23 | * @param {string|object|Image|HTMLImageElement|ArraySpan} image - The image source or object to use for the particle body. 24 | * @param {number} [w=20] - The width of the particle body. 25 | * @param {number} [h] - The height of the particle body. Defaults to the width if not provided. 26 | */ 27 | constructor(image, w, h) { 28 | super(); 29 | 30 | this.image = this.setSpanValue(image); 31 | this.w = Util.initValue(w, 20); 32 | this.h = Util.initValue(h, this.w); 33 | this.name = "Body"; 34 | } 35 | 36 | /** 37 | * Initializes the particle's body. 38 | * @param {object} particle - The particle to initialize. 39 | */ 40 | initialize(particle) { 41 | const imageTarget = this.image.getValue(); 42 | 43 | if (typeof imageTarget === "string") { 44 | particle.body = { 45 | width: this.w, 46 | height: this.h, 47 | src: imageTarget, 48 | isInner: true, 49 | inner: true 50 | }; 51 | } else { 52 | particle.body = imageTarget; 53 | } 54 | } 55 | 56 | /** 57 | * Sets the span value for the image. 58 | * @param {string|object|Image|HTMLImageElement|ArraySpan} image - The image source or object to set as span value. 59 | * @returns {ArraySpan} The ArraySpan instance. 60 | * @private 61 | */ 62 | setSpanValue(image) { 63 | return image instanceof ArraySpan ? image : new ArraySpan(image); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/initialize/Initialize.js: -------------------------------------------------------------------------------- 1 | export default class Initialize { 2 | reset() {} 3 | 4 | init(emitter, particle) { 5 | if (particle) { 6 | this.initialize(particle); 7 | } else { 8 | this.initialize(emitter); 9 | } 10 | } 11 | 12 | // sub class init 13 | initialize(target) {} 14 | } 15 | -------------------------------------------------------------------------------- /src/initialize/InitializeUtil.js: -------------------------------------------------------------------------------- 1 | import PropUtil from "../utils/PropUtil"; 2 | import Initialize from "./Initialize"; 3 | import MathUtil from "../math/MathUtil"; 4 | 5 | export default { 6 | initialize(emitter, particle, initializes) { 7 | const length = initializes.length; 8 | let i; 9 | 10 | for (i = 0; i < length; i++) { 11 | if (initializes[i] instanceof Initialize) { 12 | initializes[i].init(emitter, particle); 13 | } else { 14 | this.init(emitter, particle, initializes[i]); 15 | } 16 | } 17 | 18 | this.bindEmitter(emitter, particle); 19 | }, 20 | 21 | // init 22 | init(emitter, particle, initialize) { 23 | PropUtil.setProp(particle, initialize); 24 | PropUtil.setVectorVal(particle, initialize); 25 | }, 26 | 27 | bindEmitter(emitter, particle) { 28 | if (emitter.bindEmitter) { 29 | particle.p.add(emitter.p); 30 | particle.v.add(emitter.v); 31 | particle.a.add(emitter.a); 32 | particle.v.rotate(MathUtil.degreeTransform(emitter.rotation)); 33 | } 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/initialize/Life.js: -------------------------------------------------------------------------------- 1 | import Span from "../math/Span"; 2 | import Initialize from "./Initialize"; 3 | 4 | /** 5 | * Life class for initializing particle lifetime. 6 | * @extends Initialize 7 | */ 8 | export default class Life extends Initialize { 9 | /** 10 | * @type {Span} 11 | * @private 12 | */ 13 | lifePan; 14 | 15 | /** 16 | * @type {string} 17 | */ 18 | name; 19 | 20 | /** 21 | * Creates a new Life instance. 22 | * @param {number|Span} a - The lifetime value or the lower bound of the lifetime range. 23 | * @param {number} [b] - The upper bound of the lifetime range (if a is a number). 24 | * @param {boolean} [c] - Whether to use center-based calculation (if a and b are numbers). 25 | */ 26 | constructor(a, b, c) { 27 | super(); 28 | 29 | this.lifePan = Span.setSpanValue(a, b, c); 30 | this.name = "Life"; 31 | } 32 | 33 | /** 34 | * Initializes the lifetime of a target particle. 35 | * @param {object} target - The target particle to initialize. 36 | */ 37 | initialize(target) { 38 | if (this.lifePan.a === Infinity) target.life = Infinity; 39 | else target.life = this.lifePan.getValue(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/initialize/Mass.js: -------------------------------------------------------------------------------- 1 | import Span from "../math/Span"; 2 | import Initialize from "./Initialize"; 3 | 4 | /** 5 | * Mass class for initializing particle mass. 6 | * @extends Initialize 7 | */ 8 | export default class Mass extends Initialize { 9 | /** 10 | * @type {Span} 11 | * @private 12 | */ 13 | massPan; 14 | 15 | /** 16 | * @type {string} 17 | */ 18 | name; 19 | 20 | /** 21 | * Creates a new Mass instance. 22 | * @param {number|Span} a - The mass value or the lower bound of the mass range. 23 | * @param {number} [b] - The upper bound of the mass range (if a is a number). 24 | * @param {boolean} [c] - Whether to use center-based calculation (if a and b are numbers). 25 | */ 26 | constructor(a, b, c) { 27 | super(); 28 | this.massPan = Span.setSpanValue(a, b, c); 29 | this.name = "Mass"; 30 | } 31 | 32 | /** 33 | * Initializes the mass of a target particle. 34 | * @param {object} target - The target particle to initialize. 35 | */ 36 | initialize(target) { 37 | target.mass = this.massPan.getValue(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/initialize/Position.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import PointZone from "../zone/PointZone"; 3 | import Initialize from "./Initialize"; 4 | 5 | /** 6 | * Position class for initializing particle positions. 7 | * @extends Initialize 8 | */ 9 | export default class Position extends Initialize { 10 | /** 11 | * @type {PointZone|any} 12 | * @private 13 | */ 14 | zone; 15 | 16 | /** 17 | * @type {string} 18 | */ 19 | name; 20 | 21 | /** 22 | * Creates a new Position instance. 23 | * @param {PointZone|any} [zone] - The zone to use for positioning. Defaults to a new PointZone if not provided. 24 | */ 25 | constructor(zone) { 26 | super(); 27 | this.zone = Util.initValue(zone, new PointZone()); 28 | this.name = "Position"; 29 | } 30 | 31 | /** 32 | * Resets this initializer's parameters. 33 | * @param {PointZone|any} [zone] - The new zone to use for positioning. Defaults to a new PointZone if not provided. 34 | */ 35 | reset(zone) { 36 | this.zone = Util.initValue(zone, new PointZone()); 37 | } 38 | 39 | /** 40 | * Initializes the particle's position. 41 | * @param {object} target - The particle to initialize. 42 | * @param {object} target.p - The particle's position object. 43 | * @param {number} target.p.x - The particle's x coordinate. 44 | * @param {number} target.p.y - The particle's y coordinate. 45 | */ 46 | initialize(target) { 47 | this.zone.getPosition(); 48 | 49 | target.p.x = this.zone.vector.x; 50 | target.p.y = this.zone.vector.y; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/initialize/Radius.js: -------------------------------------------------------------------------------- 1 | import Span from "../math/Span"; 2 | import Initialize from "./Initialize"; 3 | 4 | /** 5 | * Radius class for initializing particle radius. 6 | * @extends Initialize 7 | */ 8 | export default class Radius extends Initialize { 9 | /** 10 | * @type {Span} 11 | */ 12 | radius; 13 | 14 | /** 15 | * @type {string} 16 | */ 17 | name; 18 | 19 | /** 20 | * Creates a new Radius instance. 21 | * @param {number|Span} a - The radius value or the lower bound of the radius range. 22 | * @param {number} [b] - The upper bound of the radius range (if a is a number). 23 | * @param {boolean} [c] - Whether to use center-based calculation (if a and b are numbers). 24 | */ 25 | constructor(a, b, c) { 26 | super(); 27 | this.radius = Span.setSpanValue(a, b, c); 28 | this.name = "Radius"; 29 | } 30 | 31 | /** 32 | * Resets this initializer's parameters. 33 | * @param {number|Span} a - The radius value or the lower bound of the radius range. 34 | * @param {number} [b] - The upper bound of the radius range (if a is a number). 35 | * @param {boolean} [c] - Whether to use center-based calculation (if a and b are numbers). 36 | */ 37 | reset(a, b, c) { 38 | this.radius = Span.setSpanValue(a, b, c); 39 | } 40 | 41 | /** 42 | * Initializes the particle's radius. 43 | * @param {Particle} particle - The particle to initialize. 44 | */ 45 | initialize(particle) { 46 | particle.radius = this.radius.getValue(); 47 | particle.data.oldRadius = particle.radius; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/initialize/Rate.js: -------------------------------------------------------------------------------- 1 | import Span from "../math/Span"; 2 | import Util from "../utils/Util"; 3 | 4 | /** 5 | * Rate class for controlling particle emission rate. 6 | */ 7 | export default class Rate { 8 | /** 9 | * @type {Span} 10 | * @private 11 | */ 12 | numPan; 13 | 14 | /** 15 | * @type {Span} 16 | * @private 17 | */ 18 | timePan; 19 | 20 | /** 21 | * @type {number} 22 | * @private 23 | */ 24 | startTime; 25 | 26 | /** 27 | * @type {number} 28 | * @private 29 | */ 30 | nextTime; 31 | 32 | /** 33 | * Creates a new Rate instance. 34 | * The number of particles per second emission (a [particle]/b [s]). 35 | * @param {Array|number|Span} [numpan=1] - The number of particles for each emission. 36 | * @param {Array|number|Span} [timepan=1] - The time interval between each emission. 37 | * @example 38 | * // Create a rate of 10-20 particles every 0.1-0.25 seconds 39 | * new Rate(new Span(10, 20), new Span(0.1, 0.25)); 40 | */ 41 | constructor(numpan, timepan) { 42 | this.numPan = Span.setSpanValue(Util.initValue(numpan, 1)); 43 | this.timePan = Span.setSpanValue(Util.initValue(timepan, 1)); 44 | 45 | this.startTime = 0; 46 | this.nextTime = 0; 47 | this.init(); 48 | } 49 | 50 | /** 51 | * Initializes the rate. 52 | * @private 53 | */ 54 | init() { 55 | this.startTime = 0; 56 | this.nextTime = this.timePan.getValue(); 57 | } 58 | 59 | /** 60 | * Gets the number of particles to emit based on the elapsed time. 61 | * @param {number} time - The elapsed time since the last update. 62 | * @returns {number} The number of particles to emit. 63 | */ 64 | getValue(time) { 65 | this.startTime += time; 66 | 67 | if (this.startTime >= this.nextTime) { 68 | this.startTime = 0; 69 | this.nextTime = this.timePan.getValue(); 70 | 71 | if (this.numPan.b === 1) { 72 | if (this.numPan.getValue(false) > 0.5) return 1; 73 | else return 0; 74 | } else { 75 | return this.numPan.getValue(true); 76 | } 77 | } 78 | 79 | return 0; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/initialize/Velocity.js: -------------------------------------------------------------------------------- 1 | import Proton from "../core/Proton"; 2 | import Span from "../math/Span"; 3 | import Util from "../utils/Util"; 4 | import Initialize from "./Initialize"; 5 | import Polar2D from "../math/Polar2D"; 6 | import MathUtil from "../math/MathUtil"; 7 | 8 | /** 9 | * Velocity class for initializing particle velocities. 10 | * @extends Initialize 11 | */ 12 | export default class Velocity extends Initialize { 13 | /** 14 | * @type {Span} 15 | * @private 16 | */ 17 | rPan; 18 | 19 | /** 20 | * @type {Span} 21 | * @private 22 | */ 23 | thaPan; 24 | 25 | /** 26 | * @type {string} 27 | */ 28 | name; 29 | 30 | /** 31 | * Creates a new Velocity instance. 32 | * @param {number|Span} [rpan] - The radial component of the velocity or its range. 33 | * @param {number|Span} [thapan] - The angular component of the velocity or its range. 34 | * @param {string} [type='vector'] - The type of velocity ('vector' or 'polar'). 35 | */ 36 | constructor(rpan, thapan, type) { 37 | super(); 38 | 39 | this.rPan = Span.setSpanValue(rpan); 40 | this.thaPan = Span.setSpanValue(thapan); 41 | this.type = Util.initValue(type, "vector"); 42 | 43 | this.name = "Velocity"; 44 | } 45 | 46 | /** 47 | * Resets the velocity parameters. 48 | * @param {number|Span} [rpan] - The radial component of the velocity or its range. 49 | * @param {number|Span} [thapan] - The angular component of the velocity or its range. 50 | * @param {string} [type='vector'] - The type of velocity ('vector' or 'polar'). 51 | */ 52 | reset(rpan, thapan, type) { 53 | this.rPan = Span.setSpanValue(rpan); 54 | this.thaPan = Span.setSpanValue(thapan); 55 | this.type = Util.initValue(type, "vector"); 56 | } 57 | 58 | /** 59 | * Normalizes the velocity value. 60 | * @param {number} vr - The velocity value to normalize. 61 | * @returns {number} The normalized velocity value. 62 | * @private 63 | */ 64 | normalizeVelocity(vr) { 65 | return vr * Proton.MEASURE; 66 | } 67 | 68 | /** 69 | * Initializes the particle's velocity. 70 | * @param {object} target - The particle to initialize. 71 | */ 72 | initialize(target) { 73 | if (this.type === "p" || this.type === "P" || this.type === "polar") { 74 | const polar2d = new Polar2D( 75 | this.normalizeVelocity(this.rPan.getValue()), 76 | this.thaPan.getValue() * MathUtil.PI_180 77 | ); 78 | 79 | target.v.x = polar2d.getX(); 80 | target.v.y = polar2d.getY(); 81 | } else { 82 | target.v.x = this.normalizeVelocity(this.rPan.getValue()); 83 | target.v.y = this.normalizeVelocity(this.thaPan.getValue()); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/math/ArraySpan.js: -------------------------------------------------------------------------------- 1 | import Span from "./Span"; 2 | import Util from "../utils/Util"; 3 | import MathUtil from "./MathUtil"; 4 | 5 | /** 6 | * Represents an ArraySpan, a subclass of Span that works with arrays. 7 | * @extends Span 8 | */ 9 | export default class ArraySpan extends Span { 10 | /** 11 | * Creates an instance of ArraySpan. 12 | * @param {Array|*|any} arr - The array or value to be converted to an array. 13 | */ 14 | constructor(arr) { 15 | super(); 16 | this._arr = Util.toArray(arr); 17 | } 18 | 19 | /** 20 | * Gets a random value from the array. 21 | * If the value is "random" or "Random", it returns a random color. 22 | * @returns {*} A random value from the array or a random color. 23 | */ 24 | getValue() { 25 | const val = Util.getRandFromArray(this._arr); 26 | return val === "random" || val === "Random" ? MathUtil.randomColor() : val; 27 | } 28 | 29 | /** 30 | * Creates an ArraySpan instance from the given array. 31 | * If the input is already an ArraySpan instance, it returns the input. 32 | * @static 33 | * @param {Array|ArraySpan|any} arr - The array or ArraySpan instance. 34 | * @returns {ArraySpan|null} A new ArraySpan instance or null if the input is falsy. 35 | */ 36 | static createArraySpan(arr) { 37 | if (!arr) return null; 38 | 39 | if (arr instanceof ArraySpan) return arr; 40 | else return new ArraySpan(arr); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/math/Integration.js: -------------------------------------------------------------------------------- 1 | export default class Integration { 2 | constructor(type) { 3 | this.type = type; 4 | } 5 | 6 | calculate(particles, time, damping) { 7 | this.eulerIntegrate(particles, time, damping); 8 | } 9 | 10 | // Euler Integrate 11 | // https://rosettacode.org/wiki/Euler_method 12 | eulerIntegrate(particle, time, damping) { 13 | if (!particle.sleep) { 14 | particle.old.p.copy(particle.p); 15 | particle.old.v.copy(particle.v); 16 | 17 | particle.a.multiplyScalar(1 / particle.mass); 18 | particle.v.add(particle.a.multiplyScalar(time)); 19 | particle.p.add(particle.old.v.multiplyScalar(time)); 20 | 21 | if (damping) particle.v.multiplyScalar(damping); 22 | 23 | particle.a.clear(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/math/Mat3.js: -------------------------------------------------------------------------------- 1 | const Mat3 = { 2 | create(mat3) { 3 | const mat = new Float32Array(9); 4 | if (mat3) this.set(mat3, mat); 5 | 6 | return mat; 7 | }, 8 | 9 | set(mat1, mat2) { 10 | for (let i = 0; i < 9; i++) mat2[i] = mat1[i]; 11 | 12 | return mat2; 13 | }, 14 | 15 | multiply(mat, mat2, mat3) { 16 | let a00 = mat[0], 17 | a01 = mat[1], 18 | a02 = mat[2], 19 | a10 = mat[3], 20 | a11 = mat[4], 21 | a20 = mat[6], 22 | a21 = mat[7], 23 | b00 = mat2[0], 24 | b01 = mat2[1], 25 | b02 = mat2[2], 26 | b10 = mat2[3], 27 | b11 = mat2[4], 28 | b20 = mat2[6], 29 | b21 = mat2[7]; 30 | 31 | mat3[0] = b00 * a00 + b01 * a10; 32 | mat3[1] = b00 * a01 + b01 * a11; 33 | mat3[2] = a02 * b02; 34 | mat3[3] = b10 * a00 + b11 * a10; 35 | mat3[4] = b10 * a01 + b11 * a11; 36 | mat3[6] = b20 * a00 + b21 * a10 + a20; 37 | mat3[7] = b20 * a01 + b21 * a11 + a21; 38 | 39 | return mat3; 40 | }, 41 | 42 | inverse(mat, mat3) { 43 | let a00 = mat[0], 44 | a01 = mat[1], 45 | a10 = mat[3], 46 | a11 = mat[4], 47 | a20 = mat[6], 48 | a21 = mat[7], 49 | b01 = a11, 50 | b11 = -a10, 51 | b21 = a21 * a10 - a11 * a20, 52 | d = a00 * b01 + a01 * b11, 53 | id; 54 | 55 | id = 1 / d; 56 | mat3[0] = b01 * id; 57 | mat3[1] = -a01 * id; 58 | mat3[3] = b11 * id; 59 | mat3[4] = a00 * id; 60 | mat3[6] = b21 * id; 61 | mat3[7] = (-a21 * a00 + a01 * a20) * id; 62 | 63 | return mat3; 64 | }, 65 | 66 | multiplyVec2(m, vec, mat3) { 67 | let x = vec[0], 68 | y = vec[1]; 69 | 70 | mat3[0] = x * m[0] + y * m[3] + m[6]; 71 | mat3[1] = x * m[1] + y * m[4] + m[7]; 72 | 73 | return mat3; 74 | } 75 | }; 76 | 77 | export default Mat3; 78 | -------------------------------------------------------------------------------- /src/math/MathUtil.js: -------------------------------------------------------------------------------- 1 | const PI = 3.1415926; 2 | const INFINITY = Infinity; 3 | 4 | const MathUtil = { 5 | PI: PI, 6 | PIx2: PI * 2, 7 | PI_2: PI / 2, 8 | PI_180: PI / 180, 9 | N180_PI: 180 / PI, 10 | Infinity: -999, 11 | 12 | isInfinity(num) { 13 | return num === this.Infinity || num === INFINITY; 14 | }, 15 | 16 | randomAToB(a, b, isInt = false) { 17 | if (!isInt) return a + Math.random() * (b - a); 18 | else return ((Math.random() * (b - a)) >> 0) + a; 19 | }, 20 | 21 | randomFloating(center, f, isInt) { 22 | return this.randomAToB(center - f, center + f, isInt); 23 | }, 24 | 25 | randomColor() { 26 | return "#" + ("00000" + ((Math.random() * 0x1000000) << 0).toString(16)).slice(-6); 27 | }, 28 | 29 | randomZone(display) {}, 30 | 31 | floor(num, k = 4) { 32 | const digits = Math.pow(10, k); 33 | return Math.floor(num * digits) / digits; 34 | }, 35 | 36 | degreeTransform(a) { 37 | return (a * PI) / 180; 38 | }, 39 | 40 | toColor16(num) { 41 | return `#${num.toString(16)}`; 42 | } 43 | }; 44 | 45 | export default MathUtil; 46 | -------------------------------------------------------------------------------- /src/math/Polar2D.js: -------------------------------------------------------------------------------- 1 | import Vector2D from "./Vector2D"; 2 | 3 | export default class Polar2D { 4 | constructor(r, tha) { 5 | this.r = Math.abs(r) || 0; 6 | this.tha = tha || 0; 7 | } 8 | 9 | set(r, tha) { 10 | this.r = r; 11 | this.tha = tha; 12 | return this; 13 | } 14 | 15 | setR(r) { 16 | this.r = r; 17 | return this; 18 | } 19 | 20 | setTha(tha) { 21 | this.tha = tha; 22 | return this; 23 | } 24 | 25 | copy(p) { 26 | this.r = p.r; 27 | this.tha = p.tha; 28 | return this; 29 | } 30 | 31 | toVector() { 32 | return new Vector2D(this.getX(), this.getY()); 33 | } 34 | 35 | getX() { 36 | return this.r * Math.sin(this.tha); 37 | } 38 | 39 | getY() { 40 | return -this.r * Math.cos(this.tha); 41 | } 42 | 43 | normalize() { 44 | this.r = 1; 45 | return this; 46 | } 47 | 48 | equals(v) { 49 | return v.r === this.r && v.tha === this.tha; 50 | } 51 | 52 | clear() { 53 | this.r = 0.0; 54 | this.tha = 0.0; 55 | return this; 56 | } 57 | 58 | clone() { 59 | return new Polar2D(this.r, this.tha); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/math/Rectangle.js: -------------------------------------------------------------------------------- 1 | export default class Rectangle { 2 | constructor(x, y, w, h) { 3 | this.x = x; 4 | this.y = y; 5 | 6 | this.width = w; 7 | this.height = h; 8 | 9 | this.bottom = this.y + this.height; 10 | this.right = this.x + this.width; 11 | } 12 | 13 | contains(x, y) { 14 | if (x <= this.right && x >= this.x && y <= this.bottom && y >= this.y) return true; 15 | else return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/math/Span.js: -------------------------------------------------------------------------------- 1 | import Util from "../utils/Util"; 2 | import MathUtil from "../math/MathUtil"; 3 | 4 | /** 5 | * Represents a span of values or an array. 6 | */ 7 | export default class Span { 8 | /** 9 | * @type {boolean} 10 | * @private 11 | */ 12 | isArray; 13 | 14 | /** 15 | * @type {number|number[]} 16 | * @private 17 | */ 18 | a; 19 | 20 | /** 21 | * @type {number} 22 | * @private 23 | */ 24 | b; 25 | 26 | /** 27 | * @type {boolean} 28 | * @private 29 | */ 30 | center; 31 | 32 | /** 33 | * Creates a new Span instance. 34 | * @param {number|number[]} a - The first value or an array of values. 35 | * @param {number} [b] - The second value (if a is not an array). 36 | * @param {boolean} [center=false] - Whether to use center-based calculation. 37 | */ 38 | constructor(a, b, center) { 39 | if (Util.isArray(a)) { 40 | this.isArray = true; 41 | this.a = a; 42 | } else { 43 | this.isArray = false; 44 | this.a = Util.initValue(a, 1); 45 | this.b = Util.initValue(b, this.a); 46 | this.center = Util.initValue(center, false); 47 | } 48 | } 49 | 50 | /** 51 | * Gets a value from the span. 52 | * @param {boolean} [isInt=false] - Whether to return an integer value. 53 | * @returns {number} A random value from the span. 54 | */ 55 | getValue(isInt = false) { 56 | if (this.isArray) { 57 | return Util.getRandFromArray(this.a); 58 | } else { 59 | if (!this.center) { 60 | return MathUtil.randomAToB(this.a, this.b, isInt); 61 | } else { 62 | return MathUtil.randomFloating(this.a, this.b, isInt); 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Returns a new Span object. 69 | * @param {*|Span} a - The first value or a Span object. 70 | * @param {*} [b] - The second value. 71 | * @param {*} [c] - The third value. 72 | * @returns {Span} A new Span instance. 73 | */ 74 | static setSpanValue(a, b, c) { 75 | if (a instanceof Span) { 76 | return a; 77 | } else { 78 | if (b === undefined) { 79 | return new Span(a); 80 | } else { 81 | if (c === undefined) return new Span(a, b); 82 | else return new Span(a, b, c); 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * Returns the value from a Span, if the param is not a Span it will return the given parameter. 89 | * @param {*|Span} pan - The value or Span to get the value from. 90 | * @returns {*} The value of Span OR the parameter if it is not a Span. 91 | */ 92 | static getSpanValue(pan) { 93 | return pan instanceof Span ? pan.getValue() : pan; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/math/ease.js: -------------------------------------------------------------------------------- 1 | import MathUtil from "./MathUtil"; 2 | 3 | export default { 4 | easeLinear(value) { 5 | return value; 6 | }, 7 | 8 | easeInQuad(value) { 9 | return Math.pow(value, 2); 10 | }, 11 | 12 | easeOutQuad(value) { 13 | return -(Math.pow(value - 1, 2) - 1); 14 | }, 15 | 16 | easeInOutQuad(value) { 17 | if ((value /= 0.5) < 1) return 0.5 * Math.pow(value, 2); 18 | 19 | return -0.5 * ((value -= 2) * value - 2); 20 | }, 21 | 22 | easeInCubic(value) { 23 | return Math.pow(value, 3); 24 | }, 25 | 26 | easeOutCubic(value) { 27 | return Math.pow(value - 1, 3) + 1; 28 | }, 29 | 30 | easeInOutCubic(value) { 31 | if ((value /= 0.5) < 1) return 0.5 * Math.pow(value, 3); 32 | 33 | return 0.5 * (Math.pow(value - 2, 3) + 2); 34 | }, 35 | 36 | easeInQuart(value) { 37 | return Math.pow(value, 4); 38 | }, 39 | 40 | easeOutQuart(value) { 41 | return -(Math.pow(value - 1, 4) - 1); 42 | }, 43 | 44 | easeInOutQuart(value) { 45 | if ((value /= 0.5) < 1) return 0.5 * Math.pow(value, 4); 46 | 47 | return -0.5 * ((value -= 2) * Math.pow(value, 3) - 2); 48 | }, 49 | 50 | easeInSine(value) { 51 | return -Math.cos(value * MathUtil.PI_2) + 1; 52 | }, 53 | 54 | easeOutSine(value) { 55 | return Math.sin(value * MathUtil.PI_2); 56 | }, 57 | 58 | easeInOutSine(value) { 59 | return -0.5 * (Math.cos(Math.PI * value) - 1); 60 | }, 61 | 62 | easeInExpo(value) { 63 | return value === 0 ? 0 : Math.pow(2, 10 * (value - 1)); 64 | }, 65 | 66 | easeOutExpo(value) { 67 | return value === 1 ? 1 : -Math.pow(2, -10 * value) + 1; 68 | }, 69 | 70 | easeInOutExpo(value) { 71 | if (value === 0) return 0; 72 | 73 | if (value === 1) return 1; 74 | 75 | if ((value /= 0.5) < 1) return 0.5 * Math.pow(2, 10 * (value - 1)); 76 | 77 | return 0.5 * (-Math.pow(2, -10 * --value) + 2); 78 | }, 79 | 80 | easeInCirc(value) { 81 | return -(Math.sqrt(1 - value * value) - 1); 82 | }, 83 | 84 | easeOutCirc(value) { 85 | return Math.sqrt(1 - Math.pow(value - 1, 2)); 86 | }, 87 | 88 | easeInOutCirc(value) { 89 | if ((value /= 0.5) < 1) return -0.5 * (Math.sqrt(1 - value * value) - 1); 90 | return 0.5 * (Math.sqrt(1 - (value -= 2) * value) + 1); 91 | }, 92 | 93 | easeInBack(value) { 94 | let s = 1.70158; 95 | return value * value * ((s + 1) * value - s); 96 | }, 97 | 98 | easeOutBack(value) { 99 | let s = 1.70158; 100 | return (value = value - 1) * value * ((s + 1) * value + s) + 1; 101 | }, 102 | 103 | easeInOutBack(value) { 104 | let s = 1.70158; 105 | if ((value /= 0.5) < 1) return 0.5 * (value * value * (((s *= 1.525) + 1) * value - s)); 106 | return 0.5 * ((value -= 2) * value * (((s *= 1.525) + 1) * value + s) + 2); 107 | }, 108 | 109 | getEasing(ease) { 110 | if (typeof ease === "function") return ease; 111 | else return this[ease] || this.easeLinear; 112 | } 113 | }; 114 | -------------------------------------------------------------------------------- /src/render/BaseRenderer.js: -------------------------------------------------------------------------------- 1 | import Pool from "../core/Pool"; 2 | 3 | export default class BaseRenderer { 4 | constructor(element, stroke) { 5 | this.pool = new Pool(); 6 | this.element = element; 7 | this.stroke = stroke; 8 | this.circleConf = { isCircle: true }; 9 | 10 | this.initEventHandler(); 11 | this.name = "BaseRenderer"; 12 | } 13 | 14 | setStroke(color = "#000000", thinkness = 1) { 15 | this.stroke = { color, thinkness }; 16 | } 17 | 18 | initEventHandler() { 19 | this._protonUpdateHandler = () => { 20 | this.onProtonUpdate.call(this); 21 | }; 22 | 23 | this._protonUpdateAfterHandler = () => { 24 | this.onProtonUpdateAfter.call(this); 25 | }; 26 | 27 | this._emitterAddedHandler = emitter => { 28 | this.onEmitterAdded.call(this, emitter); 29 | }; 30 | 31 | this._emitterRemovedHandler = emitter => { 32 | this.onEmitterRemoved.call(this, emitter); 33 | }; 34 | 35 | this._particleCreatedHandler = particle => { 36 | this.onParticleCreated.call(this, particle); 37 | }; 38 | 39 | this._particleUpdateHandler = particle => { 40 | this.onParticleUpdate.call(this, particle); 41 | }; 42 | 43 | this._particleDeadHandler = particle => { 44 | this.onParticleDead.call(this, particle); 45 | }; 46 | } 47 | 48 | init(proton) { 49 | this.parent = proton; 50 | 51 | proton.addEventListener("PROTON_UPDATE", this._protonUpdateHandler); 52 | proton.addEventListener("PROTON_UPDATE_AFTER", this._protonUpdateAfterHandler); 53 | 54 | proton.addEventListener("EMITTER_ADDED", this._emitterAddedHandler); 55 | proton.addEventListener("EMITTER_REMOVED", this._emitterRemovedHandler); 56 | 57 | proton.addEventListener("PARTICLE_CREATED", this._particleCreatedHandler); 58 | proton.addEventListener("PARTICLE_UPDATE", this._particleUpdateHandler); 59 | proton.addEventListener("PARTICLE_DEAD", this._particleDeadHandler); 60 | } 61 | 62 | resize(width, height) {} 63 | 64 | destroy() { 65 | this.remove(); 66 | this.pool.destroy(); 67 | this.pool = null; 68 | this.element = null; 69 | this.stroke = null; 70 | } 71 | 72 | remove(proton) { 73 | this.parent.removeEventListener("PROTON_UPDATE", this._protonUpdateHandler); 74 | this.parent.removeEventListener("PROTON_UPDATE_AFTER", this._protonUpdateAfterHandler); 75 | 76 | this.parent.removeEventListener("EMITTER_ADDED", this._emitterAddedHandler); 77 | this.parent.removeEventListener("EMITTER_REMOVED", this._emitterRemovedHandler); 78 | 79 | this.parent.removeEventListener("PARTICLE_CREATED", this._particleCreatedHandler); 80 | this.parent.removeEventListener("PARTICLE_UPDATE", this._particleUpdateHandler); 81 | this.parent.removeEventListener("PARTICLE_DEAD", this._particleDeadHandler); 82 | 83 | this.parent = null; 84 | } 85 | 86 | onProtonUpdate() {} 87 | onProtonUpdateAfter() {} 88 | 89 | onEmitterAdded(emitter) {} 90 | onEmitterRemoved(emitter) {} 91 | 92 | onParticleCreated(particle) {} 93 | onParticleUpdate(particle) {} 94 | onParticleDead(particle) {} 95 | } 96 | -------------------------------------------------------------------------------- /src/render/CustomRenderer.js: -------------------------------------------------------------------------------- 1 | import BaseRenderer from "./BaseRenderer"; 2 | 3 | /** 4 | * Represents a custom renderer that extends the BaseRenderer. 5 | * @extends BaseRenderer 6 | */ 7 | export default class CustomRenderer extends BaseRenderer { 8 | /** 9 | * Creates a new CustomRenderer instance. 10 | * @param {HTMLElement} element - The HTML element to render to. 11 | */ 12 | constructor(element) { 13 | super(element); 14 | 15 | /** 16 | * The name of the renderer. 17 | * @type {string} 18 | */ 19 | this.name = "CustomRenderer"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/render/DomRenderer.js: -------------------------------------------------------------------------------- 1 | import DomUtil from "../utils/DomUtil"; 2 | import ImgUtil from "../utils/ImgUtil"; 3 | import BaseRenderer from "./BaseRenderer"; 4 | 5 | /** 6 | * Represents a DOM-based renderer for particle systems. 7 | * @extends BaseRenderer 8 | */ 9 | export default class DomRenderer extends BaseRenderer { 10 | /** 11 | * Creates a new DomRenderer instance. 12 | * @param {HTMLElement} element - The HTML element to render to. 13 | */ 14 | constructor(element) { 15 | super(element); 16 | 17 | this.stroke = null; 18 | this.transform3d = false; 19 | this.pool.create = (body, particle) => this.createBody(body, particle); 20 | this.addImg2Body = this.addImg2Body.bind(this); 21 | 22 | this.name = "DomRenderer"; 23 | } 24 | 25 | onParticleCreated(particle) { 26 | if (particle.body) { 27 | ImgUtil.getImgFromCache(particle.body, this.addImg2Body, particle); 28 | } else { 29 | particle.body = this.pool.get(this.circleConf, particle); 30 | this.element.appendChild(particle.body); 31 | } 32 | } 33 | 34 | onParticleUpdate(particle) { 35 | if (this.bodyReady(particle)) { 36 | if (this.transform3d) { 37 | DomUtil.transform3d(particle.body, particle.p.x, particle.p.y, particle.scale, particle.rotation); 38 | } else { 39 | DomUtil.transform(particle.body, particle.p.x, particle.p.y, particle.scale, particle.rotation); 40 | } 41 | 42 | particle.body.style.opacity = particle.alpha; 43 | 44 | if (particle.body.isCircle) { 45 | particle.body.style.backgroundColor = particle.color || "#ff0000"; 46 | } 47 | } 48 | } 49 | 50 | onParticleDead(particle) { 51 | if (this.bodyReady(particle)) { 52 | this.element.removeChild(particle.body); 53 | this.pool.expire(particle.body); 54 | particle.body = null; 55 | } 56 | } 57 | 58 | bodyReady(particle) { 59 | return typeof particle.body === "object" && particle.body && !particle.body.isInner; 60 | } 61 | 62 | // private method 63 | addImg2Body(img, particle) { 64 | if (particle.dead) return; 65 | particle.body = this.pool.get(img, particle); 66 | DomUtil.resize(particle.body, img.width, img.height); 67 | 68 | this.element.appendChild(particle.body); 69 | } 70 | 71 | createBody(body, particle) { 72 | if (body.isCircle) return this.createCircle(particle); 73 | return this.createSprite(body, particle); 74 | } 75 | 76 | // private methods 77 | createCircle(particle) { 78 | const dom = DomUtil.createDiv(`${particle.id}_dom`, 2 * particle.radius, 2 * particle.radius); 79 | dom.style.borderRadius = `${particle.radius}px`; 80 | 81 | if (this.stroke) { 82 | dom.style.borderColor = this.stroke.color; 83 | dom.style.borderWidth = `${this.stroke.thinkness}px`; 84 | } 85 | dom.isCircle = true; 86 | 87 | return dom; 88 | } 89 | 90 | createSprite(body, particle) { 91 | const url = typeof body === "string" ? body : body.src; 92 | const dom = DomUtil.createDiv(`${particle.id}_dom`, body.width, body.height); 93 | dom.style.backgroundImage = `url(${url})`; 94 | 95 | return dom; 96 | } 97 | 98 | /** 99 | * Destroys the renderer and cleans up resources. 100 | */ 101 | destroy() { 102 | super.destroy(); 103 | this.stroke = null; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/render/EaselRenderer.js: -------------------------------------------------------------------------------- 1 | import Types from "../utils/Types"; 2 | import BaseRenderer from "./BaseRenderer"; 3 | 4 | export default class EaselRenderer extends BaseRenderer { 5 | constructor(element, stroke) { 6 | super(element); 7 | 8 | this.stroke = stroke; 9 | this.name = "EaselRenderer"; 10 | } 11 | 12 | onParticleCreated(particle) { 13 | if (particle.body) { 14 | this.createSprite(particle); 15 | } else { 16 | this.createCircle(particle); 17 | } 18 | 19 | this.element.addChild(particle.body); 20 | } 21 | 22 | onParticleUpdate(particle) { 23 | if (particle.body) { 24 | particle.body.x = particle.p.x; 25 | particle.body.y = particle.p.y; 26 | 27 | particle.body.alpha = particle.alpha; 28 | particle.body.scaleX = particle.body.scaleY = particle.scale; 29 | particle.body.rotation = particle.rotation; 30 | } 31 | } 32 | 33 | onParticleDead(particle) { 34 | if (particle.body) { 35 | particle.body.parent && particle.body.parent.removeChild(particle.body); 36 | this.pool.expire(particle.body); 37 | particle.body = null; 38 | } 39 | 40 | if (particle.graphics) this.pool.expire(particle.graphics); 41 | } 42 | 43 | // private 44 | createSprite(particle) { 45 | particle.body = this.pool.get(particle.body); 46 | 47 | if (particle.body.parent) return; 48 | if (particle.body["image"]) { 49 | particle.body.regX = particle.body.image.width / 2; 50 | particle.body.regY = particle.body.image.height / 2; 51 | } 52 | } 53 | 54 | createCircle(particle) { 55 | const graphics = this.pool.get(window.createjs.Graphics); 56 | 57 | if (this.stroke) { 58 | if (Types.isString(this.stroke)) { 59 | graphics.beginStroke(this.stroke); 60 | } else { 61 | graphics.beginStroke("#000000"); 62 | } 63 | } 64 | graphics.beginFill(particle.color || "#ff0000").drawCircle(0, 0, particle.radius); 65 | const shape = this.pool.get(window.createjs.Shape, [graphics]); 66 | 67 | particle.body = shape; 68 | particle.graphics = graphics; 69 | } 70 | 71 | destroy() { 72 | super.destroy(); 73 | this.stroke = null; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/render/PixelRenderer.js: -------------------------------------------------------------------------------- 1 | import Rectangle from "../math/Rectangle"; 2 | import BaseRenderer from "./BaseRenderer"; 3 | 4 | /** 5 | * Represents a pixel-based renderer for particle systems. 6 | * @extends BaseRenderer 7 | */ 8 | export default class PixelRenderer extends BaseRenderer { 9 | /** 10 | * Creates a new PixelRenderer instance. 11 | * @param {HTMLCanvasElement} element - The canvas element to render to. 12 | * @param {Rectangle} [rectangle] - The rectangle defining the rendering area. 13 | */ 14 | constructor(element, rectangle) { 15 | super(element); 16 | 17 | this.context = this.element.getContext("2d"); 18 | this.imageData = null; 19 | this.rectangle = rectangle; 20 | this.createImageData(rectangle); 21 | 22 | this.name = "PixelRenderer"; 23 | } 24 | 25 | resize(width, height) { 26 | this.element.width = width; 27 | this.element.height = height; 28 | } 29 | 30 | createImageData(rectangle) { 31 | this.rectangle = rectangle ? rectangle : new Rectangle(0, 0, this.element.width, this.element.height); 32 | this.imageData = this.context.createImageData(this.rectangle.width, this.rectangle.height); 33 | this.context.putImageData(this.imageData, this.rectangle.x, this.rectangle.y); 34 | } 35 | 36 | onProtonUpdate() { 37 | this.context.clearRect(this.rectangle.x, this.rectangle.y, this.rectangle.width, this.rectangle.height); 38 | this.imageData = this.context.getImageData( 39 | this.rectangle.x, 40 | this.rectangle.y, 41 | this.rectangle.width, 42 | this.rectangle.height 43 | ); 44 | } 45 | 46 | onProtonUpdateAfter() { 47 | this.context.putImageData(this.imageData, this.rectangle.x, this.rectangle.y); 48 | } 49 | 50 | onParticleCreated(particle) {} 51 | 52 | onParticleUpdate(particle) { 53 | if (this.imageData) { 54 | this.setPixel( 55 | this.imageData, 56 | (particle.p.x - this.rectangle.x) >> 0, 57 | (particle.p.y - this.rectangle.y) >> 0, 58 | particle 59 | ); 60 | } 61 | } 62 | 63 | setPixel(imagedata, x, y, particle) { 64 | const rgb = particle.rgb; 65 | if (x < 0 || x > this.element.width || y < 0 || y > this.element.height) return; 66 | 67 | const i = ((y >> 0) * imagedata.width + (x >> 0)) * 4; 68 | imagedata.data[i] = rgb.r; 69 | imagedata.data[i + 1] = rgb.g; 70 | imagedata.data[i + 2] = rgb.b; 71 | imagedata.data[i + 3] = particle.alpha * 255; 72 | } 73 | 74 | onParticleDead(particle) {} 75 | 76 | /** 77 | * Destroys the renderer and cleans up resources. 78 | */ 79 | destroy() { 80 | super.destroy(); 81 | this.stroke = null; 82 | this.context = null; 83 | this.imageData = null; 84 | this.rectangle = null; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/utils/ColorUtil.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * @typedef {Object} rgbObject 4 | * @property {Number} r red value 5 | * @property {Number} g green value 6 | * @property {Number} b blue value 7 | */ 8 | /** 9 | * converts a hex value to a rgb object 10 | * 11 | * @memberof Proton#Proton.Util 12 | * @method hexToRgb 13 | * 14 | * @param {String} h any hex value, e.g. #000000 or 000000 for black 15 | * 16 | * @return {rgbObject} 17 | */ 18 | hexToRgb(h) { 19 | const hex16 = h.charAt(0) === "#" ? h.substring(1, 7) : h; 20 | const r = parseInt(hex16.substring(0, 2), 16); 21 | const g = parseInt(hex16.substring(2, 4), 16); 22 | const b = parseInt(hex16.substring(4, 6), 16); 23 | 24 | return { r, g, b }; 25 | }, 26 | 27 | /** 28 | * converts a rgb value to a rgb string 29 | * 30 | * @memberof Proton#Proton.Util 31 | * @method rgbToHex 32 | * 33 | * @param {Object | Proton.hexToRgb} rgb a rgb object like in {@link Proton#Proton.} 34 | * 35 | * @return {String} rgb() 36 | */ 37 | rgbToHex(rbg) { 38 | return `rgb(${rbg.r}, ${rbg.g}, ${rbg.b})`; 39 | }, 40 | 41 | getHex16FromParticle(p) { 42 | return Number(p.rgb.r) * 65536 + Number(p.rgb.g) * 256 + Number(p.rgb.b); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/utils/DomUtil.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * Creates and returns a new canvas. The opacity is by default set to 0 4 | * 5 | * @memberof Proton#Proton.DomUtil 6 | * @method createCanvas 7 | * 8 | * @param {String} $id the canvas' id 9 | * @param {Number} $width the canvas' width 10 | * @param {Number} $height the canvas' height 11 | * @param {String} [$position=absolute] the canvas' position, default is 'absolute' 12 | * 13 | * @return {Object} 14 | */ 15 | createCanvas(id, width, height, position = "absolute") { 16 | const dom = document.createElement("canvas"); 17 | 18 | dom.id = id; 19 | dom.width = width; 20 | dom.height = height; 21 | dom.style.opacity = 0; 22 | dom.style.position = position; 23 | this.transform(dom, -500, -500, 0, 0); 24 | 25 | return dom; 26 | }, 27 | 28 | createDiv(id, width, height) { 29 | const dom = document.createElement("div"); 30 | 31 | dom.id = id; 32 | dom.style.position = "absolute"; 33 | this.resize(dom, width, height); 34 | 35 | return dom; 36 | }, 37 | 38 | resize(dom, width, height) { 39 | dom.style.width = width + "px"; 40 | dom.style.height = height + "px"; 41 | dom.style.marginLeft = -width / 2 + "px"; 42 | dom.style.marginTop = -height / 2 + "px"; 43 | }, 44 | 45 | /** 46 | * Adds a transform: translate(), scale(), rotate() to a given div dom for all browsers 47 | * 48 | * @memberof Proton#Proton.DomUtil 49 | * @method transform 50 | * 51 | * @param {HTMLDivElement} div 52 | * @param {Number} $x 53 | * @param {Number} $y 54 | * @param {Number} $scale 55 | * @param {Number} $rotate 56 | */ 57 | transform(div, x, y, scale, rotate) { 58 | div.style.willChange = "transform"; 59 | const transform = `translate(${x}px, ${y}px) scale(${scale}) rotate(${rotate}deg)`; 60 | this.css3(div, "transform", transform); 61 | }, 62 | 63 | transform3d(div, x, y, scale, rotate) { 64 | div.style.willChange = "transform"; 65 | const transform = `translate3d(${x}px, ${y}px, 0) scale(${scale}) rotate(${rotate}deg)`; 66 | this.css3(div, "backfaceVisibility", "hidden"); 67 | this.css3(div, "transform", transform); 68 | }, 69 | 70 | css3(div, key, val) { 71 | const bkey = key.charAt(0).toUpperCase() + key.substr(1); 72 | 73 | div.style[`Webkit${bkey}`] = val; 74 | div.style[`Moz${bkey}`] = val; 75 | div.style[`O${bkey}`] = val; 76 | div.style[`ms${bkey}`] = val; 77 | div.style[`${key}`] = val; 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /src/utils/ImgUtil.js: -------------------------------------------------------------------------------- 1 | import WebGLUtil from "./WebGLUtil"; 2 | import DomUtil from "./DomUtil"; 3 | 4 | const imgsCache = {}; 5 | const canvasCache = {}; 6 | let canvasId = 0; 7 | 8 | export default { 9 | /** 10 | * This will get the image data. It could be necessary to create a Proton.Zone. 11 | * 12 | * @memberof Proton#Proton.Util 13 | * @method getImageData 14 | * 15 | * @param {HTMLCanvasElement} context any canvas, must be a 2dContext 'canvas.getContext('2d')' 16 | * @param {Object} image could be any dom image, e.g. document.getElementById('thisIsAnImgTag'); 17 | * @param {Proton.Rectangle} rect 18 | */ 19 | getImageData(context, image, rect) { 20 | context.drawImage(image, rect.x, rect.y); 21 | const imagedata = context.getImageData(rect.x, rect.y, rect.width, rect.height); 22 | context.clearRect(rect.x, rect.y, rect.width, rect.height); 23 | 24 | return imagedata; 25 | }, 26 | 27 | /** 28 | * @memberof Proton#Proton.Util 29 | * @method getImgFromCache 30 | * 31 | * @todo add description 32 | * @todo describe func 33 | * 34 | * @param {Mixed} img 35 | * @param {Proton.Particle} particle 36 | * @param {Boolean} drawCanvas set to true if a canvas should be saved into particle.data.canvas 37 | * @param {Boolean} func 38 | */ 39 | getImgFromCache(img, callback, param) { 40 | const src = typeof img === "string" ? img : img.src; 41 | 42 | if (imgsCache[src]) { 43 | callback(imgsCache[src], param); 44 | } else { 45 | const image = new Image(); 46 | image.onload = e => { 47 | imgsCache[src] = e.target; 48 | callback(imgsCache[src], param); 49 | }; 50 | 51 | image.src = src; 52 | } 53 | }, 54 | 55 | getCanvasFromCache(img, callback, param) { 56 | const src = img.src; 57 | 58 | if (!canvasCache[src]) { 59 | const width = WebGLUtil.nhpot(img.width); 60 | const height = WebGLUtil.nhpot(img.height); 61 | 62 | const canvas = DomUtil.createCanvas(`proton_canvas_cache_${++canvasId}`, width, height); 63 | const context = canvas.getContext("2d"); 64 | context.drawImage(img, 0, 0, img.width, img.height); 65 | 66 | canvasCache[src] = canvas; 67 | } 68 | 69 | callback && callback(canvasCache[src], param); 70 | 71 | return canvasCache[src]; 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /src/utils/MStack.js: -------------------------------------------------------------------------------- 1 | import Mat3 from "../math/Mat3"; 2 | 3 | export default class MStack { 4 | constructor() { 5 | this.mats = []; 6 | this.size = 0; 7 | 8 | for (let i = 0; i < 20; i++) this.mats.push(Mat3.create([0, 0, 0, 0, 0, 0, 0, 0, 0])); 9 | } 10 | 11 | set(m, i) { 12 | if (i === 0) Mat3.set(m, this.mats[0]); 13 | else Mat3.multiply(this.mats[i - 1], m, this.mats[i]); 14 | 15 | this.size = Math.max(this.size, i + 1); 16 | } 17 | 18 | push(m) { 19 | if (this.size === 0) Mat3.set(m, this.mats[0]); 20 | else Mat3.multiply(this.mats[this.size - 1], m, this.mats[this.size]); 21 | 22 | this.size++; 23 | } 24 | 25 | pop() { 26 | if (this.size > 0) this.size--; 27 | } 28 | 29 | top() { 30 | return this.mats[this.size - 1]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils/PropUtil.js: -------------------------------------------------------------------------------- 1 | import Span from "../math/Span"; 2 | 3 | export default { 4 | hasProp(target, key) { 5 | if (!target) return false; 6 | return target[key] !== undefined; 7 | // return obj.hasOwnProperty(key); 8 | }, 9 | 10 | /** 11 | * set the prototype in a given prototypeObject 12 | * 13 | * @memberof Proton#Proton.Util 14 | * @method setProp 15 | * 16 | * @todo add description for param `target` 17 | * @todo translate desription from chinese to english 18 | * 19 | * @param {Object} target 20 | * @param {Object} prototypeObject An object of single prototypes 21 | * 22 | * @return {Object} target 23 | */ 24 | setProp(target, props) { 25 | for (let prop in props) { 26 | if (target.hasOwnProperty(prop)) { 27 | target[prop] = Span.getSpanValue(props[prop]); 28 | } 29 | } 30 | 31 | return target; 32 | }, 33 | 34 | /** 35 | * @memberof Proton#Proton.Util 36 | * @method setVectorVal 37 | * 38 | * @todo add description for param `target` 39 | * @todo add description for param `conf` 40 | * @todo add description for function 41 | * 42 | * @param {Object} target 43 | * @param {Object} conf 44 | */ 45 | setVectorVal(particle, conf = null) { 46 | if (!conf) return; 47 | 48 | if (this.hasProp(conf, "x")) particle.p.x = conf["x"]; 49 | if (this.hasProp(conf, "y")) particle.p.y = conf["y"]; 50 | 51 | if (this.hasProp(conf, "vx")) particle.v.x = conf["vx"]; 52 | if (this.hasProp(conf, "vy")) particle.v.y = conf["vy"]; 53 | 54 | if (this.hasProp(conf, "ax")) particle.a.x = conf["ax"]; 55 | if (this.hasProp(conf, "ay")) particle.a.y = conf["ay"]; 56 | 57 | if (this.hasProp(conf, "p")) particle.p.copy(conf["p"]); 58 | if (this.hasProp(conf, "v")) particle.v.copy(conf["v"]); 59 | if (this.hasProp(conf, "a")) particle.a.copy(conf["a"]); 60 | 61 | if (this.hasProp(conf, "position")) particle.p.copy(conf["position"]); 62 | if (this.hasProp(conf, "velocity")) particle.v.copy(conf["velocity"]); 63 | if (this.hasProp(conf, "accelerate")) particle.a.copy(conf["accelerate"]); 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /src/utils/Puid.js: -------------------------------------------------------------------------------- 1 | const idsMap = {}; 2 | 3 | const Puid = { 4 | _index: 0, 5 | _cache: {}, 6 | 7 | id(type) { 8 | if (idsMap[type] === undefined || idsMap[type] === null) idsMap[type] = 0; 9 | return `${type}_${idsMap[type]++}`; 10 | }, 11 | 12 | getId(target) { 13 | let uid = this.getIdFromCache(target); 14 | if (uid) return uid; 15 | 16 | uid = `PUID_${this._index++}`; 17 | this._cache[uid] = target; 18 | return uid; 19 | }, 20 | 21 | getIdFromCache(target) { 22 | let obj, id; 23 | 24 | for (id in this._cache) { 25 | obj = this._cache[id]; 26 | 27 | if (obj === target) return id; 28 | if (this.isBody(obj, target) && obj.src === target.src) return id; 29 | } 30 | 31 | return null; 32 | }, 33 | 34 | isBody(obj, target) { 35 | return typeof obj === "object" && typeof target === "object" && obj.isInner && target.isInner; 36 | }, 37 | 38 | getTarget(uid) { 39 | return this._cache[uid]; 40 | } 41 | }; 42 | 43 | export default Puid; 44 | -------------------------------------------------------------------------------- /src/utils/Rgb.js: -------------------------------------------------------------------------------- 1 | export default class Rgb { 2 | constructor(r = 255, g = 255, b = 255) { 3 | this.r = r; 4 | this.g = g; 5 | this.b = b; 6 | } 7 | 8 | reset() { 9 | this.r = 255; 10 | this.g = 255; 11 | this.b = 255; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/Types.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * Determine whether it is a picture object 4 | * 5 | * @return {boolean} is or no 6 | */ 7 | isImage(obj) { 8 | if (!obj) return false; 9 | if (obj.__isImage) return true; 10 | 11 | const tagName = `${obj.tagName}`.toUpperCase(); 12 | const nodeName = `${obj.nodeName}`.toUpperCase(); 13 | if (nodeName === "IMG" || tagName === "IMG") { 14 | obj.__isImage = true; 15 | return true; 16 | } 17 | 18 | return false; 19 | }, 20 | 21 | /** 22 | * Determine whether it is a string object 23 | * 24 | * @return {boolean} is or no 25 | */ 26 | isString(obj) { 27 | return typeof obj === "string"; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/utils/Util.js: -------------------------------------------------------------------------------- 1 | import ImgUtil from "./ImgUtil"; 2 | 3 | export default { 4 | /** 5 | * Returns the default if the value is null or undefined 6 | * 7 | * @memberof Proton#Proton.Util 8 | * @method initValue 9 | * 10 | * @param {Mixed} value a specific value, could be everything but null or undefined 11 | * @param {Mixed} defaults the default if the value is null or undefined 12 | */ 13 | initValue(value, defaults) { 14 | value = value !== null && value !== undefined ? value : defaults; 15 | return value; 16 | }, 17 | 18 | /** 19 | * Checks if the value is a valid array 20 | * 21 | * @memberof Proton#Proton.Util 22 | * @method isArray 23 | * 24 | * @param {Array} value Any array 25 | * 26 | * @returns {Boolean} 27 | */ 28 | isArray(value) { 29 | return Object.prototype.toString.call(value) === "[object Array]"; 30 | }, 31 | 32 | /** 33 | * Destroyes the given array 34 | * 35 | * @memberof Proton#Proton.Util 36 | * @method emptyArray 37 | * 38 | * @param {Array} array Any array 39 | */ 40 | emptyArray(arr) { 41 | if (arr) arr.length = 0; 42 | }, 43 | 44 | toArray(arr) { 45 | return this.isArray(arr) ? arr : [arr]; 46 | }, 47 | 48 | sliceArray(arr1, index, arr2) { 49 | this.emptyArray(arr2); 50 | for (let i = index; i < arr1.length; i++) { 51 | arr2.push(arr1[i]); 52 | } 53 | }, 54 | 55 | getRandFromArray(arr) { 56 | if (!arr) return null; 57 | return arr[Math.floor(arr.length * Math.random())]; 58 | }, 59 | 60 | /** 61 | * Destroyes the given object 62 | * 63 | * @memberof Proton#Proton.Util 64 | * @method emptyObject 65 | * 66 | * @param {Object} obj Any object 67 | */ 68 | emptyObject(obj, ignore = null) { 69 | for (let key in obj) { 70 | if (ignore && ignore.indexOf(key) > -1) continue; 71 | delete obj[key]; 72 | } 73 | }, 74 | 75 | /** 76 | * Makes an instance of a class and binds the given array 77 | * 78 | * @memberof Proton#Proton.Util 79 | * @method classApply 80 | * 81 | * @param {Function} constructor A class to make an instance from 82 | * @param {Array} [args] Any array to bind it to the constructor 83 | * 84 | * @return {Object} The instance of constructor, optionally bind with args 85 | */ 86 | classApply(constructor, args = null) { 87 | if (!args) { 88 | return new constructor(); 89 | } else { 90 | const FactoryFunc = constructor.bind.apply(constructor, [null].concat(args)); 91 | return new FactoryFunc(); 92 | } 93 | }, 94 | 95 | /** 96 | * This will get the image data. It could be necessary to create a Proton.Zone. 97 | * 98 | * @memberof Proton#Proton.Util 99 | * @method getImageData 100 | * 101 | * @param {HTMLCanvasElement} context any canvas, must be a 2dContext 'canvas.getContext('2d')' 102 | * @param {Object} image could be any dom image, e.g. document.getElementById('thisIsAnImgTag'); 103 | * @param {Proton.Rectangle} rect 104 | */ 105 | getImageData(context, image, rect) { 106 | return ImgUtil.getImageData(context, image, rect); 107 | }, 108 | 109 | destroyAll(arr, param = null) { 110 | let i = arr.length; 111 | 112 | while (i--) { 113 | try { 114 | arr[i].destroy(param); 115 | } catch (e) {} 116 | 117 | delete arr[i]; 118 | } 119 | 120 | arr.length = 0; 121 | }, 122 | 123 | assign(target, source) { 124 | if (typeof Object.assign !== "function") { 125 | for (let key in source) { 126 | if (Object.prototype.hasOwnProperty.call(source, key)) { 127 | target[key] = source[key]; 128 | } 129 | } 130 | 131 | return target; 132 | } else { 133 | return Object.assign(target, source); 134 | } 135 | } 136 | }; 137 | -------------------------------------------------------------------------------- /src/utils/WebGLUtil.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * @memberof Proton#Proton.WebGLUtil 4 | * @method ipot 5 | * 6 | * @todo add description 7 | * @todo add length description 8 | * 9 | * @param {Number} length 10 | * 11 | * @return {Boolean} 12 | */ 13 | ipot(length) { 14 | return (length & (length - 1)) === 0; 15 | }, 16 | 17 | /** 18 | * @memberof Proton#Proton.WebGLUtil 19 | * @method nhpot 20 | * 21 | * @todo add description 22 | * @todo add length description 23 | * 24 | * @param {Number} length 25 | * 26 | * @return {Number} 27 | */ 28 | nhpot(length) { 29 | --length; 30 | for (let i = 1; i < 32; i <<= 1) { 31 | length = length | (length >> i); 32 | } 33 | 34 | return length + 1; 35 | }, 36 | 37 | /** 38 | * @memberof Proton#Proton.WebGLUtil 39 | * @method makeTranslation 40 | * 41 | * @todo add description 42 | * @todo add tx, ty description 43 | * @todo add return description 44 | * 45 | * @param {Number} tx either 0 or 1 46 | * @param {Number} ty either 0 or 1 47 | * 48 | * @return {Object} 49 | */ 50 | makeTranslation(tx, ty) { 51 | return [1, 0, 0, 0, 1, 0, tx, ty, 1]; 52 | }, 53 | 54 | /** 55 | * @memberof Proton#Proton.WebGLUtil 56 | * @method makeRotation 57 | * 58 | * @todo add description 59 | * @todo add return description 60 | * 61 | * @param {Number} angleInRadians 62 | * 63 | * @return {Object} 64 | */ 65 | makeRotation(angleInRadians) { 66 | let c = Math.cos(angleInRadians); 67 | let s = Math.sin(angleInRadians); 68 | 69 | return [c, -s, 0, s, c, 0, 0, 0, 1]; 70 | }, 71 | 72 | /** 73 | * @memberof Proton#Proton.WebGLUtil 74 | * @method makeScale 75 | * 76 | * @todo add description 77 | * @todo add tx, ty description 78 | * @todo add return description 79 | * 80 | * @param {Number} sx either 0 or 1 81 | * @param {Number} sy either 0 or 1 82 | * 83 | * @return {Object} 84 | */ 85 | makeScale(sx, sy) { 86 | return [sx, 0, 0, 0, sy, 0, 0, 0, 1]; 87 | }, 88 | 89 | /** 90 | * @memberof Proton#Proton.WebGLUtil 91 | * @method matrixMultiply 92 | * 93 | * @todo add description 94 | * @todo add a, b description 95 | * @todo add return description 96 | * 97 | * @param {Object} a 98 | * @param {Object} b 99 | * 100 | * @return {Object} 101 | */ 102 | matrixMultiply(a, b) { 103 | let a00 = a[0 * 3 + 0]; 104 | let a01 = a[0 * 3 + 1]; 105 | let a02 = a[0 * 3 + 2]; 106 | let a10 = a[1 * 3 + 0]; 107 | let a11 = a[1 * 3 + 1]; 108 | let a12 = a[1 * 3 + 2]; 109 | let a20 = a[2 * 3 + 0]; 110 | let a21 = a[2 * 3 + 1]; 111 | let a22 = a[2 * 3 + 2]; 112 | let b00 = b[0 * 3 + 0]; 113 | let b01 = b[0 * 3 + 1]; 114 | let b02 = b[0 * 3 + 2]; 115 | let b10 = b[1 * 3 + 0]; 116 | let b11 = b[1 * 3 + 1]; 117 | let b12 = b[1 * 3 + 2]; 118 | let b20 = b[2 * 3 + 0]; 119 | let b21 = b[2 * 3 + 1]; 120 | let b22 = b[2 * 3 + 2]; 121 | 122 | return [ 123 | a00 * b00 + a01 * b10 + a02 * b20, 124 | a00 * b01 + a01 * b11 + a02 * b21, 125 | a00 * b02 + a01 * b12 + a02 * b22, 126 | a10 * b00 + a11 * b10 + a12 * b20, 127 | a10 * b01 + a11 * b11 + a12 * b21, 128 | a10 * b02 + a11 * b12 + a12 * b22, 129 | a20 * b00 + a21 * b10 + a22 * b20, 130 | a20 * b01 + a21 * b11 + a22 * b21, 131 | a20 * b02 + a21 * b12 + a22 * b22 132 | ]; 133 | } 134 | }; 135 | -------------------------------------------------------------------------------- /src/zone/CircleZone.js: -------------------------------------------------------------------------------- 1 | import Zone from "./Zone"; 2 | import MathUtil from "../math/MathUtil"; 3 | 4 | /** 5 | * Represents a circular zone in a 2D space. 6 | * @extends Zone 7 | */ 8 | export default class CircleZone extends Zone { 9 | /** 10 | * Creates a new CircleZone. 11 | * @param {number} x - The x-coordinate of the circle's center. 12 | * @param {number} y - The y-coordinate of the circle's center. 13 | * @param {number} [radius] - The radius of the circle. 14 | */ 15 | constructor(x, y, radius) { 16 | super(); 17 | 18 | this.x = x; 19 | this.y = y; 20 | this.radius = radius; 21 | this.angle = 0; 22 | this.center = { x, y }; 23 | } 24 | 25 | /** 26 | * Gets a random position within the circle. 27 | * @returns {Object} An object with x and y coordinates. 28 | */ 29 | getPosition() { 30 | this.angle = MathUtil.PIx2 * Math.random(); 31 | this.randomRadius = Math.random() * this.radius; 32 | this.vector.x = this.x + this.randomRadius * Math.cos(this.angle); 33 | this.vector.y = this.y + this.randomRadius * Math.sin(this.angle); 34 | 35 | return this.vector; 36 | } 37 | 38 | /** 39 | * Sets the center of the circle. 40 | * @param {number} x - The new x-coordinate of the center. 41 | * @param {number} y - The new y-coordinate of the center. 42 | */ 43 | setCenter(x, y) { 44 | this.center.x = x; 45 | this.center.y = y; 46 | } 47 | 48 | /** 49 | * Handles particle crossing behavior. 50 | * @param {Object} particle - The particle to check for crossing. 51 | */ 52 | crossing(particle) { 53 | const d = particle.p.distanceTo(this.center); 54 | 55 | if (this.crossType === "dead") { 56 | if (d - particle.radius > this.radius) particle.dead = true; 57 | } else if (this.crossType === "bound") { 58 | if (d + particle.radius >= this.radius) this.getSymmetric(particle); 59 | } else if (this.crossType === "cross") { 60 | if (this.alert) { 61 | console.error("Sorry, CircleZone does not support cross method!"); 62 | this.alert = false; 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Calculates the symmetric position of a particle. 69 | * @param {Object} particle - The particle to calculate symmetry for. 70 | */ 71 | getSymmetric(particle) { 72 | const tha2 = particle.v.getGradient(); 73 | const tha1 = this.getGradient(particle); 74 | 75 | const tha = 2 * (tha1 - tha2); 76 | const oldx = particle.v.x; 77 | const oldy = particle.v.y; 78 | 79 | particle.v.x = oldx * Math.cos(tha) - oldy * Math.sin(tha); 80 | particle.v.y = oldx * Math.sin(tha) + oldy * Math.cos(tha); 81 | } 82 | 83 | /** 84 | * Calculates the gradient for a particle. 85 | * @param {Object} particle - The particle to calculate the gradient for. 86 | * @returns {number} The calculated gradient. 87 | */ 88 | getGradient(particle) { 89 | return -MathUtil.PI_2 + Math.atan2(particle.p.y - this.center.y, particle.p.x - this.center.x); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/zone/ImageZone.js: -------------------------------------------------------------------------------- 1 | import Zone from "./Zone"; 2 | import Util from "../utils/Util"; 3 | 4 | /** 5 | * Represents a zone based on image data. 6 | * @extends Zone 7 | */ 8 | export default class ImageZone extends Zone { 9 | /** 10 | * Creates an ImageZone. 11 | * @param {ImageData} imageData - The image data to use for the zone. 12 | * @param {number} [x=0] - The x-coordinate offset. 13 | * @param {number} [y=0] - The y-coordinate offset. 14 | * @param {number} [d=2] - The sampling density. 15 | */ 16 | constructor(imageData, x, y, d) { 17 | super(); 18 | this.reset(imageData, x, y, d); 19 | } 20 | 21 | /** 22 | * Resets the ImageZone with new parameters. 23 | * @param {ImageData} imageData - The image data to use for the zone. 24 | * @param {number} [x=0] - The x-coordinate offset. 25 | * @param {number} [y=0] - The y-coordinate offset. 26 | * @param {number} [d=2] - The sampling density. 27 | */ 28 | reset(imageData, x, y, d) { 29 | this.imageData = imageData; 30 | this.x = Util.initValue(x, 0); 31 | this.y = Util.initValue(y, 0); 32 | this.d = Util.initValue(d, 2); 33 | 34 | this.vectors = []; 35 | this.setVectors(); 36 | } 37 | 38 | /** 39 | * Sets up vectors based on the image data. 40 | * @returns {Object} The vector object. 41 | */ 42 | setVectors() { 43 | let i, j; 44 | const length1 = this.imageData.width; 45 | const length2 = this.imageData.height; 46 | 47 | for (i = 0; i < length1; i += this.d) { 48 | for (j = 0; j < length2; j += this.d) { 49 | let index = ((j >> 0) * length1 + (i >> 0)) * 4; 50 | 51 | if (this.imageData.data[index + 3] > 0) { 52 | this.vectors.push({ x: i + this.x, y: j + this.y }); 53 | } 54 | } 55 | } 56 | 57 | return this.vector; 58 | } 59 | 60 | /** 61 | * Checks if a point is within the bounds of the image. 62 | * @param {number} x - The x-coordinate to check. 63 | * @param {number} y - The y-coordinate to check. 64 | * @returns {boolean} True if the point is within bounds, false otherwise. 65 | */ 66 | getBound(x, y) { 67 | const index = ((y >> 0) * this.imageData.width + (x >> 0)) * 4; 68 | return this.imageData.data[index + 3] > 0; 69 | } 70 | 71 | /** 72 | * Gets a random position within the image zone. 73 | * @returns {Object} A vector representing the position. 74 | */ 75 | getPosition() { 76 | const vector = Util.getRandFromArray(this.vectors); 77 | return this.vector.copy(vector); 78 | } 79 | 80 | /** 81 | * Gets the color at a specific point in the image. 82 | * @param {number} x - The x-coordinate. 83 | * @param {number} y - The y-coordinate. 84 | * @returns {Object} An object containing r, g, b, and a values. 85 | */ 86 | getColor(x, y) { 87 | x -= this.x; 88 | y -= this.y; 89 | const i = ((y >> 0) * this.imageData.width + (x >> 0)) * 4; 90 | 91 | return { 92 | r: this.imageData.data[i], 93 | g: this.imageData.data[i + 1], 94 | b: this.imageData.data[i + 2], 95 | a: this.imageData.data[i + 3] 96 | }; 97 | } 98 | 99 | /** 100 | * Handles particle crossing behavior. 101 | * @param {Object} particle - The particle to check for crossing. 102 | */ 103 | crossing(particle) { 104 | if (this.crossType === "dead") { 105 | particle.dead = this.getBound(particle.p.x - this.x, particle.p.y - this.y); 106 | } else if (this.crossType === "bound") { 107 | if (!this.getBound(particle.p.x - this.x, particle.p.y - this.y)) particle.v.negate(); 108 | } 109 | } 110 | 111 | /** 112 | * Destroys the ImageZone and cleans up resources. 113 | */ 114 | destroy() { 115 | super.destroy(); 116 | this.imageData = null; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/zone/PointZone.js: -------------------------------------------------------------------------------- 1 | import Zone from "./Zone"; 2 | 3 | /** 4 | * Represents a point zone in a 2D space. 5 | * @extends Zone 6 | */ 7 | export default class PointZone extends Zone { 8 | /** 9 | * Creates a new PointZone. 10 | * @param {number} x - The x-coordinate of the point. 11 | * @param {number} y - The y-coordinate of the point. 12 | */ 13 | constructor(x, y) { 14 | super(); 15 | 16 | /** 17 | * The x-coordinate of the point. 18 | * @type {number} 19 | */ 20 | this.x = x; 21 | 22 | /** 23 | * The y-coordinate of the point. 24 | * @type {number} 25 | */ 26 | this.y = y; 27 | } 28 | 29 | /** 30 | * Gets the position of the point. 31 | * @returns {Object} An object representing the position vector. 32 | */ 33 | getPosition() { 34 | this.vector.x = this.x; 35 | this.vector.y = this.y; 36 | 37 | return this.vector; 38 | } 39 | 40 | /** 41 | * This method is not supported for PointZone. 42 | * @param {Object} particle - The particle object (unused). 43 | */ 44 | crossing(particle) { 45 | if (this.alert) { 46 | console.error("Sorry, PointZone does not support crossing method!"); 47 | this.alert = false; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/zone/RectZone.js: -------------------------------------------------------------------------------- 1 | import Zone from "./Zone"; 2 | 3 | /** 4 | * Represents a rectangular zone for particle systems. 5 | * @extends Zone 6 | */ 7 | export default class RectZone extends Zone { 8 | /** 9 | * Creates a new RectZone. 10 | * @param {number} x - The x-coordinate of the top-left corner of the rectangle. 11 | * @param {number} y - The y-coordinate of the top-left corner of the rectangle. 12 | * @param {number} [width] - The width of the rectangle. 13 | * @param {number} [height] - The height of the rectangle. 14 | */ 15 | constructor(x, y, width = 200, height = 200) { 16 | super(); 17 | 18 | this.x = x; 19 | this.y = y; 20 | this.width = width; 21 | this.height = height; 22 | } 23 | 24 | /** 25 | * Gets a random position within the rectangular zone. 26 | * @returns {Vector2D} A vector representing the random position. 27 | */ 28 | getPosition() { 29 | this.vector.x = this.x + Math.random() * this.width; 30 | this.vector.y = this.y + Math.random() * this.height; 31 | return this.vector; 32 | } 33 | 34 | /** 35 | * Handles particle crossing behavior based on the crossType. 36 | * @param {Particle} particle - The particle to check for crossing. 37 | */ 38 | crossing(particle) { 39 | // particle dead zone 40 | if (this.crossType === "dead") { 41 | if (particle.p.x + particle.radius < this.x) particle.dead = true; 42 | else if (particle.p.x - particle.radius > this.x + this.width) particle.dead = true; 43 | 44 | if (particle.p.y + particle.radius < this.y) particle.dead = true; 45 | else if (particle.p.y - particle.radius > this.y + this.height) particle.dead = true; 46 | } 47 | 48 | // particle bound zone 49 | else if (this.crossType === "bound") { 50 | if (particle.p.x - particle.radius < this.x) { 51 | particle.p.x = this.x + particle.radius; 52 | particle.v.x *= -1; 53 | } else if (particle.p.x + particle.radius > this.x + this.width) { 54 | particle.p.x = this.x + this.width - particle.radius; 55 | particle.v.x *= -1; 56 | } 57 | 58 | if (particle.p.y - particle.radius < this.y) { 59 | particle.p.y = this.y + particle.radius; 60 | particle.v.y *= -1; 61 | } else if (particle.p.y + particle.radius > this.y + this.height) { 62 | particle.p.y = this.y + this.height - particle.radius; 63 | particle.v.y *= -1; 64 | } 65 | } 66 | 67 | // particle cross zone 68 | else if (this.crossType === "cross") { 69 | if (particle.p.x + particle.radius < this.x && particle.v.x <= 0) { 70 | particle.p.x = this.x + this.width + particle.radius; 71 | } else if (particle.p.x - particle.radius > this.x + this.width && particle.v.x >= 0) { 72 | particle.p.x = this.x - particle.radius; 73 | } 74 | 75 | if (particle.p.y + particle.radius < this.y && particle.v.y <= 0) { 76 | particle.p.y = this.y + this.height + particle.radius; 77 | } else if (particle.p.y - particle.radius > this.y + this.height && particle.v.y >= 0) { 78 | particle.p.y = this.y - particle.radius; 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/zone/Zone.js: -------------------------------------------------------------------------------- 1 | import Vector2D from "../math/Vector2D"; 2 | 3 | export default class Zone { 4 | constructor() { 5 | this.vector = new Vector2D(0, 0); 6 | this.random = 0; 7 | this.crossType = "dead"; 8 | this.alert = true; 9 | } 10 | 11 | getPosition() {} 12 | 13 | crossing(particle) {} 14 | 15 | destroy() { 16 | this.vector = null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "declaration": true, 5 | "emitDeclarationOnly": true, 6 | "outDir": "./build", 7 | "declarationMap": true, 8 | "noEmit": false, 9 | "target": "es2015", 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "esModuleInterop": true, 13 | "strict": true, 14 | "skipLibCheck": true 15 | }, 16 | "include": ["src/**/*"], 17 | "exclude": ["node_modules", "dist", "build"] 18 | } 19 | --------------------------------------------------------------------------------