Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Создание облака тегов на JS (https://javascript.ru/forum/misc/75776-sozdanie-oblaka-tegov-na-js.html)

DmitryBelg 06.11.2018 12:10

Цитата:

Сообщение от рони (Сообщение 497907)
DmitryBelg,
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>JS PF taak: TagCloud</title>
<style>
#tagContainer {
    position:relative;
    background-color:#FFE4E1;
    margin:1em;
    padding:0;
    width: 500px;
    height:400px;
    overflow:hidden;
    border:2px solid silver;
}
span.tag {
    position:absolute;
    top:0;
    left:0;
    text-shadow: 5px 1px 3px #FFFAFA;
     font-weight: 400;
}
</style>
<script>
var arrTags = [
    ["Javascript", 1634, 987],
    ["jQuery", 1111, 34],
    ["PHP", 1024, 1122],
    ["Asp.Net", 977, 1005],
    ["Photoshop", 594, 789],
    ["XML", 40, 666],
    ["Access", 55, 77],
    ["Java", 278, 277],
    ["MySQL", 155, 122]
];
window.onload = function() {
    var tagCont = document.getElementById("tagContainer");

    function sum(el) {
        return el[1] + el[2]
    }
    arrTags.sort(function(a, b) {
     return sum(a) - sum(b)
    });
    for (var i = 0; i < arrTags.length; i++) {
        var eSpan = document.createElement("span");
        eSpan.className = "tag";
        var font = 12 + (i * i);
        var x = Math.floor(Math.random() * 250);
        var y = Math.floor(Math.random() * 250);
        eSpan.style.fontSize = font + "px";
        eSpan.style.left = x + "px";
        eSpan.style.top = y + "px";
        eSpan.style.color = "#" + ("000000" + (Math.random() * 16777215 | 0).toString(16)).slice(-6);
        eSpan.innerHTML = arrTags[i][0];
        tagCont.appendChild(eSpan)
    }
};
</script>
</head>
<body>
<div id="tagContainer"></div>
</body>
</html>

Спасибо, добрый человек)

Malleys 06.11.2018 14:57

Для того, чтобы слова не обрезались и не скучивались где-то в уголке, можно использовать такой приём: пусть центр каждого тега не появляется в 25% (от ширины/высоты) от края.

Цвета генерировать можно, например, на основе оттенка H в модели HSL зависящего от популярности тега. (т. е. яркость и насыщенность остаются равномерными)

Менее популярные теги сложно прочитать, поскольку они очень мелкие. Я думаю, что популярность тега можно выразить не через размер, а через жирность начертания шрифта. Тогда размер, например, может колебаться от 1.5em до 4em (что подходит для чтения). А для подчёркивания популярности использовать жирность шрифта (font weight, обозначается как "wght") от 100 до 900, что даёт более широкий набор возможностей, какие стили применить. (Например, более жирный текст отбрасывает более крупную тень, которую, если подкрасить, можно в данном случае воспринять так, что более популярные теги ярче светятся)

P. S. Для примера я использовал шрифт https://v-fonts.com/fonts/ff-meta-variable, я подключил его через CORS Proxy, чтобы были правильные заголовки, вы же можете скачать его к себе, или какой-либо другой переменный шрифт. Обратите внимание, что это должен быть именно переменный шрифт, только тогда дробные жирности будут работать. Если вам интересна эта тема, можете посмотреть, что вообще возможно при помощи переменных шрифтов https://v-fonts.com/

Я использовал проценты, так что #tagContainer может быть любого размера.

<!DOCTYPE html>
<html>
	<head>
		<title></title>
		<style>
			
@font-face {
	src: url("https://cors-anywhere.herokuapp.com/https://v-fonts.com/assets/fonts/MetaVariableDemo-Set.woff2");
	font-family: "FF Meta Variable";
	font-style: normal;
}

body {
	margin: 0;
}

#tagContainer {
	position: relative;
	width: 100vw;
	height: 100vh;
	font-family: "FF Meta Variable";
	overflow: hidden;
	margin: 0;
	background-color: black;
	text-shadow: 0 0 2em;
}

#tagContainer > * {
	position: absolute;
	mix-blend-mode: difference;
	mix-blend-mode: screen;
	transform: translate(-50%, -50%);
}

		</style>
		<script>
			
			addEventListener("load", event => {
				var cloudElement = document.getElementById("tagContainer");
				var tags = [
					["Javascript", 1634, 987],
					["jQuery", 1111, 34],
					["PHP", 1024, 1122],
					["Asp.Net", 977, 1005],
					["Photoshop", 594, 789],
					["XML", 40, 666],
					["Access", 55, 77],
					["Java", 278, 277],
					["MySQL", 155, 122]
				];

				const { min, max } = tags.reduce(({ min, max }, [, ...v]) => {
					const total = v.reduce((a, b) => a + b, 0);
					return {
						min: Math.min(min, total),
						max: Math.max(max, total)
					}
				}, { min: Infinity, max: -Infinity });

				tags.forEach(([tag, ...v]) => {
					const total = v.reduce((a, b) => a + b, 0);
					const p = (total - min) / (max - min);
					const element = document.createElement("span");
					element.style.cssText = `
						font-variation-settings: "wght" ${100 + 800 * p};
						font-size: ${1.5 + 2.5 * p}em;
						color: hsl(${180 + 960 * p}, 100%, 50%);
						top:  ${25 + 50 * Math.random()}%;
						left: ${25 + 50 * Math.random()}%;
					`;
					element.textContent = tag;
					cloudElement.append(element);
				})
			});

		</script>
	</head>
	<body>
		<div id="tagContainer"></div>
	</body>
</html>

рони 06.11.2018 15:57

Malleys,
как всегда спасибо, но смущает инциализация min max (строка 58) и вычисление p строка 62.
мне видится более точное вычисление этих параметров в посте №7

Malleys 06.11.2018 16:03

Ну да точно, конечно же нужно const p = (total - min) / (max - min), а не const p = total / (max - min) линейная интерполяция. Спасибо, что так внимательно посмотрели!


Часовой пояс GMT +3, время: 06:01.