From: Ben Beltran Date: Thu, 10 Nov 2016 05:41:37 +0000 (-0600) Subject: Add basic animator project X-Git-Url: https://git.r.bdr.sh/rbdr/heart/commitdiff_plain/910278aad4a0b47dc40112839a18bc8e590302e5 Add basic animator project --- diff --git a/README.md b/README.md index e55907e..6238f46 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ -# heart -The heart +# Heart + +Renders a colorful heart + +## How to run + +Serve the files in the root directory. For example using: `python -m SimpleHTTPServer` + +## Setting up + +Though this project is mostly static you should run `yarn install` or +`npm install` so you can lint and document the project. + +## Generating documentation + +1. Run `npm run document`. +2. Serve the files that have been generated in the `doc` directory. If + already serving the files, just go to `/doc` + +## Linting + +1. Run `npm lint` diff --git a/css/app.css b/css/app.css new file mode 100644 index 0000000..5f63708 --- /dev/null +++ b/css/app.css @@ -0,0 +1,9 @@ +* { + margin: 0; + padding: 0; +} + +#heart-app-entry-point { + height: 100vh; + width: 100vw; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..cf7fa03 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + ❤️ + + + + + + + + +
+ + diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..25d581d --- /dev/null +++ b/js/app.js @@ -0,0 +1,31 @@ +'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); 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);