Skip to content

Commit 5ce802e

Browse files
author
Lukas Meier
committed
Initial commit
0 parents  commit 5ce802e

8 files changed

+29018
-0
lines changed

index.html

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<script src="./js/Graph.js"></script>
7+
<script src="./js/main.js"></script>
8+
<title>Document</title>
9+
<style>
10+
body{
11+
background-color: #efefef;
12+
}
13+
#canvas{
14+
border: 1px solid #dedede;
15+
margin: 50px auto;
16+
display: block;
17+
background-color: #fff;
18+
box-shadow: 15px 15px 15px rgba(0,0,0,0.2);
19+
}
20+
</style>
21+
</head>
22+
<body>
23+
<canvas width="1000" height="500" id="canvas"></canvas>
24+
</body>
25+
</html>

js/Graph.js

+241
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3+
return new (P || (P = Promise))(function (resolve, reject) {
4+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7+
step((generator = generator.apply(thisArg, _arguments || [])).next());
8+
});
9+
};
10+
var __generator = (this && this.__generator) || function (thisArg, body) {
11+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13+
function verb(n) { return function (v) { return step([n, v]); }; }
14+
function step(op) {
15+
if (f) throw new TypeError("Generator is already executing.");
16+
while (_) try {
17+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18+
if (y = 0, t) op = [op[0] & 2, t.value];
19+
switch (op[0]) {
20+
case 0: case 1: t = op; break;
21+
case 4: _.label++; return { value: op[1], done: false };
22+
case 5: _.label++; y = op[1]; op = [0]; continue;
23+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
24+
default:
25+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29+
if (t[2]) _.ops.pop();
30+
_.trys.pop(); continue;
31+
}
32+
op = body.call(thisArg, _);
33+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35+
}
36+
};
37+
var Graph = /** @class */ (function () {
38+
function Graph() {
39+
var _this = this;
40+
this.fps = 120;
41+
/**
42+
* Get the JSON data and save
43+
* it in the property
44+
*/
45+
this.loadData = function () { return __awaiter(_this, void 0, void 0, function () {
46+
var response, _a;
47+
return __generator(this, function (_b) {
48+
switch (_b.label) {
49+
case 0: return [4 /*yield*/, fetch("/res/liked_songs.json")];
50+
case 1:
51+
response = _b.sent();
52+
_a = this;
53+
return [4 /*yield*/, response.json()];
54+
case 2:
55+
_a.songs = _b.sent();
56+
this.init();
57+
return [2 /*return*/];
58+
}
59+
});
60+
}); };
61+
this.loadData();
62+
}
63+
/**
64+
* Will initialize the graph. This will
65+
* be called after the data form the
66+
* json has been loaded
67+
*/
68+
Graph.prototype.init = function () {
69+
this.canvas = document.getElementById("canvas");
70+
this.ctx = this.canvas.getContext("2d");
71+
this.canvasSize = {
72+
width: parseInt(this.canvas.getAttribute("width")),
73+
height: parseInt(this.canvas.getAttribute("height"))
74+
};
75+
this.visibleRange = {
76+
start: 0,
77+
end: this.canvasSize.width
78+
};
79+
this.startDrawingInterval();
80+
this.changeViewport();
81+
this.updateFps();
82+
};
83+
/**
84+
* Adds eventlisteners to the graph so the user
85+
* can click on songs to set them to active and
86+
* drag to change the viewport on the x axis
87+
*/
88+
Graph.prototype.changeViewport = function () {
89+
var _this = this;
90+
var dragging = false;
91+
var initialX = 0;
92+
var initialDelta = 0;
93+
var delta = 0;
94+
var preventClick = false;
95+
this.canvas.addEventListener("mousedown", function (e) {
96+
dragging = true;
97+
initialX = e.pageX;
98+
});
99+
document.addEventListener("mouseup", function (e) {
100+
dragging = false;
101+
initialDelta = delta * _this.stepSize;
102+
if (e.target === _this.canvas && !preventClick) {
103+
var clickedSong = Math.floor((e.offsetX + initialDelta) / _this.stepSize);
104+
_this.songs.forEach(function (song) { return (song.active = false); });
105+
_this.songs[clickedSong].active = true;
106+
_this.detailInfos = _this.songs[clickedSong];
107+
}
108+
preventClick = false;
109+
});
110+
document.addEventListener("mousemove", function (e) {
111+
if (!dragging)
112+
return null;
113+
preventClick = true;
114+
delta = Math.floor((initialDelta + initialX - e.pageX) / _this.stepSize);
115+
delta = delta < 0 ? 0 : delta;
116+
var stepsPerViewPort = _this.canvasSize.width / _this.stepSize;
117+
var maxDelta = _this.songs.length - stepsPerViewPort;
118+
delta = delta > maxDelta ? maxDelta : delta;
119+
_this.visibleRange.start = Math.floor(delta);
120+
_this.visibleRange.end = Math.floor(stepsPerViewPort + delta);
121+
});
122+
};
123+
/**
124+
* Will start the Graph
125+
*/
126+
Graph.prototype.startDrawingInterval = function () {
127+
var _this = this;
128+
this.drawingInterval = setInterval(function () {
129+
_this.initTime = performance.now();
130+
_this.draw();
131+
_this.deltaTime = performance.now() - _this.initTime;
132+
}, 1000 / this.fps);
133+
};
134+
/**
135+
* This function will be called on
136+
* every frame update
137+
*/
138+
Graph.prototype.draw = function () {
139+
this.clear();
140+
this.drawDurationGraph();
141+
this.drawFps();
142+
};
143+
/**
144+
* Clear the whole canvas for the next
145+
* frame
146+
*/
147+
Graph.prototype.clear = function () {
148+
this.ctx.clearRect(0, 0, this.canvasSize.width, this.canvasSize.height);
149+
};
150+
/**
151+
* Will loop through all songs and draw a
152+
* bar based of the length of the song
153+
*/
154+
Graph.prototype.drawDurationGraph = function () {
155+
this.stepSize = 5;
156+
var x = 0;
157+
var infoX = 0;
158+
var infoY = 0;
159+
var drawInfo = false;
160+
for (var i = this.visibleRange.start; i < this.visibleRange.end; i++) {
161+
if (i >= this.songs.length)
162+
break;
163+
var song = this.songs[i];
164+
var height = this.timeStringToSeconds(song.duration);
165+
if (song.active) {
166+
this.ctx.fillStyle = "blue";
167+
infoX = x;
168+
infoY = height;
169+
drawInfo = true;
170+
}
171+
else {
172+
this.ctx.fillStyle = "red";
173+
}
174+
this.ctx.fillRect(x, this.canvasSize.height - height, this.stepSize, height);
175+
x += this.stepSize;
176+
}
177+
if (drawInfo) {
178+
this.drawDetailBubble(infoX, this.canvasSize.height - infoY - 100);
179+
}
180+
};
181+
/**
182+
* Will display a white box with a black border
183+
* containing information about the currently
184+
* clicked song
185+
* @param x The x coordinate where the bubble should be displayed
186+
* @param y Thy y coordinate where the bubble should be displayed
187+
*/
188+
Graph.prototype.drawDetailBubble = function (x, y) {
189+
y = y < 1 ? 1 : y;
190+
x += 3;
191+
x = x > this.canvasSize.width - 200 ? this.canvasSize.width - 201 : x;
192+
this.ctx.fillStyle = "#000";
193+
this.ctx.fillRect(x - 1, y - 1, 202, 82);
194+
this.ctx.fillStyle = "#fff";
195+
this.ctx.fillRect(x, y, 200, 80);
196+
this.ctx.fillStyle = "#000";
197+
this.ctx.fillText(this.detailInfos["title"], x + 10, y + 20, 180);
198+
this.ctx.fillText(this.detailInfos["artist"], x + 10, y + 35, 180);
199+
this.ctx.fillText(this.detailInfos["album"], x + 10, y + 50, 180);
200+
this.ctx.fillText(this.detailInfos["duration"], x + 10, y + 65, 180);
201+
};
202+
/**
203+
* Will take a string of a timestamp and
204+
* convert it into a number of seconds
205+
* (01:05 will become 65 for example)
206+
* @param timeString The Time in the format '02:21'
207+
* @returns {number} The time in seconds
208+
*/
209+
Graph.prototype.timeStringToSeconds = function (timeString) {
210+
var total = 0;
211+
var tmp = timeString.split(":");
212+
total += parseInt(tmp[0]) * 60;
213+
total += parseInt(tmp[1]);
214+
return total;
215+
};
216+
/**
217+
* Will draw the lastly calculated fps at
218+
* the top left of the graph
219+
*/
220+
Graph.prototype.drawFps = function () {
221+
this.ctx.fillStyle = "#000";
222+
this.ctx.fillText("FPS: " + this.calculatedFps, 5, 12);
223+
};
224+
/**
225+
* Since the FPS should not be updated every
226+
* frame, this external interval is used to
227+
* update the FPS every 300ms
228+
*/
229+
Graph.prototype.updateFps = function () {
230+
var _this = this;
231+
setInterval(function () {
232+
_this.deltaTime = _this.deltaTime <= 0 ? 0 : _this.deltaTime;
233+
_this.calculatedFps = 60 / ((_this.deltaTime * _this.fps) / 1000);
234+
_this.calculatedFps =
235+
_this.calculatedFps > _this.fps
236+
? _this.fps
237+
: Math.round(_this.calculatedFps);
238+
}, 300);
239+
};
240+
return Graph;
241+
}());

js/main.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
window.onload = function () {
2+
window["graph"] = new Graph();
3+
};

package.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "LumensSlider_2.0",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC"
12+
}

0 commit comments

Comments
 (0)