X-Git-Url: https://git.r.bdr.sh/rbdr/heart/blobdiff_plain/aef6eff607f97150506522b8f8df89ac1ab2f72c..910278aad4a0b47dc40112839a18bc8e590302e5:/js/lib/heart_renderer.js diff --git a/js/lib/heart_renderer.js b/js/lib/heart_renderer.js new file mode 100644 index 0000000..f6ece86 --- /dev/null +++ b/js/lib/heart_renderer.js @@ -0,0 +1,141 @@ +'use strict'; + +((window) => { + + const kColorIteratorLimit = 256; + const kRedSpeed = 0.1; + const kGreenSpeed = 0.2; + const kBlueSpeed = 0.15; + + /** + * Renders a Heart, has its own canvas, which will be placed in the element + * set by calling #render. + * + * @class HeartRenderer + * @param {object} config configuration, extends any member of the renderer + */ + const HeartRenderer = class HeartRenderer { + + constructor(config) { + + /** + * The instance of the heart renderer being used + * + * @memberof HeartRenderer + * @instance + * @name canvas + * @type HTMLCanvasElement + * @default A brand new full width and height canvas + */ + this.canvas = window.document.createElement('canvas'); + this.canvas.style.height = '100%'; + this.canvas.style.width = '100%'; + + /** + * The maximum fps that will be used + * + * @memberof HeartRenderer + * @instance + * @name fps + * @type Number + * @default 60 + */ + this.fps = 60; + + this._animating = false; // The status of the animation. + this._previousFrameTime = Date.now(); // The timestamp of the last frame for fps control + this._currentColor = { // The current color that will be painted + red: 100, + blue: 0, + green: 50 + }; + + Object.assign(this, config); + } + + /** + * Attaches the canvas to an HTML element + * + * @memberof HeartRenderer + * @function render + * @instance + * @param {HTMLElement} element the element where we will attach our canvas + */ + render(element) { + + element.appendChild(this.canvas); + } + + /** + * Gets the context from the current canvas and starts the animation process + * + * @memberof HeartRenderer + * @function activate + * @instance + */ + activate() { + + const context = this.canvas.getContext('2d'); + this._startAnimating(context); + } + + /** + * Stops the animation process + * + * @memberof HeartRenderer + * @function deactivate + * @instance + */ + deactivate() { + + this._stopAnimating(); + } + + // Starts the animation loop + _startAnimating(context) { + + this._frameDuration = 1000 / this.fps; + this._animating = true; + + this._animate(context); + } + + // Stops the animation on the next frame. + _stopAnimating() { + + this._animating = false; + } + + // Runs the animation step controlling the FPS + _animate(context) { + + if (!this._animating) { + return; + } + + window.requestAnimationFrame(this._animate.bind(this, context)); + + const currentFrameTime = Date.now(); + const delta = currentFrameTime - this._previousFrameTime; + + if (delta > this._frameDuration) { + this._previousFrameTime = Date.now(); + this._animateStep(context, delta); + } + } + + // The actual animation processing function. + _animateStep(context, delta) { + + this._currentColor.red = Math.round(this._currentColor.red + delta * kRedSpeed) % kColorIteratorLimit; + this._currentColor.green = Math.round(this._currentColor.green + delta * kGreenSpeed) % kColorIteratorLimit; + this._currentColor.blue = Math.round(this._currentColor.blue + delta * kBlueSpeed) % kColorIteratorLimit; + + context.fillStyle = `rgb(${this._currentColor.red}, ${this._currentColor.green}, ${this._currentColor.blue})`; + context.fillRect(0, 0, this.canvas.scrollWidth, this.canvas.scrollHeight); + } + }; + + + window.HeartRenderer = HeartRenderer; +})(window);