UNPKG

immaterial-design-ripple

Version:
276 lines (237 loc) 8.85 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.requestAnimationFrame = requestAnimationFrame; exports.promiseEvent = promiseEvent; exports.createContext2d = createContext2d; exports.getImageData = getImageData; exports.transparentize = transparentize; exports.getTimingFunction = getTimingFunction; exports.createRenderSchedule = createRenderSchedule; exports.getPixelColor = getPixelColor; var _bluebird = require('bluebird'); var _bluebird2 = _interopRequireDefault(_bluebird); var _easingJs = require('easing-js'); var _easingJs2 = _interopRequireDefault(_easingJs); var _objectAssign = require('object-assign'); var _objectAssign2 = _interopRequireDefault(_objectAssign); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * 利用可能な非同期関数でcallbackを実行する * * @function requestAnimationFrame * @param {Function} [callback] * @return undefined */ function requestAnimationFrame(callback) { return window.requestAnimationFrame(callback); } /** * 指定した要素のイベントを待つプロミスを返す * * @function promiseEvent * @param {Element} target イベントを取得する要素 * @param {String} eventName 取得するイベント名 * @return {Promise<EventTarget>} deferredEvent 取得したイベント */ function promiseEvent(target, eventName) { return new _bluebird2.default(function (resolve) { var onceListener = function onceListener(event) { target.removeEventListener(eventName, onceListener); resolve(event); }; target.addEventListener(eventName, onceListener); }); } /** * 指定した大きさのcontext2dを返す * * @function createContext2d * @param {Number} width contextの幅 * @param {Number} height contextの高さ * @param {Object} [options] * @param {Object} [options.pixelated=true] アンチエイリアスを切る * @return {CanvasRenderingContext2D} */ function createContext2d(width, height) { var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; canvas.style.position = 'absolute'; canvas.style.top = 0; canvas.style.right = 0; canvas.style.bottom = 0; canvas.style.left = 0; var context = canvas.getContext('2d'); if (options.pixelated) { context.mozImageSmoothingEnabled = false; context.msImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; } return context; } /** * contextと同じ大きさの空のimageDataを返す * * @function getImageData * @param {HTMLCanvasElement} canvas 大きさの基準となるcanvas * @return {ImageData} */ function getImageData(canvas) { var width = canvas.width; var height = canvas.height; var newContext = document.createElement('canvas').getContext('2d'); newContext.canvas.width = width; newContext.canvas.height = height; return newContext.getImageData(0, 0, width, height); } /** * canvasを透明化、opacity:0でcanvasを破棄 * * @function transparentize * @param {Element} element 透明化させ、破棄する要素 * @param {Object} [options] * @param {Number} [options.opacityStep=0.02] 1フレームの透明化進行度 * @return {Promise<null>} animation 要素破棄時にfullfill */ function transparentize(element) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var elementStyle = element.style; var opts = (0, _objectAssign2.default)({ opacityStep: 0.02 }, options); return new _bluebird2.default(function (resolve) { var opacity = 1; var render = function render() { opacity -= opts.opacityStep; if (opacity <= 0) { elementStyle.opacity = 0; return resolve(); } elementStyle.opacity = opacity; return requestAnimationFrame(render); }; requestAnimationFrame(render); }).then(function () { if (element.parentNode) { element.parentNode.removeChild(element); } }); } /** * easingJsで定義された関数名であれば、その関数を返し * 引数が関数であれば、そのまま返す * それ以外はnull * * @function getTimingFunction * @param {String|Function} name EasingJsの関数名か、独自で関数を定義 * @return {Function|null} timingFunction (t,b,c,d)を受け取るイージング関数。未定義の関数名ならnull */ function getTimingFunction() { var name = arguments.length <= 0 || arguments[0] === undefined ? 'easeInBack' : arguments[0]; if (typeof name === 'function') { return name; } if (_easingJs2.default[name]) { return _easingJs2.default[name]; } return null; } /** * 指定した大きさのimageDataを作成し * 波形アニメーションとして表示するフレーム番号を計算する * x,yを始点とする * * 返される配列の値は大きさからpixelSizeを割ったもの。 * * @function createRenderSchedule * @param {Number} x 波形アニメーションの始点x * @param {Number} y 波形アニメーションの始点y * @param {Number} width 波形アニメーションの幅 * @param {Number} height 波形アニメーションの高さ * @param {Object} [options] * @param {Number} [options.pixelSize] ピクセル1粒の大きさ * @param {Number} [options.bitCrash=null] 境界にノイズを入れる、値はノイズの強さ * @param {String|Function} [options.timingFunction='easeInQuint'] フレーム番号のイージング関数名 * @return {Object} RenderSchedule * @return {Array} RenderSchedule.data yとxからなる二次元配列。表示するフレーム番号を値に持つ * @return {Number} RenderSchedule.width ピクセルの横の個数 * @return {Number} RenderSchedule.height ピクセルの縦の個数 * @return {Number} RenderSchedule.pixelSize ピクセル1粒の大きさ * @return {Function|null} RenderSchedule.easedBy フレーム番号の調整に使用した関数 */ function createRenderSchedule(x, y, width, height) { var options = arguments.length <= 4 || arguments[4] === undefined ? {} : arguments[4]; var opts = Object.create(options); if (opts.pixelSize === undefined) { opts.pixelSize = 1; } var dataWidth = Math.ceil(width / opts.pixelSize); var dataHeight = Math.ceil(height / opts.pixelSize); var data = []; // ピクセルごとの表示を開始するフレーム番号を定義する var c = 0; // maxFrame for (var i = 0; i < dataHeight; i++) { if (data[i] === undefined) { data[i] = []; } for (var j = 0; j < dataWidth; j++) { var originalX = opts.pixelSize * j; var originalY = opts.pixelSize * i; // x, yからの距離をピクセル基準で求める var distanceX = Math.abs(originalX - x); var distanceY = Math.abs(originalY - y); var distance = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2)); var showFrame = Math.floor(distance / opts.pixelSize); if (c < showFrame) { c = showFrame; } data[i].push(showFrame); } } // アニメーションの緩急を変更する // http://d.hatena.ne.jp/nakamura001/20111117/1321539246 var timingFunction = getTimingFunction(opts.timingFunction); if (timingFunction) { var d = 1; for (var _i = 0; _i < data.length; _i++) { for (var _j = 0; _j < data[_i].length; _j++) { var b = data[_i][_j]; // showFrame var t = c > 0 ? b / c : 0; // distanceRate data[_i][_j] = Math.floor(timingFunction(t, b, c, d)); // 5フレーム以降は境界部分のジャギーを目立たせる(ささくれさせる) if (opts.bitCrash > 1 && b > 5) { data[_i][_j] += Math.floor(opts.bitCrash * Math.random()); } } } } return { data: data, width: dataWidth, height: dataHeight, pixelSize: opts.pixelSize, easedBy: timingFunction }; } /** * 指定したcolorNameのrgbaを返す(CanvasRenderingContext2D経由) * * @function getPixelColor * @param {String} colorName CanvasRenderingContext2D.fillStyleの値 * @return {Array} color [r,g,b,a] */ function getPixelColor() { var colorName = arguments.length <= 0 || arguments[0] === undefined ? 'rgba(0,0,0,.3)' : arguments[0]; var context = document.createElement('canvas').getContext('2d'); context.canvas.width = 1; context.canvas.width = 1; context.fillStyle = colorName; context.fillRect(0, 0, 1, 1); // splat構文を使用するとエラーになるので、配列に変換する // const [r,g,b,a] = document.createElement('canvas').getContext('2d').getImagedata(...).data // => TypeError: Invalid attempt to destructure non-iterable instance return [].slice.call(context.getImageData(0, 0, 1, 1).data); }