]> git.r.bdr.sh - rbdr/sorting-hat-renderer/blame_incremental - lib/components/wave_renderer.js
Merge branch 'release/1.0.0'
[rbdr/sorting-hat-renderer] / lib / components / wave_renderer.js
... / ...
CommitLineData
1import Vue from 'vue';
2import DataService from '../services/data';
3
4/* global window */
5
6const internals = {
7
8 // Constants
9
10 kColors: {
11 donatello: 'purple',
12 leonardo: 'blue',
13 michaelangelo: 'orange',
14 raphael: 'red'
15 },
16 kFrequency: 100,
17 kGradientFade: 0.1,
18 kMinValue: 25,
19 kMaxValue: 100,
20 kRandomJitter: 10,
21 kTargetFPS: 60,
22
23 // Utility functions
24
25 scale(value, min, max) {
26
27 return ((value - min) / (max - min)) * (internals.kMaxValue - internals.kMinValue) - internals.kMinValue;
28 }
29};
30
31/**
32 * The wave renderer, draws some waves in a canvas to represent a set of
33 * cateogirzed averages
34 *
35 * @class WaveRenderer
36 */
37export default Vue.component('waveRenderer', {
38 template: '<transition name="fade">' +
39 '<canvas v-show="state === 1" class="wave-renderer"></canvas>' +
40 '</transition>',
41
42 data: DataService.data,
43
44 computed: {
45
46 // Convert from the min / max value in the measurement to a
47 // predefined min value between 0 and 100 (see kMinValue and
48 // kMaxValue for actual values)
49
50 scaledAverages() {
51
52 const keys = Object.keys(this.runningAverages);
53
54 const averages = keys.reduce((averagesObject, averageCategory) => {
55
56 const runningAverage = this.runningAverages[averageCategory];
57 averagesObject[averageCategory] = runningAverage.average;
58 return averagesObject;
59 }, {});
60
61 const max = Math.max(...Object.values(averages));
62 const min = Math.min(...Object.values(averages));
63
64 const scaledAverages = Object.keys(averages).reduce((scaledAveragesObject, averageCategory) => {
65
66 const value = averages[averageCategory];
67 scaledAveragesObject[averageCategory] = internals.scale(value, min, max);
68 return scaledAveragesObject;
69 }, {});
70
71 return scaledAverages;
72 }
73 },
74
75 methods: {
76
77 // Reset the size of the canvas on resize
78
79 onResize() {
80
81 this.$el.width = window.innerWidth;
82 this.$el.height = window.innerHeight;
83 }
84 },
85
86 // Initiates the animation loop
87
88 mounted() {
89
90 // Make sure we resize, do an initial sizing
91
92 window.addEventListener('resize', this.onResize.bind(this));
93 this.onResize();
94
95 // Start the whole animation (Sorry it's a mess)
96
97 const canvas = this.$el;
98
99 const context = canvas.getContext('2d');
100 const interval = 1000 / internals.kTargetFPS;
101
102 let lastTimestamp = 0;
103
104 const animationHandler = (timestamp) => {
105
106 window.requestAnimationFrame(animationHandler);
107 const delta = timestamp - lastTimestamp;
108
109 if (delta > interval) {
110
111 const keys = Object.keys(this.scaledAverages);
112 const values = Object.values(this.scaledAverages);
113 const segments = keys.length;
114 const period = canvas.width / segments;
115
116 const fillGradient = context.createLinearGradient(0, 0, canvas.width, 0);
117 const gradientBandWidth = 1 / segments;
118
119 // Position the drawing cursor left-center of screen
120
121 context.clearRect(0, 0, canvas.width, canvas.height);
122 context.beginPath();
123 context.moveTo(0, canvas.height);
124 context.lineTo(0, canvas.height / 2);
125
126 // Iterate over the segments
127
128 for (let i = 0; i < segments; ++i) {
129 const segmentStart = i * period;
130
131 const category = keys[i];
132 const magnitude = values[i];
133 const segmentHeight = Math.round(Math.random() * internals.kRandomJitter + magnitude * (canvas.height / 2) / 100);
134
135 // Calculate the gradient using the correct color according to
136 // scale
137
138 const color = internals.kColors[category] || 'black';
139 let currentGradientPosition = i * gradientBandWidth + internals.kGradientFade;
140 fillGradient.addColorStop(currentGradientPosition, color);
141 currentGradientPosition = (i + 1) * gradientBandWidth - internals.kGradientFade;
142 fillGradient.addColorStop(currentGradientPosition, color);
143
144 // This draws the sine wave
145
146 for (let j = 0; j < 180; ++j) {
147 const currentPixel = segmentStart + j * period / 180;
148 const currentAngle = j + 180 * (i % 2);
149 const currentRadians = currentAngle * Math.PI / 180;
150 const currentHeight = segmentHeight * Math.sin(internals.kFrequency * currentRadians);
151
152 context.lineTo(currentPixel, currentHeight + canvas.height / 2);
153 }
154 }
155
156 context.lineTo(canvas.width, canvas.height / 2);
157 context.lineTo(canvas.width, canvas.height);
158 context.fillStyle = fillGradient;
159 context.fill();
160
161 lastTimestamp = timestamp;
162 }
163 };
164
165 window.requestAnimationFrame(animationHandler);
166 }
167});