Skip to content

Commit d4d4975

Browse files
committed
Add zoom function
1 parent 2368ddf commit d4d4975

File tree

5 files changed

+191
-113
lines changed

5 files changed

+191
-113
lines changed

dot.png

105 Bytes
Loading

hodl.css

+33-23
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
body {
22
background-color: black;
3-
margin: 0px;
3+
margin: 0;
44
font-family: 'Droid Sans Mono', monospace;
55
font-size: 15px;
66
}
77

8+
#background {
9+
position: fixed;
10+
top: 8px;
11+
left: 8px;
12+
}
13+
814
#wrapper {
9-
height: 100%;
10-
width: 100%;
11-
margin: auto;
1215
position: fixed;
1316
top: 0;
1417
left: 0;
1518
bottom: 0;
1619
right: 0;
17-
z-index: 999;
1820
overflow: scroll;
1921
-webkit-overflow-scrolling: touch;
2022
}
@@ -28,17 +30,32 @@ body {
2830
#hodl {
2931
position: absolute;
3032
display: none;
33+
image-rendering: pixelated;
34+
transform: scale(1);
35+
transform-origin: top left;
3136
}
3237

33-
.year {
38+
.label {
3439
position: absolute;
3540
height: 20px;
3641
}
3742

43+
.dot {
44+
position: absolute;
45+
height: 4px;
46+
width: 4px;
47+
image-rendering: pixelated;
48+
content: url("dot.png");
49+
}
50+
51+
.line {
52+
content: url("line.png");
53+
}
54+
3855
#index {
3956
position: fixed;
4057
top: 27px;
41-
right: 151px;
58+
right: 147px;
4259
pointer-events: none;
4360
}
4461

@@ -49,9 +66,9 @@ body {
4966
}
5067

5168
#legend {
52-
position: fixed;
53-
top: 16px;
54-
right: 36px;
69+
position: absolute;
70+
top: -10px;
71+
right: -117px;
5572
pointer-events: none;
5673
}
5774

@@ -80,7 +97,7 @@ body {
8097
#help {
8198
width: 300px;
8299
position: fixed;
83-
top: 76px;
100+
top: 91px;
84101
left: 50%;
85102
margin-left: -150px;
86103
text-align: center;
@@ -89,25 +106,18 @@ body {
89106

90107
@media screen and (max-width: 950px) {
91108
#tip {
92-
top: 76px;
109+
top: 91px;
93110
}
94111
#help {
95-
top: 176px;
112+
top: 191px;
96113
}
97114
}
98115

99-
@media screen and (max-width: 650px) {
116+
@media screen and (max-width: 630px) {
100117
#index {
101118
top: initial;
102-
right: 76px;
103-
bottom: 20px;
104-
transform: scale(0.5);
105-
transform-origin : 100% 100%;
106-
}
107-
#legend {
108-
top: initial;
109-
right: 16px;
110-
bottom: 16px;
119+
right: 85px;
120+
bottom: 30px;
111121
transform: scale(0.5);
112122
transform-origin : 100% 100%;
113123
}

hodl.js

+122-60
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ const COLORMAPS = [
1212

1313
var prices;
1414
var since;
15+
const SCALES = [25, 33, 50, 67, 75, 80, 90, 100, 110, 125, 150, 175, 200, 250, 300, 400, 500];
16+
var scaleIndex = 7;
17+
var scaleSpanClearTimeout;
18+
var scrollBarWidth;
1519

1620
function onLoad() {
1721
var xobj = new XMLHttpRequest();
@@ -33,112 +37,113 @@ function init() {
3337
if (localStorage.getItem("palette") === null) {
3438
localStorage.palette = 7;
3539
}
40+
var wrapperDiv = document.getElementById('wrapper');
41+
scrollBarWidth = wrapperDiv.offsetWidth - wrapperDiv.clientWidth;
42+
43+
var backgroundDiv = document.getElementById('background');
44+
backgroundDiv.style.height = 'calc(100vh - ' + (scrollBarWidth + 16) + 'px)';
45+
backgroundDiv.style.width = 'calc(100vw - ' + (scrollBarWidth + 16) + 'px)';
46+
47+
createLabels();
48+
setScale();
3649
drawIndex();
3750
drawHodl();
38-
createLabels();
3951
}
4052

4153
function drawIndex() {
42-
var gradientCanvas = document.getElementById('gradient');
43-
var gradientContext = gradientCanvas.getContext('2d');
54+
var colorMapCanvas = document.getElementById('colormap');
55+
var colorMapContext = colorMapCanvas.getContext('2d');
4456

45-
gradientContext.fillStyle = createIndexGradient(gradientContext);
46-
gradientContext.fillRect(0, 0, 25, 401);
57+
colorMapContext.fillStyle = createColorMap(colorMapContext);
58+
colorMapContext.fillRect(0, 0, 25, 401);
4759

48-
gradientCanvas.style.display = 'block';
60+
colorMapCanvas.style.display = 'block';
4961
}
5062

51-
function createIndexGradient(indexContext) {
52-
var indexGradient = indexContext.createLinearGradient(0, 0, 0, 400);
53-
var stop = 0;
54-
for (var color of COLORMAPS[localStorage.palette]) {
55-
indexGradient.addColorStop(stop, color);
56-
stop += 0.1;
63+
function createColorMap(colorMapContext) {
64+
var colorMapGradient = colorMapContext.createLinearGradient(0, 0, 0, 400);
65+
var step = 0;
66+
var colorMap = COLORMAPS[localStorage.palette];
67+
for (var color of colorMap) {
68+
colorMapGradient.addColorStop(step / (colorMap.length - 1), color);
69+
step++;
5770
}
58-
return indexGradient;
71+
return colorMapGradient;
5972
}
6073

6174
function drawHodl() {
62-
var size = prices.length;
75+
var size = prices.length - 1;
6376

6477
var borderDiv = document.getElementById('border');
65-
borderDiv.style.width = (size + 7) + 'px';
78+
borderDiv.style.width = (size + 8) + 'px';
6679
borderDiv.style.height = (size + 8) + 'px';
6780

81+
var backgroundColor = COLORMAPS[localStorage.palette][5];
82+
6883
var hodlCanvas = document.getElementById('hodl');
69-
hodlCanvas.width = size - 1;
84+
hodlCanvas.style.backgroundColor = backgroundColor;
85+
hodlCanvas.style.display = 'block';
86+
hodlCanvas.width = size;
7087
hodlCanvas.height = size;
7188

7289
var hodlContext = hodlCanvas.getContext('2d');
7390
drawPixels(hodlContext);
74-
drawDots(hodlContext);
7591

76-
hodlCanvas.style.backgroundColor = COLORMAPS[localStorage.palette][5];
77-
hodlCanvas.style.display = 'block';
92+
var backgroundDiv = document.getElementById('background');
93+
backgroundDiv.style.backgroundColor = backgroundColor;
7894

7995
var aboutDiv = document.getElementById('about');
8096
aboutDiv.style.display = 'block';
8197
}
8298

8399
function drawPixels(hodlContext) {
84-
var size = prices.length;
85-
var colors = getColorLookup();
100+
var size = prices.length - 1;
101+
var colorMap = getColorMap();
86102

87-
var imageData = hodlContext.createImageData(size - 1, size);
103+
var imageData = hodlContext.createImageData(size, size);
88104
var buffer = new ArrayBuffer(imageData.data.length);
89-
var pixelMap = new Uint32Array(buffer);
90-
var y = size - 1;
105+
var pixels = new Uint32Array(buffer);
106+
var y = 0;
91107
for (var selldate = 1; selldate <= size; selldate++) {
92108
for (var buydate = 0; buydate < selldate; buydate++) {
93109
var profit = getProfit(buydate, selldate);
94-
pixelMap[y + buydate] = colors[getColorIndex(profit)];
110+
pixels[y + buydate] = colorMap[getColorIndex(profit)];
95111
}
96-
y += size - 1;
112+
y += size;
97113
}
98114

99115
imageData.data.set(new Uint8ClampedArray(buffer));
100116
hodlContext.putImageData(imageData, 0, 0);
101117
}
102118

103-
function drawDots(hodlContext) {
104-
var size = prices.length;
105-
for (var i = 0; i < size; i++) {
119+
function createLabels() {
120+
var labelsDiv = document.getElementById('labels');
121+
for (var i = 0; i < prices.length; i++) {
106122
var date = getIndexDate(i);
107123
if (date.getDate() == 1) {
108124
if (date.getMonth() == 0) {
109-
hodlContext.moveTo(i + 0.5, i + 0.5);
110-
hodlContext.lineTo(i + 3.5, i - 2.5);
111-
hodlContext.stroke();
112-
} else {
113-
hodlContext.moveTo(i + 0.5, i + 0.5);
114-
hodlContext.lineTo(i + 1.5, i - 0.5);
115-
hodlContext.stroke();
125+
var labelDiv = document.createElement('div');
126+
labelDiv.id = 'y' + date.getFullYear();
127+
labelDiv.classList.add('label');
128+
labelDiv.innerHTML = date.getFullYear();
129+
130+
labelsDiv.appendChild(labelDiv);
116131
}
132+
var labelImg = document.createElement('img');
133+
labelImg.id = 'y' + date.getFullYear() + 'm' + date.getMonth();
134+
labelImg.classList.add('dot');
135+
if (date.getMonth() == 0) {
136+
labelImg.classList.add('line');
137+
}
138+
labelsDiv.appendChild(labelImg);
117139
}
118140
}
119141
}
120142

121-
function createLabels() {
122-
var yearsDiv = document.getElementById('years');
123-
for (var i = 0; i < prices.length; i++) {
124-
var date = getIndexDate(i);
125-
if (date.getDate() == 1 && date.getMonth() == 0) {
126-
127-
var div = document.createElement('div');
128-
div.classList.add('year');
129-
div.style.top = (i - 15) + 'px';
130-
div.style.left = (i + 15) + 'px';
131-
div.innerHTML = date.getFullYear();
132-
133-
yearsDiv.appendChild(div);
134-
}
135-
}
136-
}
137-
138-
function getColorLookup() {
139-
var gradientCanvas = document.getElementById('gradient');
140-
var gradientContext = gradientCanvas.getContext('2d');
141-
imageData = gradientContext.getImageData(0, 0, 1, 400);
143+
function getColorMap() {
144+
var colorMapCanvas = document.getElementById('colormap');
145+
var colorMapContext = colorMapCanvas.getContext('2d');
146+
imageData = colorMapContext.getImageData(0, 0, 1, 400);
142147
return new Uint32Array(imageData.data.buffer);
143148
}
144149

@@ -157,15 +162,15 @@ function onMouseMove(event) {
157162
var hodlCanvas = document.getElementById('hodl');
158163
var tipDiv = document.getElementById('tip');
159164
var markerCanvas = document.getElementById('marker');
160-
var size = prices.length;
165+
var size = prices.length - 1;
161166
if (event.offsetX >= 0 && event.offsetX < size &&
162167
event.offsetY >= 0 && event.offsetY < size &&
163-
event.offsetX < event.offsetY) {
168+
event.offsetX <= event.offsetY) {
164169

165170
hodlCanvas.style.cursor = 'crosshair';
166171

167172
var buy = event.offsetX;
168-
var sell = event.offsetY;
173+
var sell = event.offsetY + 1;
169174
var buyDate = formatEpoch(buy);
170175
var buyPrice = formatPrice(buy);
171176
var sellDate = formatEpoch(sell);
@@ -271,3 +276,60 @@ function onChangePaletteClick(event) {
271276
drawHodl();
272277
event.preventDefault();
273278
}
279+
280+
function onZoomInClick(event) {
281+
if (scaleIndex < SCALES.length - 1) {
282+
scaleIndex++;
283+
setScale();
284+
}
285+
setScaleSpan();
286+
event.preventDefault();
287+
}
288+
289+
function onZoomOutClick(event) {
290+
if (scaleIndex > 0) {
291+
scaleIndex--;
292+
setScale();
293+
}
294+
setScaleSpan();
295+
event.preventDefault();
296+
}
297+
298+
function setScale() {
299+
var scale = SCALES[scaleIndex];
300+
var scaleFraction = scale / 100;
301+
302+
var hodlCanvas = document.getElementById('hodl');
303+
hodlCanvas.style.transform = `scale(${scaleFraction})`;
304+
305+
var size = prices.length * scaleFraction;
306+
307+
var borderDiv = document.getElementById('border');
308+
borderDiv.style.width = (size + 7) + 'px';
309+
borderDiv.style.height = (size + 8) + 'px';
310+
311+
for (var i = 0; i < prices.length; i++) {
312+
var date = getIndexDate(i);
313+
if (date.getDate() == 1) {
314+
if (date.getMonth() == 0) {
315+
var div = document.getElementById('y' + date.getFullYear());
316+
div.style.top = ((i * scaleFraction) - 14) + 'px';
317+
div.style.left = ((i * scaleFraction) + 13) + 'px';
318+
}
319+
var img = document.getElementById('y' + date.getFullYear() + 'm' + date.getMonth());
320+
img.style.top = ((i * scaleFraction) + 4) + 'px';
321+
img.style.left = ((i * scaleFraction) + 8) + 'px';
322+
}
323+
}
324+
}
325+
326+
function setScaleSpan() {
327+
var scale = SCALES[scaleIndex];
328+
var scaleSpan = document.getElementById('scale');
329+
var scalePadSpan = document.getElementById('scale-pad');
330+
scaleSpan.innerHTML = scale + '%';
331+
scalePadSpan.innerHTML = '&nbsp;&nbsp;&nbsp;' + (scaleIndex > 6 ? '&nbsp;' : '');
332+
333+
clearTimeout(scaleSpanClearTimeout);
334+
scaleSpanClearTimeout = setTimeout(function(){ scaleSpan.innerHTML = ''; scalePadSpan.innerHTML = '';}, 2000);
335+
}

0 commit comments

Comments
 (0)