--- /dev/null
+'use strict';
+
+// Sets up the application
+
+((window) => {
+
+ const internals = {};
+
+ internals.onLoad = () => {
+
+ const mainElement = window.document.getElementById('heart-app-entry-point');
+ const heartRenderer = new HeartRenderer();
+
+ heartRenderer.render(mainElement);
+ heartRenderer.activate();
+
+ /**
+ * Exported global object. This will contain the instance of the heart
+ * renderer being used. It is set up on load.
+ *
+ * @name App
+ * @type Object
+ * @property {HeartRenderer} heartRenderer The instance of the heart renderer being used.
+ */
+ window.App = {
+ heartRenderer
+ };
+ };
+
+ window.addEventListener('load', internals.onLoad);
+})(window);
--- /dev/null
+'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);