PHP Classes

File: public_html/vendor/websid/stdlib/mini_display.js

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   public_html/vendor/websid/stdlib/mini_display.js   Download  
File: public_html/vendor/websid/stdlib/mini_display.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 10,353 bytes
 

Contents

Class file image Download
/* * Simple visualization used by my old chiptune player example pages. * * version 1.02 * * Page provides a collapsible "what's this" info box (see <details>). For * background visuals an animated "frequency spectrum" is drawn as well * as some text info describing the played song. * * external dependencies: * * ScriptNodePlayer * document canvas elements: "spectrumCanvas", "logoCanvas", * document div elements: "moz-reflect-spectrum", "moz-reflect-logo" * respective styles defined in common.css */ /** * Abstract interface used to provide the data to display. */ class DisplayAccessor { constructor(doGetSongInfo) { this.getSongInfo = doGetSongInfo; // util for use in subclasses } getDisplayTitle() {} getDisplaySubtitle() {} getDisplayLine1() {} getDisplayLine2() {} getDisplayLine3() {} }; /** * This renders a "frequency spectrum" based graph and some song meta information. */ class SongDisplay { /* * @param displayAccessor concrete subclass of DisplayAccessor * @param colors either the URL to an image or an array containing color strings * @param barType 0: "growing bars", 1: "jumping bars" * @param cpuLimit percentage of CPU that can be used for graphics: 0= 0%, 1=100%.. or anything inbetween */ constructor(displayAccessor, colors, barType, cpuLimit) { this._displayAccessor = displayAccessor; this._hexChars = "0123456789ABCDEF"; if (typeof colors == 'string' || colors instanceof String) { this._colors; this._backgroundImgUrl = colors; this._backgroundImg = 0; } else if( Object.prototype.toString.call( colors ) === '[object Array]' ) { this._colors = colors; // array containing colors } else { console.log("fatal error: invalid 'colors' argument"); } this._width = 800; this._height = 100; this._barType = barType; this._barWidth = 5; this._barHeight = 30; this._barSpacing = 10; this._colorOffset = 0; this._refreshCounter = 0; this._lastRenderTime = 0; this._worstRenderTime = 0; this._timeLimit = 1000 / 60 * cpuLimit; // renderiung should take no longer than 10% of the available time (e.g. 3-4 millis per frame) this._canvasSpectrum = document.getElementById('spectrumCanvas'); if (this._canvasSpectrum) { this._ctxSpectrum = this._canvasSpectrum.getContext('2d'); this._mozReflectSpectrum = document.getElementById('moz-reflect-spectrum'); } this._canvasLegend = document.getElementById('logoCanvas'); this._ctxLegend = this._canvasLegend.getContext('2d'); this._mozReflectLogo = document.getElementById('moz-reflect-logo'); } _updateImage(src) { this._backgroundImg = 0; let imgObj = new Image(); imgObj.onload = function () { this._backgroundImg = imgObj; }.bind(this); imgObj.src = src; } setBarDimensions(w, h, s) { this._barWidth = w; this._barHeight = h; this._barSpacing = s; } _drawGrowingBars(freqByteData, numBars) { const offset = 100; for (let i = 0; i < numBars; ++i) { this._ctxSpectrum.fillStyle = this._colorGradient(this._colors, i / numBars); let magnitude = freqByteData[i + offset] * this._height / 255; this._ctxSpectrum.fillRect(i * this._barSpacing, this._height, this._barWidth, -magnitude); this._ctxSpectrum.fillStyle= "#b75b4e"; let d = this._caps[i] * this._decayRate; this._caps[i]= Math.max(d, magnitude); this._ctxSpectrum.fillRect(i * this._barSpacing, this._height - this._caps[i], this._barWidth, 3); } } _drawJumpingBars(freqByteData, numBars) { const offset = 100; for (let i = 0; i < numBars; ++i) { let scale = freqByteData[i + offset] / 0xff; let magnitude = scale * this._height; let p = Math.abs((i + this._colorOffset) % numBars); this._ctxSpectrum.fillStyle = this._colorGradient(this._colors, p / numBars); // computationally much less expensive than previous impl with use of a scaled image this._ctxSpectrum.fillRect(i * this._barSpacing, this._height - magnitude, this._barWidth, this._barHeight * (scale * 1.4)); } } _drawJumpingImageBars(freqByteData, numBars) { const offset = 100; for (let i = 0; i < numBars; ++i) { let scale = freqByteData[i + offset] / 0xff; let magnitude = scale * this._height; let o = Math.round(this._height - magnitude); this._ctxSpectrum.drawImage(this._backgroundImg, 0,0, this._barWidth, 255, i * this._barSpacing, o, this._barWidth, 30 * (scale * 1.4)); } } _drawGrowingImageBars(freqByteData, numBars) { const offset = 100; let imgW = this._backgroundImg.naturalWidth; let imgH = this._backgroundImg.naturalHeight; let sliceW = imgW / numBars; for (let i = 0; i < numBars; ++i) { let magnitude = freqByteData[i + offset] * this._height / 255; let o = Math.round(this._height - magnitude); this._ctxSpectrum.drawImage(this._backgroundImg, i * sliceW, 0, sliceW, imgH, i * this._barSpacing, o, this._barWidth, Math.round(magnitude)); } } _throttledCall(func) { // with low enough load the browser should be able to render 60fps: the load from the music generation // cannot be changed but the load from the rendering can.. this._worstRenderTime = Math.max(this._worstRenderTime, this._lastRenderTime); this._lastRenderTime = new Date().getTime(); // start new measurement let slowdownFactor = this._timeLimit ? Math.max(1, this._worstRenderTime / this._timeLimit) : 1; this._refreshCounter++; if (this._refreshCounter >= slowdownFactor) { this._refreshCounter = 0; func(); } this._lastRenderTime= new Date().getTime()-this._lastRenderTime; } _redrawSpectrum() { if (this._canvasSpectrum) { let freqByteData = ScriptNodePlayer.getInstance().getFreqByteData(); let numBars = Math.round(this._width / this._barSpacing); if(typeof this._caps === 'undefined') { this._caps = new Array(numBars); this._decayRate = 0.99; for (let i = 0; i < numBars; i++) this._caps[i] = 0; } try { // seems that dumbshit Safari (11.0.1 OS X) uses the fillStyle for "clearRect"! this._ctxSpectrum.fillStyle = "rgba(0, 0, 0, 0.0)"; } catch (err) {} this._ctxSpectrum.clearRect(0, 0, this._width, this._height); this._ctxSpectrum.lineCap = 'round'; if (this._colors) // colors based effects { if (this._barType & 0x2) { this._colorOffset -= 2; // cycle colors } if ((this._barType & 0x1) == 1) { this._drawJumpingBars(freqByteData, numBars); } else { this._drawGrowingBars(freqByteData, numBars); } } else // background image based effects { if (this._backgroundImg) { if (this._barType == 1) { this._drawJumpingImageBars(freqByteData, numBars); } else { this._drawGrowingImageBars(freqByteData, numBars); } } } // hack: make sure dumb Firefox knows that redraw is needed.. this._mozReflectSpectrum.style.visibility = "hidden"; this._mozReflectSpectrum.style.visibility = "visible"; } } redrawSpectrum() { this._throttledCall(this._redrawSpectrum.bind(this)); } _text(ctx, t, x, y) { if(typeof t === 'undefined') return; t = t.replace(/&#.*?;/g, function (needle) { // replace html chars "&#111;" return String.fromCharCode( parseInt(needle.substring(2, needle.length-1))); }.bind(this)); ctx.strokeText(t, x, y); ctx.fillText(t, x, y); } redrawSongInfo() { if (!this._colors && !this._backgroundImg) this._updateImage(this._backgroundImgUrl); try { // seems that the Safari (11.0.1 OS X) idiots use the fillStyle for // "clearRect", i.e. it is impossible to properly make this canvas // completely transparent this._ctxLegend.fillStyle = "rgba(0, 0, 0, 0.0)"; } catch (err) {} this._ctxLegend.clearRect(0, 0, 800, 300); this._ctxLegend.textBaseline = "middle"; this._ctxLegend.fillStyle = '#000'; this._ctxLegend.strokeStyle = "#FFFFFF"; this._ctxLegend.font = '30px arial, sans-serif'; // Chrome garbage no longer understands: '90px serif bold' this._text(this._ctxLegend, this._displayAccessor.getDisplayTitle(), 20, 15); this._ctxLegend.font = '15px sans-serif'; this._text(this._ctxLegend, this._displayAccessor.getDisplaySubtitle(), 20, 35); this._ctxLegend.fillStyle = '#888'; this._ctxLegend.font = '15px sans-serif'; this._ctxLegend.textBaseline = 'bottom'; this._text(this._ctxLegend, this._displayAccessor.getDisplayLine1(), 20, 65); this._text(this._ctxLegend, this._displayAccessor.getDisplayLine2(), 20, 80); this._text(this._ctxLegend, this._displayAccessor.getDisplayLine3(), 20, 95); } _colorGradient(cols, s) { let p = (cols.length - 1) * s; let i = Math.floor(p); return this._fadeColor(cols[i], cols[i + 1], p - i); } _fadeColor(from, to, s) { let r1 = (from >> 16) & 0xff; let g1 = (from >> 8) & 0xff; let b1 = from & 0xff; let r = Math.round(r1 + (((to >> 16) & 0xff) - r1) * s); let g = Math.round(g1 + (((to >> 8) & 0xff) - g1) * s); let b = Math.round(b1 + ((to & 0xff) - b1) * s); return "#" + this._hex(r>>4) + this._hex(r) + this._hex(g>>4) + this._hex(g) + this._hex(b>>4) + this._hex(b); } _hex(n) { return this._hexChars.charAt(n & 0xf); } }; window.console || (window.console = { 'log': alert }); /** * Initialize UI */ $(function() { // setup initial state of the <details> info box $('html').addClass($.fn.details.support ? 'details' : 'no-details'); $('details').details(); if (!(window.openDetails === 'undefined') && window.openDetails) { if ($.fn.details.support) { $('details').attr('open', ''); // Chrome } else { let $details= $('details'); let $detailsSummary = $('summary', $details).first(); let $detailsNotSummary = $details.children(':not(summary)'); $details.addClass('open').prop('open', true).triggerHandler('open.details'); $detailsNotSummary.show(); } } // hack to get rid of Firefox specific pseudo elements used to sim webkit-box-reflect if (!window.chrome) { let e = document.getElementById("moz-reflect-logo"); e.className += " enableMozReflection"; let f = document.getElementById("moz-reflect-spectrum"); f.className += " enableMozReflection"; } });