aboutsummaryrefslogtreecommitdiff
path: root/lib/components/wave_renderer.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/components/wave_renderer.js')
-rw-r--r--lib/components/wave_renderer.js167
1 files changed, 167 insertions, 0 deletions
diff --git a/lib/components/wave_renderer.js b/lib/components/wave_renderer.js
new file mode 100644
index 0000000..022f9ff
--- /dev/null
+++ b/lib/components/wave_renderer.js
@@ -0,0 +1,167 @@
+import Vue from 'vue';
+import DataService from '../services/data';
+
+/* global window */
+
+const internals = {
+
+ // Constants
+
+ kColors: {
+ donatello: 'purple',
+ leonardo: 'blue',
+ michaelangelo: 'orange',
+ raphael: 'red'
+ },
+ kFrequency: 100,
+ kGradientFade: 0.1,
+ kMinValue: 25,
+ kMaxValue: 100,
+ kRandomJitter: 10,
+ kTargetFPS: 60,
+
+ // Utility functions
+
+ scale(value, min, max) {
+
+ return ((value - min) / (max - min)) * (internals.kMaxValue - internals.kMinValue) - internals.kMinValue;
+ }
+};
+
+/**
+ * The wave renderer, draws some waves in a canvas to represent a set of
+ * cateogirzed averages
+ *
+ * @class WaveRenderer
+ */
+export default Vue.component('waveRenderer', {
+ template: '<transition name="fade">' +
+ '<canvas v-show="state === 1" class="wave-renderer"></canvas>' +
+ '</transition>',
+
+ data: DataService.data,
+
+ computed: {
+
+ // Convert from the min / max value in the measurement to a
+ // predefined min value between 0 and 100 (see kMinValue and
+ // kMaxValue for actual values)
+
+ scaledAverages() {
+
+ const keys = Object.keys(this.runningAverages);
+
+ const averages = keys.reduce((averagesObject, averageCategory) => {
+
+ const runningAverage = this.runningAverages[averageCategory];
+ averagesObject[averageCategory] = runningAverage.average;
+ return averagesObject;
+ }, {});
+
+ const max = Math.max(...Object.values(averages));
+ const min = Math.min(...Object.values(averages));
+
+ const scaledAverages = Object.keys(averages).reduce((scaledAveragesObject, averageCategory) => {
+
+ const value = averages[averageCategory];
+ scaledAveragesObject[averageCategory] = internals.scale(value, min, max);
+ return scaledAveragesObject;
+ }, {});
+
+ return scaledAverages;
+ }
+ },
+
+ methods: {
+
+ // Reset the size of the canvas on resize
+
+ onResize() {
+
+ this.$el.width = window.innerWidth;
+ this.$el.height = window.innerHeight;
+ }
+ },
+
+ // Initiates the animation loop
+
+ mounted() {
+
+ // Make sure we resize, do an initial sizing
+
+ window.addEventListener('resize', this.onResize.bind(this));
+ this.onResize();
+
+ // Start the whole animation (Sorry it's a mess)
+
+ const canvas = this.$el;
+
+ const context = canvas.getContext('2d');
+ const interval = 1000 / internals.kTargetFPS;
+
+ let lastTimestamp = 0;
+
+ const animationHandler = (timestamp) => {
+
+ window.requestAnimationFrame(animationHandler);
+ const delta = timestamp - lastTimestamp;
+
+ if (delta > interval) {
+
+ const keys = Object.keys(this.scaledAverages);
+ const values = Object.values(this.scaledAverages);
+ const segments = keys.length;
+ const period = canvas.width / segments;
+
+ const fillGradient = context.createLinearGradient(0, 0, canvas.width, 0);
+ const gradientBandWidth = 1 / segments;
+
+ // Position the drawing cursor left-center of screen
+
+ context.clearRect(0, 0, canvas.width, canvas.height);
+ context.beginPath();
+ context.moveTo(0, canvas.height);
+ context.lineTo(0, canvas.height / 2);
+
+ // Iterate over the segments
+
+ for (let i = 0; i < segments; ++i) {
+ const segmentStart = i * period;
+
+ const category = keys[i];
+ const magnitude = values[i];
+ const segmentHeight = Math.round(Math.random() * internals.kRandomJitter + magnitude * (canvas.height / 2) / 100);
+
+ // Calculate the gradient using the correct color according to
+ // scale
+
+ const color = internals.kColors[category] || 'black';
+ let currentGradientPosition = i * gradientBandWidth + internals.kGradientFade;
+ fillGradient.addColorStop(currentGradientPosition, color);
+ currentGradientPosition = (i + 1) * gradientBandWidth - internals.kGradientFade;
+ fillGradient.addColorStop(currentGradientPosition, color);
+
+ // This draws the sine wave
+
+ for (let j = 0; j < 180; ++j) {
+ const currentPixel = segmentStart + j * period / 180;
+ const currentAngle = j + 180 * (i % 2);
+ const currentRadians = currentAngle * Math.PI / 180;
+ const currentHeight = segmentHeight * Math.sin(internals.kFrequency * currentRadians);
+
+ context.lineTo(currentPixel, currentHeight + canvas.height / 2);
+ }
+ }
+
+ context.lineTo(canvas.width, canvas.height / 2);
+ context.lineTo(canvas.width, canvas.height);
+ context.fillStyle = fillGradient;
+ context.fill();
+
+ lastTimestamp = timestamp;
+ }
+ };
+
+ window.requestAnimationFrame(animationHandler);
+ }
+});