1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
document.addEventListener('DOMContentLoaded', function () {
var containers = document.querySelectorAll('[data-tag-cloud]');
if (!containers.length) return;
Array.prototype.forEach.call(containers, function (container) {
if (container.offsetWidth < 400) return;
var links = Array.prototype.slice.call(
container.querySelectorAll('.tag-cloud-link')
);
if (!links.length) return;
// Sort descending by weight (biggest first = placed near center)
links.sort(function (a, b) {
return parseFloat(b.dataset.weight) - parseFloat(a.dataset.weight);
});
// String hash → deterministic angle seed (0..2π)
function hashAngle(str) {
var h = 0;
for (var i = 0; i < str.length; i++) {
h = (h * 31 + str.charCodeAt(i)) & 0xffffffff;
}
return ((h >>> 0) / 0xffffffff) * 2 * Math.PI;
}
// AABB collision check
function overlaps(a, b) {
return !(
a.right < b.left ||
a.left > b.right ||
a.bottom < b.top ||
a.top > b.bottom
);
}
var placed = [];
var containerWidth = container.offsetWidth;
var cx = containerWidth / 2;
// Measure each tag before repositioning
var sizes = links.map(function (link) {
var rect = link.getBoundingClientRect();
return { w: rect.width, h: rect.height };
});
// Switch container to relative positioning and remove flex layout
container.style.position = 'relative';
container.style.display = 'block';
container.classList.remove('flex', 'flex-wrap');
var padding = -2; // px gap between tags (negative allows ~2px edge overlap)
var aStep = 0.2; // radians per spiral step
var rScale = (containerWidth * 0.013); // spiral tightness
var minTop = Infinity, maxBottom = -Infinity;
links.forEach(function (link, i) {
var w = sizes[i].w;
var h = sizes[i].h;
var seed = hashAngle(link.href);
var theta = seed;
var placed_rect;
// Step along spiral until no collision
for (var attempt = 0; attempt < 3000; attempt++) {
var r = rScale * theta;
var x = cx + r * Math.cos(theta) - w / 2;
var y = r * Math.sin(theta) - h / 2;
var candidate = { left: x, top: y, right: x + w, bottom: y + h };
var collision = false;
for (var j = 0; j < placed.length; j++) {
var p = placed[j];
var padded = {
left: p.left - padding,
top: p.top - padding,
right: p.right + padding,
bottom: p.bottom + padding
};
if (overlaps(candidate, padded)) {
collision = true;
break;
}
}
if (!collision) {
placed_rect = candidate;
break;
}
theta += aStep;
}
if (!placed_rect) {
// Fallback: just append to flow if spiral exhausted
link.style.position = 'static';
return;
}
placed.push(placed_rect);
link.style.position = 'absolute';
link.style.left = Math.round(placed_rect.left) + 'px';
link.style.top = Math.round(placed_rect.top) + 'px';
if (placed_rect.top < minTop) minTop = placed_rect.top;
if (placed_rect.bottom > maxBottom) maxBottom = placed_rect.bottom;
});
// Normalize: shift all tags so topmost is at y=16px
var offset = 16 - minTop;
links.forEach(function (link) {
if (link.style.position === 'absolute') {
link.style.top = (parseInt(link.style.top) + offset) + 'px';
}
});
// Set container height to fit all tags + 2rem bottom padding (32px)
container.style.height = (maxBottom - minTop + 48) + 'px';
});
});
|