pixi实现十几种粒子喷射模拟重力弹射动画效果代码
代码语言:html
所属分类:粒子
代码描述:pixi实现十几种粒子喷射模拟重力弹射动画效果代码,点击更换预设就可切换到其他的动画效果。
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style> * { -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Safari */ -khtml-user-select: none; /* Konqueror HTML */ -moz-user-select: none; /* Old versions of Firefox */ -ms-user-select: none; /* Internet Explorer/Edge */ user-select: none; /* Non-prefixed version, currently supported by Chrome, Edge, Opera and Firefox */ } body, html { font-family: Roboto, sans-serif; padding: 0; margin: 0; height: 100vh; line-height: 21px; font-size: 16px; overflow: hidden; } @media only screen and (max-width : 1024px) { body, html { font-size: 14px; line-height: 19px; } .small { font-size: 13px; } .intro select { font-size: 14px; } } input { font-family: Fira Sans; } .small { font-size: 14px; } .red { color: #880000; } .cascade-label { position: absolute; border: 2px solid black; border-radius: 20px; width: 30px; height: 30px; display: flex; justify-content: center; align-items: center; background: white; color: black; font-size: 20px; z-index: 50; visibility: hidden; /*display: none;*/ } #help-container { position: fixed; display: none; padding: 5vh; background: white; border: 3px solid #888; height: 100vh; box-sizing: border-box; overflow: auto; z-index: 500; } #intro-container { display: grid; grid-template-rows: 8fr auto 2fr; grid-template-columns: minmax(2vw, 1fr) 20fr minmax(2vw, 1fr); position: fixed; width: 100vw; height: 100vh; } @media only screen and (max-width : 1024px) { #intro-container { grid-template-columns: 1rem auto 1rem; grid-template-rows: 8fr auto 1rem; } } .intro { grid-row: 2; grid-column: 2; border: 3px solid #AAA; color: #666; background: white; width: 100%; box-sizing: border-box; padding: 2rem 3rem; } @media only screen and (max-width : 1024px) { .intro { padding: 1rem; } } .intro button { margin: 1rem 0; background-color: #800; color: white; font-size: 1rem; height: 2rem; line-height: 2rem; padding: 0 1rem; border-radius: 20px; border: 0; cursor: pointer; } #help-button { text-shadow: 2px 2px 8px #888888; box-shadow: 2px 2px #888888; cursor: pointer; color: white; display: flex; justify-content: center; align-items: center; position: absolute; bottom: 2vh; left: 2vh; width: 30px; height: 30px; border-radius: 30px; border: 2px solid white; } .intro select { font-size: 18px; } </style> </head> <body> <div data-cascade-label='1' class="cascade-label">1</div> <div data-cascade-label='2' class="cascade-label">2</div> <div data-cascade-label='3' class="cascade-label">3</div> <div data-cascade-label='4' class="cascade-label">4</div> <div id="pixi"></div> <canvas id='container' style='position:absolute;' width='1024' height='768'> </canvas> <input style="display: none;" id="input" type='text' value='Cascades' class='input' /> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/pixi.5.1.5.js"></script> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/dat.gui-min.js"></script> <script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/zepto.1.2.0.js"></script> <script> (function ($) { $.extend($.fn, { fadeIn: function (ms) { if (typeof ms === 'undefined') ms = 500; $(this).css( { display: 'block', opacity: 0 }). animate({ opacity: 1 }, ms); return this; }, fadeOut: function (ms) { if (typeof ms === 'undefined') ms = 500; $(this).css( { opacity: 1 }). animate({ opacity: 0 }, ms, 'linear', function () { $(this).css('display', 'none'); }); return this; } }); })(Zepto); const creator = new URLSearchParams(window.location.search).get('creator'); const viewer = new URLSearchParams(window.location.search).get('viewer'); const debug = new URLSearchParams(window.location.search).get('debug'); const circle64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFyGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0MzUyLCAyMDIwLzAxLzMwLTE1OjUwOjM4ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTE1VDE4OjU3OjUxKzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIxLTA0LTE1VDE4OjU3OjUxKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0xNVQxODo1Nzo1MSswMTowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpjN2ZlN2NjYy1iODgzLTdhNDUtOThiYy0yNDY3OWQyODk2MjAiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo4NTE1OTI1MS1mNGZlLWQxNDAtODNlNy05NjNhNjM1NDJmMDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3NTYwNzM3MS00YzU1LTllNDItYjk5ZS1lYWNlMDFmMWM2Y2IiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3NTYwNzM3MS00YzU1LTllNDItYjk5ZS1lYWNlMDFmMWM2Y2IiIHN0RXZ0OndoZW49IjIwMjEtMDQtMTVUMTg6NTc6NTErMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4xIChXaW5kb3dzKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YzdmZTdjY2MtYjg4My03YTQ1LTk4YmMtMjQ2NzlkMjg5NjIwIiBzdEV2dDp3aGVuPSIyMDIxLTA0LTE1VDE4OjU3OjUxKzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+aEZZegAAAXtJREFUWIXNl01Kw0AYhp+2/mwroitXgroveATTI9RTSI+gZ1B6ix7AegTRvRRcifQPTJfGxeuiM2YaC5O0JuMLWYRJ5nlIMpPvq0kiZLYKXLsNtIEL4Bw4BppmLAZegUfgAbgHvnLNKsl3NCVdS5oof6bmnqZvfh/8UtKoADibkZmjsEBDUm8DcDY9M2cugYak/h/CbfqrJFYJ3JYAt7nzCXRKhNt0XGZN6T7QBF6AwwJLc51MgDMWS5e6M9CtAI5hdO2JfQI7wBtwUIEAwBQ4AhL7BKIK4RhWBOkriCqE27RdgVYAgZYrcBpA4ATSj/CTxYdYZRJgt+69rORYgXkA9twVGAYQGLoCTwEEnl2BQQCBASxvxe/AfkXwX1txAtxVBAfoGWaQ3/GUxcYXw/LvOAauSoZjGPHPWbZEMmVTWfGWZP+iKA1elmcbk/EG4LHWbEyyrdmNpFkB8Mzc423N3GXoi21OI9LmdM+MfZA2pwNzJHkmLSJQSr4BvSQwCTtLg5kAAAAASUVORK5CYII="; const square64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFyGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNi4wLWMwMDIgNzkuMTY0MzUyLCAyMDIwLzAxLzMwLTE1OjUwOjM4ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTE1VDE4OjU4OjE4KzAxOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDIxLTA0LTE1VDE4OjU4OjE4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0xNVQxODo1ODoxOCswMTowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplYmM1MzZkNC1mNTFiLWRjNDItOTRiZC04YjYwOWQ3NDAzNmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo0NWEzOWIxYi1jMmZhLThjNDItOGQxYy1mNDc5MTAxNGIxZjEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDozNTMxOGU1OC05OTliLWY0NDctODA1Ny01MzE4OGRjYTZiMWUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDozNTMxOGU1OC05OTliLWY0NDctODA1Ny01MzE4OGRjYTZiMWUiIHN0RXZ0OndoZW49IjIwMjEtMDQtMTVUMTg6NTg6MTgrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4xIChXaW5kb3dzKSIvPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZWJjNTM2ZDQtZjUxYi1kYzQyLTk0YmQtOGI2MDlkNzQwMzZkIiBzdEV2dDp3aGVuPSIyMDIxLTA0LTE1VDE4OjU4OjE4KzAxOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgMjEuMSAoV2luZG93cykiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+HjIhmQAAADtJREFUWIXt17ERACAMAkDjuf/KsTGuQPM0lHxLdfdK5rxOKWqHhn8AAAAAAAAAAAAAAAAAAOYdVwpwAfjWBEJmmnP5AAAAAElFTkSuQmCC"; const star64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB4ElEQVRYhcWWu2sWURBHTx4mGB8kGjEoimhrJ6YSjY0PUOJbQWwsbPwPrLSwTWFnY5U2laCWoo2ghYWdoCCWotgIBuORK/eDddnXd9fsDgzs3js7v8PM7p0dUWlhU8CPNglG2zwM3Abm+gS4CFzoC2AXMA9c6gvgHDACHAV29gUQbCxz3RnANLCQuU9uQyrAGWBD5j7AzHYJkC/5eGobUgA2AicL1i93BXAC2FywfhzY3gVAWanDO7E4bLIwC04Bd2Mfi2wmt7YbmCyJ/VkzGzYBE/H6LXCNMIzUefW93dhv9YE6+XcQRoDgW9RH64zwRV3MaP4DMPAr6td1EH+h7snrFQEE36s+/0/Cv9R76liRVhlA8FH1jrraQvyzulChUQkw8MOJL+hjdbYuf9NfskPAmyaB0daAHcC3usCmB9HpIcQZZkQ3BUgZNI1GdJMW7AM+JgCsxh/WyjY0qcD5ir13wIeSvYlGlWvwFbwsecsfqlPqVnW5JOZJXf468Tl1LZf0u3q1IPZ63MtaOENm2gDcyiV8rR6oiN+vvso9c7MNwLOYJEywpcEEq/Fx9X48goM9TQWYjiUME+xsA+G8H1M/xRzbUgBulE2wITwIr1S1oSrZkVjOVPGsHywF6NWAP10TpwyQiq6rAAAAAElFTkSuQmCC"; const flower64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAEnUlEQVRYhcWXe2jWVRjHP9pmuuksy5ZUZKWh1iATy6j+KzIkyyhXS4nQ6F72V0Ut0swILWzgpQuVkVqRGlKBrQuVFyZqrTJKuikz3TRzbs3m5r5xxvel09nv1TcK+sKP9z2X5znPee4HSYV+4yRdI6mfvwER3SBJ70hqlFT5D3hSyKYSSW/qL7RLOiypTdLDkvpL2hKtH5Q0pFABenN0VAG/Ac8BG4E+0E3XD5gNzAJGR1z6AtcXwLcbvbrVkI2BwDRgKjACKAY2AJ8BdwMDPA4MxgF1wHfASUArcDswD7gU+AKYnHlKhlqC+p6V1CLpd0mLPF5jFQfVPy5pr6S53vOopI1e+0HSnZKW6u+oKMQHSiU1RWSvSGr2/82SZkt6w3MTJG2V9KCkVvXEU8nMDVkCFCUKOQMYHI3bgDL/P9/fdqDGe9cAT9gnUgxKlZ1lgZSwTzI+JoPmdOARO2evPIcHtCSH/pq1KdXAgWTcmYd5wEVeXwdcnLF+AvAYUA4ctJP2RGSPwZJulbQtstvXkl7PsG8OL0i6Ls9ajaTOaPylpGck3SapKHXCMZJ2SOqQ9HRE1CWpOoN5hw9fJ6nWHh+8f5+kBZIekLT6CILXSRoWzg554HJgFVBq9bwP7HIsBxX/AtwDnGrbl9t0V3kuYAuwG6h13gg54j2gBOgAuoBhwBX2G8x/Yrj9nkiyNsf885L+iG47U9J2SfOPcKuA7yW9m2cthOy8ZG5TuMlmSxawGJjuG8SOGm4+xyk5h6XWWjtQ6bkyayILI63dGCOLTDwDGGv1DMgg3ml13u9x8Oq3XA92AOcC5ziHnGiTTkp4rAQmJHP1IYabgZkmOj6P9MG2d0R+kktEy1yIaqK9E10TYmwD9gHDk/mFuSiYbpuEMNmfYb+dtl+7x7skTZE0SdK1DrHY1q/5/yFJyyQtzuD5ci4KKoBNzoKHXGJDOa1IpF0LfAxUe/wp8LN9pNJZM9Df7MTUYH+4DJgS8QmRtcRrc4IAKxN7dVnl9S6pZVbfky7L0/KYCfvFGJvnE8/td4kear5NDtXy7stKWpgnbFpdDRe5FQtVcpYz49uROWSzvSpphs2Vw1z/3uTE1JKcsT5ooMRRcAlwNnBWxs0WAHf5f7PDcTlwJbDe6q/KiKCPfPtvbJZTorVQqKamHdFIe3uxN4Qo+RZotMfHaLKZAvP78phkqwvcaLd0uX1dDumatBpWOu3mUOvQafDNB0ZrZU5cN+ZzCOBzh2Vf0zY68VU7fffoiF5KbJTzj5/cdufQ4O5og6QRkuozfCi0aC9G492Sjj1aR5QvjQ5NMln4/5BNFJLYCnv9ycBeq31sEjHB6w+njFMBVptxFgLzD4E9Trm5TmiyW/PT7GjH2YeyUGpT5hWgznbL9fmxxFW23yhgSEJX6lZtuXmOB85M9jSlhwdkvQv6+ibh+9FZ60gPmOCo/d2i4fr/gb8g6C1uz4LXz+9BXcDzaUWeRNXpbuk8SQcy1q82fbGk4f/mbTjKj48YoVW7N9pzYbLnK0m9/6vHafgukLRK0lo/TMJLOd0z3mk4NLWZr6BMAf5XAH8CklhnAxftqrIAAAAASUVORK5CYII="; const ring64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACHElEQVRYhcWXMU9UQRDHf1zBcUSiPZW5I6EiRloaQ6nVeeY+x30Lz68AvZhopxUhNrREpKLwYmVCqBRPDK/gb5bMkskKd/sO7t0kk7zs7sz/vzv7ZmfmJJEpD4DnwDPgCfAYeGimv4DvwCHwGfgEDLPcBgJjtCVpS9JQ+TI0m9Y4/6MmFyS9lnRRAjiVYNs3Xzfi3BaCJvDejvr6sIA9YB84AI6BHza3DKwC68AGsAnMOdsQmlfAt5wQPJV0kuzknaR2Rriits3Gy4n5HhmCELNTZzSQ1JNULwEetW62A+fvNL0X3qAh6dAt/iKpMwFwqh3zFeWrYf1HoJ/s/D7Ao3aSk+inBJqSCregd4/gUXvOfxFDESe3kgs3Scxz7oS/mNuRwJJLMpclb3tZbRuGDHMpDHYdq90pgkfddXjdmiWNKPu5D8MdxGNsBgJrbuCgAgIeY61maTfKcQUEPEYzvAUXwLwNLAJ/p0ygAZzbd1GbMthYCQTO3KLlCjA9xlkgMHADqxUQ8BiDQODIDaxXQMBjHNWsyIiyUQEBj7E3y1T8J2CHE/gN7BijUEZ1gfoUdl4337FUe3uFbcxaM3iOV/xzTMUFyZs45xc1rFyqoiRbvIkAsy5Ko1Zalt/WmLSAD8lTfZfGJCS7l7mNSdQFu5iFJpdiXGuWc5zhdwlFa0gcuXJuRefVrzZKy7bnL5L2/JHN/Uza849Z7TnwD3cWpkTclzuSAAAAAElFTkSuQmCC"; class Collider { constructor() { // control some internal vars here this.particleCount = 1000; this.hitBoxSize = 2; this.running = false; this.textCanvas = document.getElementById("container"); this.textContext = this.textCanvas.getContext('2d'); //this.cWidth = this.textCanvas.width; //this.cHeight = this.textCanvas.height; this.loadedFontFaces = []; this.fontFace = null; this.initParams(); this.resize(); this.hitTest = []; this.boxSize = this.hitBoxSize; this.av = 0; this.pause = 0; this.emitter1 = new Emitter(this); this.emitter1.i = 1; this.emitter1.edge = 'top'; this.emitter1.velocity = 15; this.emitter1.distance = 0.3; this.emitter2 = new Emitter(this); this.emitter2.i = 2; this.emitter2.edge = 'top'; this.emitter2.velocity = 15; this.emitter2.distance = 0.4; this.emitter3 = new Emitter(this); this.emitter3.i = 3; this.emitter3.edge = 'top'; this.emitter3.velocity = 15; this.emitter3.distance = 0.6; this.emitter4 = new Emitter(this); this.emitter4.i = 4; this.emitter4.edge = 'top'; this.emitter4.velocity = 15; this.emitter4.distance = 0.7; this.emitters = [ this.emitter1, this.emitter2, this.emitter3, this.emitter4]; this.init(); } /** * Do a full reset of all particles */ hardReset() { if (this.running) { this.particles.destroy(); this.initParticles(); } } /** * Initialise all the internal parameters * @return {[type]} [description] */ initParams() { // todo - make textSize proportional to screen size?? No? Yes? ARGH this.textSize = 150; this.textPosition = 0.5; this.textBorderWidth = 0; this.textBorderColor = '#2222AA'; this.font = 'Monoton'; this.particleSize = 1; this.particleShape = 'circle'; //this.leftColor = '#880000'; //this.rightColor = '#334455'; this.pal1 = '#ffc610'; this.pal2 = '#ff5600'; this.pal3 = '#3333EE'; this.pal4 = '#EE3333'; this.cyclePalette = false; this.textColor = '#2222AA'; //this.leftPosition = 50; //this.rightPosition = 50; //this.leftVelocity = 10; //this.rightVelocity = 10; this.gravity = 1; this.wind = -0.25; //this.turbulence = 1; this.airDrag = 0.1; this.textStickiness = 25; this.shadeParticles = true; // should particles be mixed up in the emitters? this.mixPalette = false; } /** * Tells all sprites to regenerate on falling off the screen */ regenerateSprites() { this.sprites.forEach(sprite => { sprite.regenerate = true; }); } reset() { this.initParams(); this.rebuild(); } shortHalt() { this.pause = 1; } resize() { this.cWidth = window.innerWidth; this.cHeight = window.innerHeight; if (debug) { this.cWidth = 1024; this.cHeight = 800; } this.textCanvas.width = this.cWidth; this.textCanvas.height = this.cHeight; if (this.particleRenderer) { this.particleRenderer.resize(this.cWidth, this.cHeight); } this.rebuild(); this.repositionLabels(); } /** * Returns the paletter color associated with the index i * *really* messy. Urgh. Stop it * @param {[type]} i [description] * @return {[type]} [description] */ getPaletteColor(i) { /*if(this.cyclePalette) { i += this.tOffset; i = i % 4; }*/ if (i == 1) { return this.pal1; } else if (i == 2) { return this.pal2; } else if (i == 3) { return this.pal3; } else if (i == 4) { return this.pal4; } } buildCollisionMatrix() { // build collision matrix let iData = this.textContext.getImageData(0, 0, this.cWidth, this.cHeight); let data = iData.data; for (var i = 0; i < this.cWidth / this.boxSize; i++) { this.hitTest[i] = []; for (var j = 0; j < this.cHeight / this.boxSize; j++) { //if above or below a specific row, safely assume there is no text here if (j > (this.cHeight / 2 + this.textSize * 1.5) / this.boxSize || j < (this.cHeight / 2 - this.textSize * 1.5) / this.boxSize) { this.hitTest[i][j] = 0; continue; } var pixel = 0; // get boxSize/2 pixels to the left, right, above and below this point for (var x = i * this.boxSize - this.boxSize; x < i * this.boxSize + this.boxSize; x++) { for (var y = j * this.boxSize - this.boxSize; y < j * this.boxSize + this.boxSize; y++) { if (x < 0 || y < 0 || x > this.cWidth || y > this.cHeight) { continue; } pixel += data[x * 4 + y * 4 * this.cWidth] + data[x * 4 + y * 4 * this.cWidth + 1] + data[x * 4 + y * 4 * this.cWidth + 2] + data[x * 4 + y * 4 * this.cWidth + 3]; } } this.hitTest[i][j] = pixel; } } } initParticles() { // When using the highly optimised ParticleContainer, cannot apply tint operations this.particles = new PIXI.ParticleContainer(100000, { tint: true, position: true, autoResize: true }); //this.particles = new PIXI.DisplayObjectContainer(); // create a texture from an image path this.stage.addChild(this.particles); this.sprites = []; for (var i = 0; i < this.particleCount; i++) { let sprite = this.createSprite(i); this.sprites.push(sprite); this.particles.addChild(sprite); } } init() { document.getElementById('input').value = "bfw.wiki"; this.particleRenderer = PIXI.autoDetectRenderer({ width: this.cWidth, height: this.cHeight, transparent: true }); //this.particleRenderer.backgroundColor = 0x888888; this.particleRenderer.view.style.position = "absolute"; this.particleRenderer.view.style.top = "0px"; this.particleRenderer.view.style.left = "0px"; document.getElementById('pixi').appendChild(this.particleRenderer.view); this.stage = new PIXI.Container(); this.setWeightedEmitters(); this.initParticles(); this.rebuild(); this.repositionLabels(); requestAnimationFrame(this.animate.bind(this)); } // thanks http://www.javascriptkit.com/javatutors/weighrandom2.shtml setWeightedEmitters() { this.emitterTotalFlow = this.emitters.reduce(function (prev, cur) { return prev + cur.flow; }, 0); this.weightedEmitters = []; const emitterWeights = []; this.emitters.forEach(emitter => { emitterWeights.push(emitter.flow); }); this.emitters.forEach(emitter => { // todo - oh please zero index emitter.i // Please. for (let i = 0; i < emitterWeights[emitter.i - 1]; i++) { this.weightedEmitters[this.weightedEmitters.length] = emitter.i - 1; } }); } /** * //Add to Collider.Utils? * Apply luminance to a sprite. Used to get the subtle colour variation effect when choosing an emitter colour */ lum(hex, lum) { // validate hex string hex = String(hex).replace(/[^0-9a-f]/gi, ''); if (hex.length < 6) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } lum = lum || 0; // convert to decimal and change luminosity var rgb = "#", c,i; for (i = 0; i < 3; i++) { c = parseInt(hex.substr(i * 2, 2), 16); c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16); rgb += ("00" + c).substr(c.length); } return rgb; } /** * [initSprite description] * @param {[type]} sprite [description] * @return {[type]} [description] */ initSprite(sprite) { sprite.anchor.set(0.5); sprite.mass = 0.2 + Math.random() * 0.8; // Which emitter is this sprite going to come from? if (this.emitterTotalFlow == 0) { // can't init sprite while all emitters are turned off! return; } let emitter; if (!sprite.emitter || this.mixPalette) { let i = Math.floor(Math.random() * this.emitterTotalFlow); emitter = this.emitters[this.weightedEmitters[i]]; } else { emitter = sprite.originalEmitter; // use the same emitter } emitter.emit(sprite); sprite.velocity.y = Math.random() * 4 + 1; sprite.inactive = 0; sprite.dropOut = false; if (sprite.regenerate) { const targetColor = this.getPaletteColor(sprite.emitter.i); let col; if (this.shadeParticles) { col = this.lum(targetColor, -0.33 + Math.random() * 0.33).replace('#', '0x'); } else { col = targetColor.replace('#', '0x'); } sprite.tint = col; let pngSizeFactor = 0.33; sprite.scale.set((sprite.scaleAdjustment * this.particleSize * sprite.mass + Math.random() * 0.5) * pngSizeFactor); // cater for later base64 data PNGs sprite.bornAt = performance.now(); sprite.stillFor = 0; sprite.lastPos = { x: 0, y: 0 }; sprite.regenerate = false; } } /** * [i description] * @type {[type]} */ createSprite(i = false) { let sprite; if (this.particleShape == 'square') { sprite = PIXI.Sprite.from(square64); } else if (this.particleShape == 'star') { sprite = PIXI.Sprite.from(star64); } else if (this.particleShape == 'flower') { sprite = PIXI.Sprite.from(flower64); } else if (this.particleShape == 'ring') { sprite = PIXI.Sprite.from(ring64); } else { sprite = PIXI.Sprite.from(circle64); } sprite.regenerate = true; sprite.specular = Math.random(); sprite.velocity = { x: 0, y: 0 }; sprite.i = i; sprite.delay = 5000 * Math.random(); //feed sprites slowly return sprite; } //batchAdd(); animate(t) { this.running = true; this.tOffset = Math.floor(t / 1000 * 0.25 % 4) + 1; if (this.pause > 0) { this.pause--; requestAnimationFrame(this.animate.bind(this)); return; } this.particleRenderer.render(this.stage); for (var i in this.sprites) { var sprite = this.sprites[i]; if (sprite.inactive || sprite.bornAt + sprite.delay > t) { sprite.alpha = 0; continue; } else { sprite.alpha = 1; } sprite.velocity.x *= 1 - this.airDrag * 0.25; let dx = sprite.velocity.x * sprite.mass; let dy = sprite.velocity.y * sprite.mass; sprite.position.x += dx; sprite.position.y += dy; // knock the sprites from the top of the letters once they've settled if (sprite.stillFor > this.textStickiness * (Math.random(0.4) + 0.8)) { sprite.dropOut = true; } let reverseFactor = 1; if (sprite.emitter) { reverseFactor = sprite.emitter.reverseGravity ? -1 : 1; } // 0.25 is a fudge factor to slow gravity down a bit. seems too strong sprite.velocity.y += this.gravity * reverseFactor * 0.25; //sprite.velocity.y += 0.3 * this.gravity; if (!sprite.stillFor) { sprite.position.x += this.wind; } var _x = Math.floor(sprite.position.x / this.boxSize); var _y = Math.floor(sprite.position.y / this.boxSize); if (_x >= 0 && _y >= 0 && _x < this.cWidth / this.boxSize && _y < this.cHeight / this.boxSize) { let hit; // Fixes an error sometimes where this isn't defined. Randomness if (this.hitTest[_x]) { hit = this.hitTest[_x][_y]; } else { hit = 0; } if (hit > 0 && !sprite.dropOut) { sprite.velocity.y = 0 - sprite.velocity.y * (0.1 + 0.3 * Math.random()); if (sprite.velocity.y < 0.2 && sprite.velocity.y > -0.2) { sprite.velocity.y = 0; //stabilise } sprite.stillFor++; // assuming that the sprite lies still after settling on a surface //sprite.velocity.x *= -1 + 2 * Math.random(); sprite.velocity.x *= -1 + 0.25 * Math.random(); } else { sprite.stillFor = 0; } } else { if (sprite.regenerate) { // Kill the sprite. Create a new one in its place. *no one will ever know* this.particles.removeChild(sprite); let newSprite = this.createSprite(sprite.i); this.initSprite(newSprite); this.particles.addChild(newSprite); this.sprites[i] = newSprite; } else { // recycle the sprite if it falls off the edge this.initSprite(sprite); } } } requestAnimationFrame(this.animate.bind(this)); } //requestAnimationFrame(animate); /** * Rebuild the text and collision matrix * @return {[type]} [description] */ rebuild() { if (!this.loadedFontFaces.includes(this.font)) { let fontFace = new FontFace(this.font, "url('https://assets.codepen.io/52084/" + this.font + ".ttf')"); fontFace.load().then(_ => { document.fonts.add(fontFace); this.loadedFontFaces.push(fontFace); this.fontFace = fontFace; this.renderText(); }); } else { console.log("unused?"); this.fontFace = fontFace; this.renderText(); } } renderText() { const input = document.getElementById('input'); let text = input.value; // bit of a ham-fisted way of adjusting size to always fit on screen let adjustmentFactor = this.cWidth / 1280; if (adjustmentFactor > 1) { adjustmentFactor = 1; } const adjustedTextSize = this.textSize * adjustmentFactor; this.textContext.font = adjustedTextSize + "px " + '"' + this.fontFace.family + '"'; this.textContext.clearRect(0, 0, this.cWidth, this.cHeight); let textWidth = this.textContext.measureText(text).width; this.textContext.fillStyle = this.textColor; if (this.textBorderWidth > 0) { this.textContext.strokeStyle = this.textBorderColor; this.textContext.lineWidth = this.textBorderWidth; this.textContext.strokeText(text, (this.cWidth - textWidth) / 2, this.textPosition * this.cHeight); } this.textContext.fillText(text, (this.cWidth - textWidth) / 2, this.textPosition * this.cHeight); this.buildCollisionMatrix(); } /** * Reposition the number labels for the cascade helper * @return {[type]} [description] */ repositionLabels() { if (!this.emitters) { return; } this.emitters.forEach(emitter => { let origin = emitter.origin(); let label = document.querySelectorAll('[data-cascade-label="' + emitter.i + '"]'); let x = origin.x; let y = origin.y; if (emitter.edge == 'top') { x += 20; y += 40; } if (emitter.edge == 'right') { x -= 40; y += 20; } if (emitter.edge == 'bottom') { y -= 40; x -= 20; } if (emitter.edge == 'left') { x += 40; y += 20; } if (label) { console.log(x); label[0].style.left = x + "px"; label[0].style.top = y + "px"; } }); }} /** * To do - get all the particle junk into this */ class Particle { constructor(collider) { // ?? } step() { } animate() { }} /** * Emitter class - sets up particles with initial position/velocity */ class Emitter { constructor(collider) { this.collider = collider; this.distance = 0.5; // distance along the chosen edge this.edge = 'top'; // top, right, bottom, left this.velocity = 5; // speed at which particles are emitted this.reverseGravity = false; this.scaleAdjustment = 1; this.spread = 1; // 0 - 2 this.flow = 100; // How many particles come out of this one? (0-1) } /** * Set the initial origin point * Pretty basic screen-space stuff, no translations happening here * @return {[type]} [description] */ origin() { let x, y; if (this.edge == 'top') { .........完整代码请登录后点击上方下载按钮下载查看
网友评论0