web-haptics 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-haptics",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Haptic feedback for the mobile web.",
5
5
  "homepage": "https://haptics.lochie.me",
6
6
  "sideEffects": false,
@@ -1,3 +0,0 @@
1
- "use client";
2
- var b={success:{pattern:[{duration:50},{delay:50,duration:50}],description:"A series of taps indicating success."},nudge:{pattern:[{duration:80,intensity:.8},{delay:80,duration:50,intensity:.3}],description:"A series of taps indicating a nudge."},error:{pattern:[{duration:50,intensity:.75},{delay:50,duration:50,intensity:.75},{delay:50,duration:50,intensity:.75}],description:"A series of taps indicating a warning or error."},buzz:{pattern:[{duration:1e3,intensity:1}],description:"A long vibration."}};var v=16,g=184,m=1e3,f=20;function x(a){if(typeof a=="number")return{vibrations:[{duration:a}]};if(typeof a=="string"){let i=b[a];return i?{vibrations:i.pattern.map(t=>({...t}))}:(console.warn(`[web-haptics] Unknown preset: "${a}"`),null)}if(Array.isArray(a)){if(a.length===0)return{vibrations:[]};if(typeof a[0]=="number"){let i=a,t=[];for(let e=0;e<i.length;e+=2){let n=e>0?i[e-1]:0;t.push({...n>0&&{delay:n},duration:i[e]})}return{vibrations:t}}return{vibrations:a.map(i=>({...i}))}}return{vibrations:a.pattern.map(i=>({...i}))}}function C(a,i){if(i>=1)return[a];if(i<=0)return[];let t=Math.max(1,Math.round(f*i)),e=f-t,n=[],o=a;for(;o>=f;)n.push(t),n.push(e),o-=f;if(o>0){let s=Math.max(1,Math.round(o*i));n.push(s);let r=o-s;r>0&&n.push(r)}return n}function w(a,i){let t=[];for(let e=0;e<a.length;e++){let n=a[e],o=Math.max(0,Math.min(1,n.intensity??i)),s=n.delay??0;s>0&&(t.length>0&&t.length%2===0?t[t.length-1]+=s:(t.length===0&&t.push(0),t.push(s)));let r=C(n.duration,o);if(r.length===0){t.length>0&&t.length%2===0?t[t.length-1]+=n.duration:n.duration>0&&(t.push(0),t.push(n.duration));continue}for(let d of r)t.push(d)}return t}var I=0,y=class a{hapticLabel=null;domInitialized=!1;instanceId;debug;showSwitch;rafId=null;patternResolve=null;audioCtx=null;audioFilter=null;audioGain=null;audioBuffer=null;constructor(i){this.instanceId=++I,this.debug=i?.debug??!1,this.showSwitch=i?.showSwitch??!1}static isSupported=typeof navigator<"u"&&typeof navigator.vibrate=="function";async trigger(i=[{duration:10}],t){let e=x(i);if(!e)return;let{vibrations:n}=e;if(n.length===0)return;let o=Math.max(0,Math.min(1,t?.intensity??.5));for(let s of n)if(s.duration>m&&(s.duration=m),!Number.isFinite(s.duration)||s.duration<0||s.delay!==void 0&&(!Number.isFinite(s.delay)||s.delay<0)){console.warn("[web-haptics] Invalid vibration values. Durations and delays must be finite non-negative numbers.");return}if(a.isSupported&&navigator.vibrate(w(n,o)),!a.isSupported||this.debug){if(this.ensureDOM(),!this.hapticLabel)return;if(this.debug&&await this.ensureAudio(),this.stopPattern(),this.hapticLabel.click(),this.debug&&this.audioCtx){let s=Math.max(0,Math.min(1,n[0].intensity??o));this.playClick(s)}await this.runPattern(n,o)}}cancel(){this.stopPattern(),a.isSupported&&navigator.vibrate(0)}destroy(){this.stopPattern(),this.hapticLabel&&(this.hapticLabel.remove(),this.hapticLabel=null,this.domInitialized=!1),this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setDebug(i){this.debug=i,!i&&this.audioCtx&&(this.audioCtx.close(),this.audioCtx=null,this.audioFilter=null,this.audioGain=null,this.audioBuffer=null)}setShowSwitch(i){if(this.showSwitch=i,this.hapticLabel){let t=this.hapticLabel.querySelector("input");this.hapticLabel.style.display=i?"":"none",t&&(t.style.display=i?"":"none")}}stopPattern(){this.rafId!==null&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.patternResolve?.(),this.patternResolve=null}runPattern(i,t){return new Promise(e=>{this.patternResolve=e;let n=[],o=0;for(let u of i){let h=Math.max(0,Math.min(1,u.intensity??t)),l=u.delay??0;l>0&&(o+=l,n.push({end:o,isOn:!1,intensity:0})),o+=u.duration,n.push({end:o,isOn:!0,intensity:h})}let s=o,r=0,d=-1,p=u=>{r===0&&(r=u);let h=u-r;if(h>=s){this.rafId=null,this.patternResolve=null,e();return}let l=n[0];for(let c of n)if(h<c.end){l=c;break}if(l.isOn){let c=v+(1-l.intensity)*g;d===-1?d=u:u-d>=c&&(this.hapticLabel?.click(),this.debug&&this.audioCtx&&this.playClick(l.intensity),d=u)}this.rafId=requestAnimationFrame(p)};this.rafId=requestAnimationFrame(p)})}playClick(i){if(!this.audioCtx||!this.audioFilter||!this.audioGain||!this.audioBuffer)return;let t=this.audioBuffer.getChannelData(0);for(let n=0;n<t.length;n++)t[n]=(Math.random()*2-1)*Math.exp(-n/25);this.audioGain.gain.value=.5*i;let e=this.audioCtx.createBufferSource();e.buffer=this.audioBuffer,e.connect(this.audioFilter),e.onended=()=>e.disconnect(),e.start()}async ensureAudio(){if(!this.audioCtx&&typeof AudioContext<"u"){this.audioCtx=new AudioContext,this.audioFilter=this.audioCtx.createBiquadFilter(),this.audioFilter.type="bandpass",this.audioFilter.frequency.value=4e3,this.audioFilter.Q.value=8,this.audioGain=this.audioCtx.createGain(),this.audioFilter.connect(this.audioGain),this.audioGain.connect(this.audioCtx.destination);let i=.004;this.audioBuffer=this.audioCtx.createBuffer(1,this.audioCtx.sampleRate*i,this.audioCtx.sampleRate);let t=this.audioBuffer.getChannelData(0);for(let e=0;e<t.length;e++)t[e]=(Math.random()*2-1)*Math.exp(-e/25)}this.audioCtx?.state==="suspended"&&await this.audioCtx.resume()}ensureDOM(){if(this.domInitialized||typeof document>"u")return;let i=`web-haptics-${this.instanceId}`,t=document.createElement("label");t.setAttribute("for",i),t.textContent="Haptic feedback",t.style.position="fixed",t.style.bottom="10px",t.style.left="10px",t.style.padding="5px 10px",t.style.backgroundColor="rgba(0, 0, 0, 0.7)",t.style.color="white",t.style.fontFamily="sans-serif",t.style.fontSize="14px",t.style.borderRadius="4px",t.style.zIndex="9999",t.style.userSelect="none",this.hapticLabel=t;let e=document.createElement("input");e.type="checkbox",e.setAttribute("switch",""),e.id=i,e.style.all="initial",e.style.appearance="auto",this.showSwitch||(t.style.display="none",e.style.display="none"),t.appendChild(e),document.body.appendChild(t),this.domInitialized=!0}};export{b as a,y as b};
3
- //# sourceMappingURL=chunk-42RGEEVN.mjs.map