Skip to content

Instantly share code, notes, and snippets.

@mendes5
Created January 1, 2018 21:53
Show Gist options
  • Select an option

  • Save mendes5/5566c297bf6204d5e2ae859e5606a388 to your computer and use it in GitHub Desktop.

Select an option

Save mendes5/5566c297bf6204d5e2ae859e5606a388 to your computer and use it in GitHub Desktop.
const C$ = (str = 'html', all = false) => !all ? document.querySelector(str) : document.querySelectorAll(str);
const sin = Math.sin
const cos = Math.cos
const abs = Math.abs
const int = Number.parseInt
const float = Number.parseFloat
const rand = Math.random
const u = (_ => {
const toRadFactor = Math.PI / 180
Math.toRad = (v) => v * toRadFactor
Math.TWO_PI = Math.PI * 2
Math.HALF_PI = Math.PI / 2
const _canvas = document.createElement('canvas').getContext('2d')
const getRandRGB = () => {
_canvas.fillStyle = `hsl(${random(360)}, 100%, 50%)`
return hexToRgb(_canvas.fillStyle.slice(1))
}
const getRandHEX = () => {
_canvas.fillStyle = `hsl(${random(360)}, 100%, 50%)`
return _canvas.fillStyle
}
//By 'David'
//http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
const hexToRgb = (hex) => {
const arrBuff = new ArrayBuffer(4);
const vw = new DataView(arrBuff);
vw.setUint32(0, parseInt(hex, 16), false);
const arrByte = new Uint8Array(arrBuff);
return [arrByte[1], arrByte[2], arrByte[3]]
}
let __;
const array = n => new Array(n).fill(true)
const arrayOf = (n = 1, s = 0) => array(n).map(i => new s())
const arrayBy = (n = 1, s = _ => 0) => array(n).map(i => s())
const range = (n, s = 0, f = 1) => array(n + 1).map(i => (i = s += f) - 1)
const UUID = (a = 4, b = 4) => array(a).map(i => new Array(b).fill(0).map(e => '0123456789abcdef'[Math.floor(Math.random() * 16)]).join('')).join('-')
const random = (v) => Math.floor(Math.random() * v)
//By 'Francisc'
//http://stackoverflow.com/questions/4959975/generate-random-number-between-two-numbers-in-javascript
const randIntBetw = (min, max) => Math.floor(Math.random() * (max - min + 1) + min)
const randomOf = (obj, _) => Array.isArray(obj) ? obj[randIntBetw(0, obj.length - 1)] : (_ = Object.keys(obj), obj[_[randIntBetw(0, _.length)]])
class DrawList {
constructor(creator, options = {}) {
this.creator = creator
this.before = options.before || ((e) => 0)
this.after = options.after || ((e) => 0)
this.drawables = []
}
add(...items) {
items.map(item => {
if ('redraw' in item) {
this.drawables.push(item)
} else {
console.error('Item has not an redraw function', item, this)
}
})
return this
}
remove(item) {
const itemIndex = this.drawables.indexOf(item)
if (itemIndex === -1) {
console.error('Item not found in draw list', item, this)
} else {
this.drawables.splice(itemIndex, 1)
}
return this
}
draw() {
this.before(this.creator)
for (let item of this.drawables) {
item.redraw(this.creator)
}
this.after(this.creator)
}
}
const extendContext2D = ctx => {
return Object.assign(ctx, {
clear() {
this.clearRect(0, 0, this.canvas.width, this.canvas.height)
},
createDrawList(options = {}) {
return new DrawList(this, options)
},
resize(x = 0, y = 0) {
this.canvas.width = x
this.canvas.height = y
},
strokeCircle(x = 0, y = 0, r = 0) {
this.beginPath()
this.ellipse(x, y, r, r, 0, 0, Math.TWO_PI)
this.stroke()
},
fillCircle(x = 0, y = 0, r = 0) {
this.beginPath()
this.ellipse(x, y, r, r, 0, 0, Math.TWO_PI)
this.fill()
},
_circle(x = 0, y = 0, r = 0) {
this.ellipse(x, y, r, r, 0, 0, Math.TWO_PI)
}
})
}
const extendContext3D = gl => {
return Object.assign(gl, {
resize(x = 0, y = 0) {
this.viewport(0, 0, x, y)
this.canvas.width = x
this.canvas.height = y
},
fitToScreen() {
gl.viewport(
0, 0,
this.canvas.width = innerWidth,
this.canvas.height = innerHeight
)
},
fitToParent() {
gl.viewport(
0, 0,
this.canvas.width = this.canvas.parentElement.clientWidth,
this.canvas.height = this.canvas.parentElement.clientHeight
)
}
})
}
const extendCanvas = canvas => {
return Object.assign(canvas, {
mode: undefined,
resize(x = 0, y = 0) {
this.width = x;
this.height = y;
},
fitToParent() {
this.width = this.parentElement.clientWidth
this.height = this.parentElement.clientHeight
},
fitToScreen() {
this.width = innerWidth
this.height = innerHeight
},
get2D() {
if (this.mode === undefined) {
this.mode = 'CanvasRenderingContext2D'
return extendContext2D(this.getContext('2d'))
} else {
return null
}
},
getGL() {
if (this.mode === undefined) {
this.mode = 'WebGLRenderingContext'
let gl = this.getContext('webgl')
if (!gl) {
console.error('WebGL not supported in this browser version.')
return null
}
return extendContext3D(gl)
} else {
return null
}
},
getGL2() {
if (this.mode === undefined) {
this.mode = 'WebGL2RenderingContext'
let gl2 = this.getContext('webgl2')
if (!gl2) {
console.error('WebGL 2 not supported in this browser version.')
return null
}
return extendContext3D(gl2)
} else {
return null
}
}
})
}
const getCanvas = {
fromId(id) {
const canvas = C$(id)
return extendCanvas(canvas)
},
fromClass(name, index = 0) {
const canvas = C$(name, true)
return extendCanvas(canvas[index])
},
creatingIt() {
const canvas = document.createElement('canvas')
return extendCanvas(canvas)
}
}
const getText = (url) => fetch(url, { mode: 'no-cors' }).then(r => r.text())
const getJSON = (url) => fetch(url, { mode: 'no-cors' }).then(r => r.json())
const getBlob = (url) => fetch(url, { mode: 'no-cors' }).then(r => r.blob())
const getArrayBuffer = (url) => fetch(url, { mode: 'no-cors' }).then(r => r.arrayBuffer())
//Copied from mozilla docs
//https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
const flatten = arr => arr.reduce((a, b) => a.concat(b))
//By 'jolly.exe'
//http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
const getParameterByName = (name, url) => {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
const utilData = {
textFormats: ['txt', 'glsl'],
imageFormats: ['png', 'bmp', 'gif', 'jpg', 'jpeg'],
videoFormats: ['mov', 'webm', 'mp4', 'swf'],
audioFormats: ['mp3', 'ogg', 'wav'],
parseableFormats: ['js', 'css', 'html'],
deniedFormats: [],
}
const loadScript = (url) => {
return new Promise((res, rej) => {
const script = document.createElement('script')
script.src = url
document.body.appendChild(script)
script.onerror = e => rej(e)
script.onload = e => res(e)
})
}
const pixelToGlCoord = (event, canvas) => {
var x = event.clientX, y = event.clientY;
var midX = canvas.width / 2, midY = canvas.height / 2;
var rect = event.target.getBoundingClientRect();
x = ((x - rect.left) - midX) / midX;
y = (midY - (y - rect.top)) / midY;
return { x, y }
}
const getE3ShaderData = (str) => {
let shaderData = { uniform: [], in: [] }
let data = str.match(/in.*;|uniform.*;/g)
data && data.map(item => {
let [input, type, name] = item.split(' ')
name = name.slice(0, name.length - 1)
shaderData[input].push(new ShaderDescriptor(name, type))
})
shaderData.isVertexShader = /gl_Position/.test(str)
return shaderData
}
const getE2ShaderData = (str) => {
let shaderData = { uniform: {}, attribute: {} }
let data = str.match(/attribute.*;|uniform.*;/g)
data && data.map(item => {
let [type1, type2, name] = item.split(' ')
name = name.slice(0, name.length - 1)
shaderData[type1][name] = {}
shaderData[type1][name].location = name
shaderData[type1][name].type = type2
})
shaderData.isFragmentShader = /gl_FragColor/.test(str)
shaderData.isVertexShader = /gl_Position/.test(str)
return shaderData
}
const loadAll = (obj = {}) => {
let resolved = {}
// iDontKnowHowToNameThis é um objeto que lembra os 'index' dos itens para
//coloca-los de volta em um objeto igual o da
//entrada e que sera usado para criar um outro objeto quando a Promisse for
//resolvida
let iDontKnowHowToNameThis = {}
let promisseArray = []
let keys = Object.keys(obj)
for (let i of Object.keys(obj)) {
let path = obj[i]
let ext = getFileExtension(path)
if (isEqualToAny(ext, utilData.textFormats)) {
iDontKnowHowToNameThis[i] = promisseArray.push(getText(path)) - 1
} else if (isEqualToAny(ext, utilData.audioFormats)) {
iDontKnowHowToNameThis[i] = promisseArray.push(Url2Tag(path, 'audio')) - 1
} else if (isEqualToAny(ext, utilData.videoFormats)) {
iDontKnowHowToNameThis[i] = promisseArray.push(Url2Tag(path, 'video')) - 1
} else if (isEqualToAny(ext, utilData.imageFormats)) {
iDontKnowHowToNameThis[i] = promisseArray.push(Url2Tag(path, 'image')) - 1
} else if (isEqualToAny(ext, utilData.parseableFormats)) {
iDontKnowHowToNameThis[i] = promisseArray.push(Url2Tag(path, 'script')) - 1
} else if (isEqualToAny(ext, utilData.deniedFormats)) {
throw new Error('u got a virus m9.')
} else if (ext === 'json') {
iDontKnowHowToNameThis[i] = promisseArray.push(getJSON(path)) - 1
}
}
return Promise.all(promisseArray).then(data => {
for (let i of Object.keys(iDontKnowHowToNameThis)) {
resolved[i] = data[iDontKnowHowToNameThis[i]]
}
return resolved
})
}
//Modified version, by 'adnasa'
//https://gist.github.com/adnasa/94e26f50082454910657
const getFileExtension = (filePath = '') => {
const ext = filePath.split('.')
return ext[ext.length - 1]
}
const isEqualToAny = (input, items = []) => {
let res = false
items.map(_i_ => !res && (res = (_i_ === input)))
return res
}
const hasAllStrings = (input, strArr = []) => {
let res = true
strArr.map(val => !input.includes(val) && (res = false))
return res
}
const extensionToMediaType = (name) => {
if (isEqualToAny(name, utilData.videoFormats))
return 'video'
else if (isEqualToAny(name, utilData.audioFormats))
return 'audio'
else if (isEqualToAny(name, utilData.parseableFormats))
return 'script'
else if (isEqualToAny(name, utilData.imageFormats))
return 'image'
else return 'none'
}
//Magic
const Url2Tag = (url, type) => {
let result;
const ext = type || extensionToMediaType(getFileExtension(url))
switch (ext.toLowerCase()) {
case 'audio':
result = fetch(url, { mode: 'no-cors' }).then(r => r.blob().then(r => {
let uri = URL.createObjectURL(r)
let tag = document.createElement('audio')
tag.src = uri
return tag
}))
break;
case 'video':
result = fetch(url, { mode: 'no-cors' }).then(r => r.blob().then(r => {
let uri = URL.createObjectURL(r)
let tag = document.createElement('video')
tag.src = uri
return tag
}))
break;
case 'image':
result = fetch(url, { mode: 'no-cors' }).then(r => r.blob().then(r => {
let uri = URL.createObjectURL(r)
let tag = document.createElement('img')
tag.src = uri
return tag
}))
break;
case 'script':
result = fetch(url, { mode: 'no-cors' }).then(r => r.blob().then(r => {
let uri = URL.createObjectURL(r)
let tag = document.createElement('script')
tag.src = uri
return tag
}))
break;
default:
console.warn(`Unknow format ${ext}.`)
break;
}
return result;
}
/*Usage:
new KeyListener({
'KeyW':{
press(){player.jump()},
release(){ player.speed = 0; player.direction = -1}
}
} ,gl.canvas)*/
class KeyListener {
constructor(obj, element = window) {
element.addEventListener('keyup', this.keyUp.bind(this), false);
element.addEventListener('keydown', this.keyDown.bind(this), false);
this.elememt = element
this.keys = {}
for (let key in obj) {
this.keys[key] = {
isDown: false,
press: obj[key].press,
release: obj[key].release
}
}
}
addFunction(keyName, obj) {
this.keys[keyName] = {
isDown: false,
press: obj.press,
release: obj.release
}
}
removeFunction(keyName) {
delete this.keys[keyName]
}
keyUp(e) {
if (this.keys[e.code] && this.keys[e.code].isDown) {
e.preventDefault()
this.keys[e.code].isDown = false
this.keys[e.code].release && this.keys[e.code].release()
}
}
keyDown(e) {
if (this.keys[e.code] && !this.keys[e.code].isDown) {
e.preventDefault()
this.keys[e.code].isDown = true
this.keys[e.code].press && this.keys[e.code].press()
}
}
detach() {
this.elememt.removeEventListener('keydown', this.keyDown, false)
this.elememt.removeEventListener('keyup', this.keyUp, false)
}
}
/*Usage
const keys = new BooleanicKeys({
'KeyW'(dt){player.x+=10*dt}
},gl.canvas)
let loop = (dt)=>{
//paramter is optional
keys.update(dt)
}
*/
class BooleanicKeys {
constructor(obj, element = window) {
element.addEventListener('keyup', this.keyUp.bind(this), false);
element.addEventListener('keydown', this.keyDown.bind(this), false);
this.elememt = element
this.keys = {}
for (let key in obj) {
this.keys[key] = {
isDown: false,
press: obj[key],
}
}
}
addFunction(keyName, func) {
this.keys[keyName] = {
isDown: false,
press: func,
}
}
removeFunctionkeyName() {
delete this.keys[keyName]
}
keyUp(e) {
if (this.keys[e.code] && this.keys[e.code].isDown) {
e.preventDefault()
this.keys[e.code].isDown = false
}
}
keyDown(e) {
if (this.keys[e.code] && !this.keys[e.code].isDown) {
e.preventDefault()
this.keys[e.code].isDown = true
}
}
update(val) {
for (let key in this.keys) {
this.keys[key].isDown && this.keys[key].press(val)
}
}
detach() {
this.elememt.removeEventListener('keydown', this.keyDown, false)
this.elememt.removeEventListener('keyup', this.keyUp, false)
}
}
async function loadJSON(url) {
return await fetch(url).then(r => r.json())
}
let loop = (fn, hd) => {
let handle;
let stoped = false;
let lp = function (dt) {
fn(dt)
handle = requestAnimationFrame(lp)
}
lp()
let result = {
stop: _ => cancelAnimationFrame(handle),
play: _ => lp(),
toggle: _ => (stoped = !stoped) ? result.stop() : result.play()
}
return result
}
let playFromSoundCloud = async function (url) {
let audio = new Audio()
audio.crossOrigin = "anonymous"
let link = `https://api.soundcloud.com/resolve.json?url=${encodeURIComponent(url)}&client_id=17a992358db64d99e492326797fff3e8`
let result = await fetch(link).then(res => res.json()).then(json => json)
audio.src = `http://api.soundcloud.com/tracks/${result.id}/stream?client_id=17a992358db64d99e492326797fff3e8`
audio.play()
return { audio, result }
}
function mantainAspectRatio(startX = 0, startY = 0, endX = 1, endY = 1, aspectRatio = 1, mode = 'fit') {
const quadStartX = Math.min(startX, endX)
const quadStartY = Math.min(startY, endY)
const quadEndX = Math.max(startX, endX)
const quadEndY = Math.max(startY, endY)
const outerX = quadStartX
const outerY = quadStartY
const outerW = quadEndX - quadStartX
const outerH = quadEndY - quadStartY
let innerX, innerY, innerW, innerH;
if (mode === 'fit') {
if (outerH > outerW) {
if (outerW / aspectRatio > outerH) {
innerH = outerH
innerW = outerH * aspectRatio
} else {
innerW = outerW
innerH = outerW / aspectRatio
}
} else {
if (outerH * aspectRatio > outerW) {
innerW = outerW
innerH = outerW / aspectRatio
} else {
innerH = outerH
innerW = outerH * aspectRatio
}
}
} else if (mode === 'fill') {
if (outerH > outerW) {
if (outerW / aspectRatio > outerH) {
innerW = outerW
innerH = outerW / aspectRatio
} else {
innerH = outerH
innerW = outerH * aspectRatio
}
} else {
if (outerH * aspectRatio > outerW) {
innerH = outerH
innerW = outerH * aspectRatio
} else {
innerW = outerW
innerH = outerW / aspectRatio
}
}
} else {
throw new Error(`the 'mode' argument of mantainAspectRatio must be either 'fit' or 'fill', ${mode} is invalid`)
}
innerX = outerX + (outerW / 2 - innerW / 2)
innerY = outerY + (outerH / 2 - innerH / 2)
return {
outerX,
outerY,
outerW,
outerH,
innerX,
innerY,
innerW,
innerH,
}
}
return Object.assign({
mantainAspectRatio,
extendCanvas,
extendContext2D,
extendContext3D,
getRandRGB,
getRandHEX,
hexToRgb,
range,
UUID,
random,
randIntBetw,
getCanvas,
getText,
getJSON,
getBlob,
getArrayBuffer,
flatten,
getParameterByName,
loadScript,
pixelToGlCoord,
getE3ShaderData,
getE2ShaderData,
loadAll,
getFileExtension,
isEqualToAny,
hasAllStrings,
Url2Tag,
KeyListener,
BooleanicKeys,
loadJSON,
loop,
arrayOf,
arrayBy,
randomOf,
playFromSoundCloud,
array
}, {
//By 'Keith Peters' 2007 (colorized)
//https://github.com/bit101
lerp: (norm, min, max) => (max - min) * norm + min,
norm: (value, min, max) => (value - min) / (max - min),
distance: (p0, p1) => Math.sqrt((p1.x - p0.x) ** 2 + (p1.y - p0.y) ** 2),
distanceXY: (x0, y0, x1, y1) => Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2),
circleCollision: (c0, c1) => u.distance(c0, c1) <= c0.radius + c1.radius,
inRange: (value, min, max) => value >= Math.min(min, max) && value <= Math.max(min, max),
clamp: (value, min, max) => Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max)),
circlePointCollision: (point, circle) => u.distance(point, circle) < circle.radius,
pointInRect: (x, y, rect) => u.inRange(x, rect.x, rect.x + rect.width) && u.inRange(y, rect.y, rect.y + rect.height),
map: (value, sourceMin, sourceMax, destMin, destMax) => u.lerp(u.norm(value, sourceMin, sourceMax), destMin, destMax),
rangeIntersect: (min0, max0, min1, max1) => Math.max(min0, max0) >= Math.min(min1, max1) && Math.min(min0, max0) <= Math.max(min1, max1),
rectIntersect: (r0, r1) => u.rangeIntersect(r0.x, r0.x + r0.width, r1.x, r1.x + r1.width) && u.rangeIntersect(r0.y, r0.y + r0.height, r1.y, r1.y + r1.height),
})
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment