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