Last active
January 14, 2024 02:53
-
-
Save Jeremboo/cb55bdbff02c96f89e23 to your computer and use it in GitHub Desktop.
Useful functions on javascript es2015 : Geometry2D/3D, Math, Canvas, DOM, Color, Node...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******* | |
* MATH | |
*******/ | |
// http://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range | |
export const randInt(min, max) { | |
min = Math.ceil(min); | |
return Math.floor(Math.random() * (Math.floor(max) - min + 1)) + min; | |
} | |
export const randFloat = (min, max) => (Math.random() * (max - min) + min); | |
// Combine min() and max() | |
// https://stackoverflow.com/questions/11409895/whats-the-most-elegant-way-to-cap-a-number-to-a-segment | |
export const clamp = (min, max, x) => Math.min(Math.max(x, min), max); | |
export const lerp = (min, max, x) => max + (x * (min - max)); // (0, 10, 0.5) = 5; | |
export const inverseLerp = (min, max, x) => (x - min) / (max - min); // (0, 10, 5) = 0.5; | |
export const sqr = a => a * a; | |
// http://cwestblog.com/2012/11/12/javascript-degree-and-radian-conversion/ | |
export const radians = degrees => degrees * Math.PI / 180; | |
export const degrees = radians => radians * 180 / Math.PI; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******* | |
* GEOMETRY 2D | |
*******/ | |
export const getAngleBetweenTwoVec2 = (x1, y1, x2, y2) => Math.atan2(y1 - y2, x1 - x2); // RADIAN | |
export const getDistBetweenTwoVec2 = (x1, y1, x2, y2) => Math.sqrt(Math.sqr(y2 - y1) + Math.sqr(x2 - x1)); | |
// More complete informations returned | |
export const getDistBetweenTwoVec2 = (x1, y1, x2, y2) => { | |
const x = x1 - x2 | |
const y = y1 - y2 | |
return { x, y, force: Math.sqrt(sqr(y) + sqr(x)) } | |
} | |
/******* | |
* GEOMETRY 3D / THREE.JS | |
*******/ | |
import { Matrix4, Euler, Vector3 } from 'three'; | |
export const getRotationMatrix = vectRotation => { | |
const m = new Matrix4(); | |
const m1 = new Matrix4(); | |
const m2 = new Matrix4(); | |
const m3 = new Matrix4(); | |
m1.makeRotationX(-vectRotation.x); | |
m2.makeRotationY(-vectRotation.y); | |
m3.makeRotationY(-vectRotation.z); | |
m.multiplyMatrices(m1, m2); | |
m.multiply(m3); | |
return m; | |
}; | |
export const getRandomEuler = () => new Euler( | |
getRandomFloat(0, 6.2831), | |
getRandomFloat(0, 6.2831), | |
getRandomFloat(0, 6.2831), | |
); | |
export const getRandomNormalizedVector3 = () => new Vector3( | |
getRandomFloat(-1, 1), | |
getRandomFloat(-1, 1), | |
getRandomFloat(-1, 1), | |
); | |
export const getRandomPosWithinASphere = (r) => { | |
const theta = getRandomFloat(0, Math.PI * 2); | |
const phi = getRandomFloat(-Math.PI * 0.5, Math.PI * 0.5); | |
return new Vector3( | |
r * Math.cos(theta) * Math.cos(phi), | |
(r * 0.9) * Math.sin(phi), | |
r * Math.sin(theta) * Math.cos(phi), | |
); | |
}; | |
export const getNormalizedPosFromScreen = (x, y) => new Vector3( | |
((x / window.innerWidth) * 2) - 1, | |
-((y / window.innerHeight) * 2) + 1, | |
0.5, | |
); | |
// https://stackoverflow.com/questions/13055214/mouse-canvas-x-y-to-three-js-world-x-y-z | |
export const getDistanceBetweenNormalizedMousePosAndPos = (normalizedMousePos, pos, camera) => { | |
const nm = normalizedMousePos.clone() | |
nm.unproject(camera); | |
const dir = nm.sub(camera.position).normalize(); | |
const distance = (pos.z - camera.position.z) / dir.z; | |
const mousePos = camera.position.clone().add(dir.multiplyScalar(distance)); | |
return pos.clone().sub(mousePos); | |
}; | |
// Ty Robin <3 | |
// https://codepen.io/robin-dela/pen/dZXVrQ?editors=0010 | |
// https://threejs.org/docs/#api/core/Raycaster | |
export const addCursorMoveListener = (mesh, camera, scene, callback) => { | |
const raycaster = new Raycaster(); | |
const moveEvent = 'ontouchstart' in (window || navigator.msMaxTouchPoints) ? 'touchmove' : 'mousemove'; | |
window.addEventListener(moveEvent, (e) => { | |
const mouseVec = getNormalizedPosFromScreen( | |
e.clientX || e.touches[0].clientX, | |
e.clientY || e.touches[0].clientY, | |
); | |
raycaster.setFromCamera(mouseVec, camera); | |
const intersects = raycaster.intersectObjects(scene.children); | |
let i = 0; | |
let targetedObjectIntersected = false; | |
while (i < intersects.length && !targetedObjectIntersected) { | |
if (intersects[i].object.uuid === mesh.uuid) { | |
targetedObjectIntersected = true; | |
callback(intersects[i]); | |
} | |
i += 1; | |
} | |
}); | |
}; | |
export const worldToLocalDirection = ( object, worldDirectionVector, localDirectionVector ) => { | |
object.updateMatrixWorld() | |
localDirectionVector.copy( worldDirectionVector ).applyQuaternion( object.getWorldQuaternion().inverse() ) | |
return localDirectionVector | |
} | |
// https://medium.com/@Zadvorsky/into-vertex-shaders-part-2-emulating-the-3d-graphics-pipeline-41e06a8b49a4 | |
export const getWorldMatrix = (mesh) => new Matrix4().compose( | |
child.position, | |
new Quaterion().setFromEuler(mesh.rotation), | |
child.scale | |
); | |
export const getModelViewMatrix = (mesh) => new Matrix4().multiplyMatrices( | |
camera.matrixWorldInverse, | |
getWorldMatrix(mesh) | |
); | |
/** | |
* Get the width and the height of the field size from a specific distance | |
*/ | |
export const getCameraVisionFieldSizeFromPosition = (position = new Vector3(), camera) => { | |
// (2 * Math.tan(radians(camera.fov) / 2)) => Always the same number. Can be optimized | |
const height = (2 * Math.tan(radians(camera.fov) / 2)) * (camera.position.distanceTo(position)); | |
const width = camera.aspect * height; | |
return { | |
width, | |
height, | |
}; | |
}; | |
/******* | |
* MOBILE ORIENTATION | |
*******/ | |
// http://stackoverflow.com/questions/35283320/three-js-rotate-camera-with-both-touch-and-device-orientation | |
// https://threejs.org/examples/misc_controls_deviceorientation.html | |
import { Vector3, Euler, Quaternion } from 'three'; | |
export const updateRotationFromMobileOrientation = (object, { alpha = 0, beta = 0, gamma = 0, orient = 0, alphaOffsetAngle = 0 }) => { | |
const zee = new Vector3(0, 0, 1); | |
const euler = new Euler(); | |
const q0 = new Quaternion(); | |
const q1 = new Quaternion(-Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)); // - PI/2 around the x-axis | |
euler.set(radians(beta), radians(alpha) + alphaOffsetAngle, -radians(gamma), 'YXZ'); // 'ZXY' for the device, but 'YXZ' for us | |
object.quaternion.setFromEuler(euler); // orient the device | |
object.quaternion.multiply(q1); // camera looks out the back of the device, not the top | |
// object.quaternion.multiply(q0.setFromAxisAngle(zee, radians(orient))); // adjust for screen orientation | |
}; | |
// onMovileOrientationEvent((data) => { | |
// updateRotationFromMobileOrientation(threejsObject, data); | |
// }); | |
export const onMobileOrientationEvent = (callback) => { | |
const updateOrientation = event => { | |
const { alpha, beta, gamma } = event; | |
callback({ alpha, beta, gamma, orient: window.orientation || 0 }); | |
}; | |
window.addEventListener('deviceorientation', updateOrientation); | |
window.addEventListener('orientationChange', updateOrientation); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******* | |
* HTML | |
*******/ | |
// http://stackoverflow.com/questions/14600129/remove-html-and-javascript-comments-automatically | |
export const stripHtmlComments = (content) => content.replace(/<!--(?!>)[\S\s]*?-->/g, ''); | |
// http://stackoverflow.com/questions/211703/is-it-possible-to-get-the-position-of-div-within-the-browser-viewport-not-withi | |
export const getPositionInViewport = (e) => { | |
const offset = { | |
x: e.offsetWidth / 2, | |
y: e.offsetHeight / 2, | |
}; | |
while (e) { | |
offset.x += e.offsetLeft; | |
offset.y += e.offsetTop; | |
e = e.offsetParent; | |
} | |
if (document.documentElement && (document.documentElement.scrollTop || document.documentElement.scrollLeft)) { | |
offset.x -= document.documentElement.scrollLeft; | |
offset.y -= document.documentElement.scrollTop; | |
} else if (document.body && (document.body.scrollTop || document.body.scrollLeft)) { | |
offset.x -= document.body.scrollLeft; | |
offset.y -= document.body.scrollTop; | |
} else if (window.pageXOffset || window.pageYOffset) { | |
offset.x -= window.pageXOffset; | |
offset.y -= window.pageYOffset; | |
} | |
return offset; | |
}; | |
// https://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling/22480938#22480938 | |
export const isScrolledIntoView = (el) => { | |
const { top, bottom } = el.getBoundingClientRect(); | |
return (bottom > 0) && (top < window.innerHeight); | |
}; | |
// http://stackoverflow.com/questions/3955229/remove-all-child-elements-of-a-dom-node-in-javascript | |
export const removeAllNodeChild = (parentElm) => { | |
while (parentElm.firstChild) { | |
parentElm.removeChild(parentElm.firstChild); | |
} | |
}; | |
export const offsetTop = (element) => { | |
if (!element) { | |
return 0; | |
} | |
return element.offsetTop + offsetTop(element.offsetParent); | |
}; | |
// https://stackoverflow.com/questions/8158012/how-to-scale-an-image-to-full-screen-using-javascript/8160528#8160528 | |
export const fullScreenPreserveRatio = (elm) => { | |
const elmW = parseInt(elm.style.width, 10); | |
const elmH = parseInt(elm.style.height, 10); | |
const maxWidth = window.innerWidth; | |
const maxHeight = window.innerHeight; | |
const widthRatio = maxWidth / elmW; | |
const heightRatio = maxHeight / elmH; | |
let ratio = widthRatio; | |
if (widthRatio * elmH > maxHeight) { | |
ratio = heightRatio; | |
} | |
elm.style.width = `${elmW * ratio}px`; | |
elm.style.height = `${elmH * ratio}px`; | |
}; | |
/******* | |
* MOBILE | |
*******/ | |
// http://stackoverflow.com/questions/11381673/detecting-a-mobile-browser | |
export const mobileCheck = () => { | |
let check = false; | |
((a) => {if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); | |
return check; | |
}; | |
/******* | |
* AJAX / LOADING | |
*******/ | |
export const loadImage = (url) => new Promise((resolve, reject) => { | |
const downloadingImage = new Image(); | |
downloadingImage.onload = () => { | |
resolve(downloadingImage); | |
}; | |
downloadingImage.onerror = () => { | |
reject(); | |
}; | |
downloadingImage.src = url; | |
}); | |
// http://stackoverflow.com/questions/6150289/how-to-convert-image-into-base64-string-using-javascript | |
export const toDataUrl = (url, callback) => { | |
const xhr = new XMLHttpRequest(); | |
xhr.responseType = 'blob'; | |
xhr.onload = () => { | |
const reader = new FileReader(); | |
reader.onloadend = () => { | |
callback(reader.result); | |
}; | |
reader.readAsDataURL(xhr.response); | |
}; | |
xhr.open('GET', url); | |
xhr.send(); | |
}; | |
// http://stackoverflow.com/questions/14388452/how-do-i-load-a-json-object-from-a-file-with-ajax | |
export const fetchJSONFile = (path, callback) => { | |
const httpRequest = new XMLHttpRequest(); | |
httpRequest.onreadystatechange = () => { | |
if (httpRequest.readyState === 4) { | |
if (httpRequest.status === 200) { | |
const data = JSON.parse(httpRequest.responseText); | |
if (callback) callback(data); | |
} | |
} | |
}; | |
httpRequest.open('GET', path); | |
httpRequest.send(); | |
}; | |
// LoadVideo | |
const md = new MobileDetect(window.navigator.userAgent); | |
const onIOS = md.is('iPhone'); // FIX use native detection | |
export const addVideo = (videoElement, url) => new Promise((resolve, reject) => { | |
if (onIOS) videoElement.autoplay = true; | |
videoElement.addEventListener('loadeddata', () => { | |
loaded = true; | |
loader.style.display = 'none'; | |
videoElement.pause(); | |
resolve(); | |
}); | |
videoElement.addEventListener('error', reject); | |
videoElement.src = url; | |
}); | |
/******* | |
* ANIMATION | |
*******/ | |
export const easing = (target, value, { vel = 0.03, update = f => f, callback = f => f } = {}) => { | |
const f = (target - value) * vel; | |
update(f); | |
if (Math.abs(f) < 0.001) { | |
callback(); | |
} | |
}; | |
export const easingRAF = (target, value, { vel = 0.03, update = f => f, callback = f => f } = {}) => { | |
const f = (target - value) * vel; | |
const newValue = value + f; | |
update(newValue); | |
if (Math.abs(f) < 0.001) { | |
update(target); | |
callback(); | |
return; | |
} | |
requestAnimationFrame(easing.bind(this, target, newValue, { vel, update, callback })); | |
}; | |
/******* | |
* URL | |
*******/ | |
// http://stackoverflow.com/questions/979975/how-to-get-the-value-from-the-get-parameters | |
export const queryString = () => { | |
// This function is anonymous, is executed immediately and | |
// the return value is assigned to QueryString! | |
const queryString = {}; | |
const query = window.location.search.substring(1); | |
const vars = query.split('&'); | |
for (let i = 0; i < vars.length; i++) { | |
const pair = vars[i].split('='); | |
// If first entry with this name | |
if (typeof queryString[pair[0]] === 'undefined') { | |
queryString[pair[0]] = decodeURIComponent(pair[1]); | |
// If second entry with this name | |
} else if (typeof queryString[pair[0]] === 'string') { | |
const arr = [queryString[pair[0]], decodeURIComponent(pair[1])]; | |
queryString[pair[0]] = arr; | |
// If third or later entry with this name | |
} else { | |
queryString[pair[0]].push(decodeURIComponent(pair[1])); | |
} | |
} | |
return queryString; | |
}; | |
// http://stackoverflow.com/questions/824349/modify-the-url-without-reloading-the-page/3354511#3354511 | |
export const updateQueryStringUrlParameter = (key, value) => { | |
const uri = window.location.href; | |
const re = new RegExp(`([?&])${key}=.*?(&|$)`, 'i'); | |
const separator = uri.indexOf('?') !== -1 ? '&' : '?'; | |
const newUrl = uri.match(re) | |
? uri.replace(re, `$1${key}=${value}$2`) | |
: `${uri}${separator}${key}=${value}` | |
; | |
window.history.pushState({}, '', newUrl); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******* | |
* [R, G, B] | |
*******/ | |
export const getRGBFromImageData = (imageData, lineLenght, x, y) => { | |
const pixelPosition = (y * (lineLenght * 4)) + (x * 4); | |
return [ | |
imageData[pixelPosition], | |
imageData[pixelPosition + 1], | |
imageData[pixelPosition + 2], | |
]; | |
}; | |
/******* | |
* HEXA && CSS RGB | |
*******/ | |
export const getRandomHexa = () => | |
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f'][Math.floor(Math.random() * 16)] | |
; | |
export const getRandomColor = (type = '#') => { | |
const color = `${type}`; | |
for (let i = 0; i < 6; i++ ) { | |
color += getRandomHexa(); | |
} | |
return color; | |
} | |
export const getCSSRGBColor = RGB => | |
(`rgb(${Math.round(RGB[0])}, ${Math.round(RGB[1])}, ${Math.round(RGB[2])})`) | |
; | |
// http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb | |
export const componentToHex = (c) => { | |
const hex = c.toString(16); | |
return hex.length === 1 ? `0${hex}` : hex; | |
}; | |
export const RBGToHex = (RGB, prepend = '#') => | |
prepend + componentToHex(RGB[0]) + componentToHex(RGB[1]) + componentToHex(RGB[2]) | |
; | |
/******* | |
* OHTER COLOR TYPE | |
*******/ | |
import { Vector4 } from 'three'; | |
export const getVec4Color = color => new Vector4( | |
color[0] / 256, | |
color[0] / 256, | |
color[0] / 256, | |
1 | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******* | |
* CANVAS | |
*******/ | |
export const canvasBuilder = (width = window.innerWidth, height = window.innerHeight) => { | |
const canvas = document.createElement('canvas'); | |
canvas.width = width; | |
canvas.height = height; | |
const context = canvas.getContext('2d'); | |
return { | |
canvas, | |
context, | |
getImageData: () => context.getImageData(0, 0, width, height).data, | |
}; | |
}; | |
/******* | |
* NOISE | |
*******/ | |
// https://gist.github.com/donpark/1796361 | |
export const randomNoise = (width = window.innerWidth, height = window.innerHeight) => { | |
const { canvas, context, getImageData } = canvasBuilder(width, height); | |
const pixels = getImageData(); | |
const length = pixels.length; | |
let i = 0; | |
while (i < length) { | |
pixels[i++] = pixels[i++] = pixels[i++] = (Math.random() * 256) | 0; | |
pixels[i++] = 255; | |
} | |
context.putImageData(imageData, 0, 0); | |
return canvas; | |
}; | |
export const perlinNoise = (width = window.innerWidth, height = window.innerHeight) => { | |
const { canvas, context } = canvasBuilder(width, height); | |
const noise = randomNoise(width, height); | |
context.save(); | |
/* Scale random iterations onto the canvas to generate Perlin noise. */ | |
let size; | |
for (size = 4; size <= width; size *= 2) { | |
const x = (Math.random() * (width - size)) | 0; | |
const y = (Math.random() * (height - size)) | 0; | |
context.globalAlpha = 10 / size; | |
context.drawImage(noise, x, y, size, size, 0, 0, width, height); | |
} | |
context.restore(); | |
return canvas; | |
}; | |
export const gradientPerlinNoise = (width = window.innerWidth, height = window.innerHeight ) => { | |
const { canvas, context } = canvasBuilder(width, height); | |
const pNoise = perlinNoise(width, height); | |
const gradient = context.createLinearGradient(0, 0, width, 0); | |
gradient.addColorStop(0.1, 'rgba(0, 0, 0, 0.8)'); | |
gradient.addColorStop(0.9, 'rgba(255, 255, 255, 0.6)'); | |
context.drawImage(pNoise, 0, 0); | |
context.fillStyle = gradient; | |
context.fillRect(0, 0, width, height); | |
return canvas; | |
}; | |
/******* | |
* CANVAS OR IMAGE | |
*******/ | |
window.URL = window.URL || window.webkitURL; | |
export const applyImageToCanvas = (url, w, h, handling) => new Promise((resolve, reject) => { | |
const xhr = new XMLHttpRequest(); | |
xhr.open('GET', url, true); | |
xhr.responseType = 'blob'; | |
xhr.onload = (e) => { | |
if (e.target.status === 200) { | |
const blob = e.target.response; | |
const image = new Image(); | |
image.crossOrigin = 'Anonymous'; | |
image.onload = () => { | |
const width = w || image.width; | |
const height = h || image.height; | |
const canvasB = canvasBuilder(width, height); | |
const { canvas, context } = canvasB; | |
context.globalAlpha = 1; | |
switch(handling) { | |
case 'mosaique': | |
const nbrTileColumn = width / image.width; | |
const nbrTileLine = height / image.height; | |
let i; | |
let j; | |
for (i = 0; i < nbrTileLine; i++) { | |
for (j = 0; j < nbrTileColumn; j++) { | |
context.drawImage(image, j * image.width, i * image.height); | |
} | |
} | |
break; | |
case 'cover': | |
break; | |
default: | |
context.drawImage(image, 0, 0, width, height); | |
break; | |
} | |
window.URL.revokeObjectURL(blob); | |
// resolve(Object.assign(canvasB, { image })); | |
resolve(canvas); | |
}; | |
image.onerror = () => { | |
reject('Err : Canvas cannot be loaded'); | |
}; | |
image.src = window.URL.createObjectURL(blob); | |
} | |
}; | |
xhr.send(); | |
}); | |
// https://developer.mozilla.org/fr/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation | |
export const globalCompositeOpetationSupported = operation => { | |
const { context } = canvasBuilder(); | |
context.globalCompositeOperation = operation; | |
return (context.globalCompositeOperation === operation); | |
}; | |
export const applyGlobalCompositeOperation = (existingCanvasImg, newCanvasImg, operation) => { | |
const { canvas, context } = canvasBuilder(existingCanvasImg.width, existingCanvasImg.height); | |
context.drawImage(existingCanvasImg, 0, 0); | |
context.globalCompositeOperation = operation; | |
if (context.globalCompositeOperation !== operation) { | |
// When the operation it doesn't supported | |
// https://github.com/mozilla/pdf.js/issues/3900 | |
if (operation === 'multiply') { | |
context.globalAlpha = 0.5; | |
} | |
} | |
context.drawImage(newCanvasImg, 0, 0); | |
return canvas; | |
}; | |
export const tintCanvasImg = (canvasImg, color) => { | |
const w = canvasImg.width; | |
const h = canvasImg.height; | |
const { canvas, context } = canvasBuilder(w, h); | |
context.fillStyle = color; | |
context.fillRect(0, 0, w, h); | |
const tintedCanvas = applyGlobalCompositeOperation(canvas, canvasImg, 'multiply'); | |
return applyGlobalCompositeOperation(tintedCanvas, canvasImg, 'destination-in'); | |
}; | |
export const tintCanvasImgToIE = (canvasImg, color, maskCanvasImg) => { | |
const w = canvasImg.width; | |
const h = canvasImg.height; | |
const mask = maskCanvasImg || canvasImg; | |
const { canvas, context } = canvasBuilder(w, h); | |
context.fillStyle = color; | |
context.fillRect(0, 0, w, h); | |
const tintedCanvas = canvasBuilder(w, h); | |
tintedCanvas.context.drawImage(canvas, 0, 0); | |
tintedCanvas.context.globalAlpha = 0.4; | |
tintedCanvas.context.drawImage(canvasImg, 0, 0); | |
tintedCanvas.context.globalAlpha = 1; | |
return applyGlobalCompositeOperation(tintedCanvas, mask, 'destination-in'); | |
}; | |
export const applyShadowToCanvasImg = (canvasImg, color = '#494949') => { | |
const blurSize = 10; | |
const { canvas, context } = canvasBuilder(canvasImg.width + blurSize, canvasImg.height + (blurSize * 5)); | |
context.shadowOffsetX = blurSize * 0.4; | |
context.shadowOffsetY = blurSize * 0.3; | |
context.shadowColor = color; | |
context.shadowBlur = blurSize * 0.8; | |
context.drawImage(canvasImg, blurSize * 0.5, 0); | |
return canvas; | |
}; | |
export const flipCanvasImg = canvasImg => { | |
const w = canvasImg.width; | |
const h = canvasImg.height; | |
const { canvas, context } = canvasBuilder(w, h); | |
context.save(); | |
context.scale(-1, 1); | |
context.drawImage(canvasImg, -w, 0, w, h); | |
context.restore(); | |
return canvas; | |
}; | |
export const resizeCanvasImg = (canvasImg, w, h) => { | |
const { canvas, context } = createCanvas(w, h); | |
context.drawImage(canvasImg, 0, 0, w, h); | |
return canvas; | |
}; | |
/******* | |
* SHAPE | |
*******/ | |
export const gradientArc = (ctx, { x = 0, y = 0, size = 10, ratio = 0.5 } = {}) => { | |
const canvasB = canvasBuilder(ctx.canvas.width, ctx.canvas.height); | |
// create with the temps canvas | |
const gradStyle = canvasB.context.createRadialGradient(x, y, 1, x, y, size); | |
gradStyle.addColorStop(0, 'rgba(0, 0, 0, 1)'); | |
gradStyle.addColorStop(ratio, 'rgba(0, 0, 0, 0.5)'); | |
gradStyle.addColorStop(1, 'rgba(0, 0, 0, 0)'); | |
canvasB.context.fillStyle = gradStyle; | |
canvasB.context.arc(x, y, size, 0, Math.PI * 2); | |
canvasB.context.fill(); | |
ctx.drawImage(canvasB.canvas, 0, 0); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******* | |
* TESTS | |
*******/ | |
export const testPerf = (fct, ...params) => { | |
const t0 = performance.now(); | |
const result = fct(...params); | |
const t1 = performance.now(); | |
console.log(`PERF === ${t1 - t0} ms.`); | |
return result; | |
}; | |
export const getAndTestExtention = (filePath, extentionToTest = ['.mp4', '.gif', '.png', '.jpg']) => { | |
if (!existsSync(filePath)) { | |
return false; | |
} | |
return (extentionToTest.indexOf(extname(filePath)) !== -1); | |
}; | |
export const isJSON = (str) => { | |
try { | |
JSON.parse(str); | |
} catch (e) { | |
return false; | |
} | |
return true; | |
}; | |
/******* | |
* ARRAY | |
*******/ | |
export const traverseArr = (arr, fct) => { | |
let i = arr.length; | |
for (i; i > 0; i--) { | |
fct(arr[i]); | |
} | |
}; | |
// http://stackoverflow.com/questions/5767325/how-to-remove-a-particular-element-from-an-array-in-javascript | |
export const removeInstanceFromArray = (arr, instance) => { | |
const index = arr.indexOf(instance); | |
if (index !== -1) arr.splice(index, 1); | |
return arr; | |
}; | |
/******* | |
* DATE | |
*******/ | |
export const timestampToClockFormat = (tmp) => { | |
const date = new Date(tmp); | |
const addZero = (v) => v < 10 ? `0${v}` : v; | |
return `${addZero(date.getHours() - ((tmp < 3600000) ? 1 : 0)) | |
}:${addZero(date.getMinutes()) | |
}:${addZero(date.getSeconds()) | |
}`; | |
}; | |
export const getFrenchDateAndHour = (d) => | |
`${d.toLocaleDateString('fr-FR')} à ${d.toTimeString().slice(0, -15)}` | |
; | |
/******* | |
* HELPERS | |
*******/ | |
export const logError = (message) => { | |
console.warn(`### ERROR -> | |
${message} | |
`); | |
}; | |
export const logEvent = (message) => { | |
console.log(`${getFrenchDateAndHour()} -> | |
${message} | |
`); | |
}; | |
export const logInfo = (message) => { | |
console.log(` - ${message}`); | |
}; | |
/******* | |
* OTHER | |
*******/ | |
export const componentToHex = (c) => { | |
const hex = c.toString(16); | |
return hex.length === 1 ? `0${hex}` : hex; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* DIRECTORY | |
* require('fs'); | |
* require('camelcase'); | |
*/ | |
/** | |
* test if a path exist | |
* @param {String} path the path to test | |
* @return {Boolean} answer | |
*/ | |
const pathExist = path => { | |
if (!fs.existsSync(path)) { | |
console.log(`ERROR : ${path} does not exist !`); | |
return false; | |
} | |
return true; | |
}; | |
/** | |
* Create a directory into the path passed into parameter | |
* @param {String} parentPath valid parent path | |
* @param {String} [type=''] name of the type of path | |
* @return {Object} name and pathName | |
*/ | |
const createDir = (parentPath, type = '') => { | |
const name = ask(`${type} name : `); | |
const nameToCamelCase = camelCase(name); | |
const path = `${parentPath}${nameToCamelCase}`; | |
if (!fs.existsSync(path)) { | |
fs.mkdirSync(path); | |
} else { | |
console.log(`ERROR : ${path} already exist ! Please write another.`); | |
return createDir(parentPath, type); | |
} | |
return { name, path }; | |
}; | |
/** | |
* Create a directory into the path passed into parameter | |
* @param {String} parentPath valid parent path | |
* @param {Array} arr array of directory name | |
* @return {String} the final path | |
*/ | |
const createDirsDepth = (parentPath, arr) => { | |
let i; | |
let recurcivePath = `${parentPath}`; | |
const length = arr.length; | |
for (i = 0; i < length; i++) { | |
createDir(recurcivePath, arr[i]); | |
recurcivePath += `${arr[i]}/`; | |
} | |
return recurcivePath; | |
}; | |
/** | |
* ASK | |
* require('readline-sync'); | |
* require('fs'); | |
*/ | |
/** | |
* ask a simple question | |
* @param {String} question the question to ask to the user | |
* @return {String} the answer from user | |
*/ | |
const ask = question => readlineSync.question(question); | |
/** | |
* ask a question to selected an answer into an array. | |
* @param {Array} arr array of possiblities | |
* @param {String} type type of values to select | |
* @return {arr[]} one value of the array | |
*/ | |
const askWitchChoice = (arr, type = '') => | |
arr[readlineSync.keyInSelect(arr, `Witch ${type} ? : `)] | |
; | |
/** | |
* ask a question to select a child directory from a path | |
* @param {String} dirPath the parent path | |
* @param {String} type type of values to select | |
* @return {String} directory path selected | |
*/ | |
const askWitchChildDir = (dirPath, type) => { | |
const dir = fs.readdirSync(dirPath); | |
removeInstanceFromArray(dir, '.DS_Store'); | |
removeInstanceFromArray(dir, 'data.json'); | |
return askWitchChoice(dir, type); | |
}; | |
/** | |
* ask a boolean question. | |
* @param {String} question the question to ask to the user | |
* @param {Boolean} [defaultValue=true] the default value if the answer is null | |
* @return {Boolean} answer | |
*/ | |
const askBool = (question, defaultValue = true) => { | |
const trueValue = ['y', 'yes', 'yeah', 'yep', 'oui']; | |
const falseValue = ['n', 'no', 'nah', 'nope']; | |
if (defaultValue) { | |
trueValue.push(''); | |
} else { | |
falseValue.push(''); | |
} | |
const answer = readlineSync.question( | |
`${question} (${defaultValue ? 'yes' : 'no'})`, { trueValue, falseValue } | |
); | |
if (answer === true || answer === false) { | |
return answer; | |
} | |
console.log('Please, answer with a correct value.') | |
return askBool(question, defaultValue); | |
}; | |
/******* | |
* KEY | |
*******/ | |
export const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); | |
export const guid = () => `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Move the camera AROUND the tageted position. | |
* @param {Vector3} position targeted position | |
* @param {Vector3} target targeted position | |
* @param {Function} onComplete callback after the aniumation | |
* @return {Tween} Return the tween to add it on another animation | |
*/ | |
getTmMoveCameraAroundTarget (position, target, onComplete = f => f) { | |
// Compute initial vector2 | |
const originVec2 = { x: target.x, y: target.z } // TODO could be global | |
const currentVec2 = { x: this.camera.position.x, y: this.camera.position.z } | |
const targetedVec2 = { x: position.x, y: position.z } | |
// Set the current values | |
const v = { | |
angle: AngleVec2(currentVec2, originVec2), // current angle | |
dist: DistVec2(currentVec2, originVec2), // current distance | |
y: this.camera.position.y // current height | |
} | |
// Animate to the targeted values | |
return TweenLite.to(v, 0.5, { | |
angle: AngleVec2(targetedVec2, originVec2), // targeted angle | |
dist: DistVec2(targetedVec2, originVec2), // targeted distance | |
y: position.y, // targeted height | |
onComplete, | |
onUpdate: () => { | |
// Update the camera position to move around the origin | |
this.camera.position.set( | |
originVec2.x + (v.dist * Math.cos(v.angle)), | |
v.y, | |
originVec2.y + (v.dist * Math.sin(v.angle)) | |
) | |
// Always look at the origin | |
this.camera.lookAt(target) | |
} | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const GetSVGElmWithAttribute = (wrapper, svgElmName, attributeName) => | |
Array.from(wrapper.querySelectorAll(svgElmName)).filter((path) => { | |
const attr = path.getAttribute(attributeName) | |
return attr && attr !== 'none' | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******* | |
* ENCODER / DECODER | |
*******/ | |
// https://stackoverflow.com/questions/15341912/how-to-go-from-blob-to-arraybuffer | |
// https://stackoverflow.com/questions/26946548/javascript-file-uploading-readasarraybuffer | |
export const arrayBufferToBlob = (arrayBuffer) => { | |
const uint8Array = new Uint8Array(arrayBuffer); | |
return new Blob([uint8Array.buffer]); | |
}; | |
export const blobToArrayBuffer = blob => new Promise((resolve, reject) => { | |
const fileReader = new FileReader(); | |
fileReader.onload = (e) => { | |
resolve(new Int8Array(e.target.result)); | |
}; | |
// TODO add reject | |
fileReader.readAsArrayBuffer(blob); | |
}); | |
export const audioBlobToAudio = audioBlob => new Promise((resolve, reject) => { | |
const audioUrl = URL.createObjectURL(audioBlob); | |
const audio = new Audio(audioUrl); | |
audio.addEventListener('loadedmetadata', () => { | |
audio.addEventListener('durationchange', () => { | |
if (audio.duration !== Infinity) { | |
resolve(audio); | |
} | |
}); | |
}); | |
audio.load(); | |
}); | |
// https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String | |
export const uint8ArrayToString = buf => String.fromCharCode.apply(null, buf); | |
export const arrayBufferToString = buf => String.fromCharCode.apply(null, new Uint8Array(buf)); | |
export const stringToArrayBuffer = (str) => { | |
const buf = new ArrayBuffer(str.length); | |
const bufView = new Uint8Array(buf); | |
for (let i = 0, strLen = str.length; i < strLen; i++) { | |
bufView[i] = str.charCodeAt(i); | |
} | |
return buf; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment