UNPKG

asciiground

Version:

Library for creating animated ASCII canvas backgrounds supporting generative patterns and extensive configuration options.

2 lines (1 loc) 6.68 kB
(function(l,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(l=typeof globalThis<"u"?globalThis:l||self,f(l.ASCIIGround={}))})(this,function(l){"use strict";class f{permutation;constructor(i=0){this.permutation=this.generatePermutation(i)}generatePermutation(i){const t=[];for(let n=0;n<256;n++)t[n]=n;for(let n=255;n>0;n--){const e=Math.floor(i*(n+1)%(n+1));[t[n],t[e]]=[t[e],t[n]],i=i*16807%2147483647}return[...t,...t]}fade(i){return i*i*i*(i*(i*6-15)+10)}lerp(i,t,n){return i+n*(t-i)}grad(i,t,n){const e=i&3,s=e<2?t:n,d=e<2?n:t;return((e&1)===0?s:-s)+((e&2)===0?d:-d)}noise(i,t){const n=Math.floor(i)&255,e=Math.floor(t)&255;i-=Math.floor(i),t-=Math.floor(t);const s=this.fade(i),d=this.fade(t),o=this.permutation[n]+e,r=this.permutation[o],c=this.permutation[o+1],a=this.permutation[n+1]+e,h=this.permutation[a],p=this.permutation[a+1];return this.lerp(this.lerp(this.grad(this.permutation[r],i,t),this.grad(this.permutation[h],i-1,t),s),this.lerp(this.grad(this.permutation[c],i,t-1),this.grad(this.permutation[p],i-1,t-1),s),d)}}function m(){const u=[[12448,12543],[12352,12447],[19968,20096]],[i,t]=u[Math.floor(Math.random()*u.length)];return String.fromCharCode(Math.floor(Math.random()*(t-i))+i)}class g{canvas;ctx;options;animationId=null;startTime=0;perlin;cols=0;rows=0;charWidth=0;charHeight=0;japanRainDrops=[];rainDropDensity=.9;get isAnimating(){return this.animationId!==null}constructor(i,t){this.canvas=i;const n=i.getContext("2d");if(!n)throw new Error("Could not get 2D context from canvas.");this.ctx=n,this.options={pattern:t.pattern,characters:t.characters,speed:t.speed,fontSize:t.fontSize||12,fontFamily:t.fontFamily||"monospace",color:t.color||"#00ff00",backgroundColor:t.backgroundColor||"#000000",animated:t.animated!==void 0?t.animated:!0,direction:t.direction||"down",amplitudeX:t.amplitudeX??1,amplitudeY:t.amplitudeY??1,frequency:t.frequency??1,noiseScale:t.noiseScale??.1,rainDensity:t.rainDensity??.9,rainDirection:t.rainDirection??"vertical"},this.rainDropDensity=this.options.rainDensity,this.perlin=new f,this.setupCanvas(),this.options.pattern==="japan-rain"&&this.initJapanRain()}setupCanvas(){this.ctx.font=`${this.options.fontSize}px ${this.options.fontFamily}`,this.ctx.textBaseline="top";const i=this.ctx.measureText("M");this.charWidth=i.width,this.charHeight=this.options.fontSize,this.cols=Math.floor(this.canvas.width/this.charWidth),this.rows=Math.floor(this.canvas.height/this.charHeight)}getNoiseFunction(){const{noiseScale:i,direction:t,amplitudeX:n,amplitudeY:e,frequency:s,rainDirection:d}=this.options;switch(this.options.pattern){case"perlin":return(o,r,c)=>{let a=o,h=r;switch(t){case"left":a=-o;break;case"right":a=o;break;case"up":h=-r;break;case"down":h=r;break}return this.perlin.noise(a*i,h*i+c)};case"wave":return(o,r,c)=>{let a=c*s,h=o,p=r;switch(t){case"left":a=-a;break;case"right":a=a;break;case"up":a=-a;break;case"down":a=a;break}return Math.sin(h*.1*n+a)*Math.cos(p*.1*e+a*.5)};case"rain":return(o,r,c)=>{let a=0;switch(d){case"vertical":a=0;break;case"diagonal-left":a=Math.PI/4;break;case"diagonal-right":a=-Math.PI/4;break}const h=Math.cos(a),p=Math.sin(a);return Math.sin((r*p+o*h)*.2+c*2)*Math.cos(o*.05)};case"static":return()=>Math.random()*2-1;default:return()=>0}}initJapanRain(){this.japanRainDrops=[],this.rainDropDensity=this.options.rainDensity??.9;for(let i=0;i<this.cols;i++)if(Math.random()<this.rainDropDensity){const t=Math.floor(Math.random()*20)+8,n=Array.from({length:t},()=>m());this.japanRainDrops.push({col:i,y:Math.floor(Math.random()*this.rows),speed:.5+Math.random()*1.2,chars:n,length:t,age:0})}}updateJapanRainDrops(){for(const t of this.japanRainDrops){if(t.y+=t.speed*this.options.speed,t.age+=this.options.speed,Math.random()<.04){let n=Math.floor(Math.random()*t.length);t.chars[n]=m()}t.y-t.length>this.rows&&(t.y=-Math.floor(Math.random()*8),t.length=Math.floor(Math.random()*20)+8,t.chars=Array.from({length:t.length},()=>m()),t.speed=.5+Math.random()*1.2,t.age=0)}const i=Math.floor(this.cols*this.rainDropDensity);for(;this.japanRainDrops.length<i;){const t=this.japanRainDrops.length%this.cols,n=Math.floor(Math.random()*20)+8,e=Array.from({length:n},()=>m());this.japanRainDrops.push({col:t,y:Math.floor(Math.random()*this.rows),speed:.5+Math.random()*1.2,chars:e,length:n,age:0})}this.japanRainDrops.length>i&&(this.japanRainDrops.length=i)}renderJapanRain(){this.ctx.fillStyle="rgba(0, 0, 0, 0.2)",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);for(const i of this.japanRainDrops)for(let t=0;t<i.length;t++){const n=i.chars[t],e=i.col*this.charWidth,s=(Math.floor(i.y)-t)*this.charHeight;s<0||s>this.canvas.height||(t===0?this.ctx.fillStyle="#ccffcc":t<3?this.ctx.fillStyle=this.options.color||"#00ff00":this.ctx.fillStyle="rgba(0,255,0,0.7)",this.ctx.fillText(n,e,s))}}render(i){if(this.options.pattern==="japan-rain"){this.updateJapanRainDrops(),this.renderJapanRain();return}this.ctx.fillStyle=this.options.backgroundColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height),this.ctx.fillStyle=this.options.color;const t=this.getNoiseFunction(),n=(i-this.startTime)/1e3*this.options.speed;for(let e=0;e<this.rows;e++)for(let s=0;s<this.cols;s++){const o=(t(s,e,n)+1)/2,r=Math.floor(o*this.options.characters.length),c=Math.max(0,Math.min(r,this.options.characters.length-1)),a=this.options.characters[c],h=s*this.charWidth,p=e*this.charHeight;this.ctx.fillText(a,h,p)}}start(){if(this.animationId!==null)return;this.startTime=performance.now();const i=t=>{this.render(t),this.animationId=requestAnimationFrame(i)};this.animationId=requestAnimationFrame(i)}stop(){this.animationId!==null&&(cancelAnimationFrame(this.animationId),this.animationId=null)}updateOptions(i){this.options={...this.options,...i},typeof i.rainDensity=="number"&&(this.rainDropDensity=i.rainDensity),this.setupCanvas(),this.options.pattern==="japan-rain"&&this.initJapanRain()}resize(i,t){this.canvas.width=i,this.canvas.height=t,this.setupCanvas(),this.options.pattern==="japan-rain"&&this.initJapanRain()}}function M(u){const i=document.createElement("canvas");i.style.position="fixed",i.style.top="0",i.style.left="0",i.style.width="100%",i.style.height="100%",i.style.zIndex="-1",i.style.pointerEvents="none",i.width=window.innerWidth,i.height=window.innerHeight,document.body.appendChild(i);const t=new g(i,u),n=()=>{i.width=window.innerWidth,i.height=window.innerHeight,t.resize(i.width,i.height)};return window.addEventListener("resize",n),t}l.ASCIIGround=g,l.createFullPageBackground=M,l.default=g,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});