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 05.11.2018 16:40

Создание облака тегов на JS
 
Приветствую, друзья!)

Надо, значится, сделать облако тегов содержащихся в многомерном массиве. Само облако нужно поместить в уже имеющемся контейнере. С размещением тегов из массива в контейнере у меня особых проблем не возникло. Однако не могу приложить ума, как заставить меняться размер шрифта тегов в зависимости от того, сколько каждый из тегов имеет голосов (т.е. надо еще сложить между собой числа в каждом из подмассивов), цвет у тегов должен быть тоже разным.

Есть идеи, как можно решить задачу? Заранее спасибо за ответы!


Код:

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>JS PF taak: TagCloud</title>
<style media="screen" type="text/css">
<!--

}
#tagContainer {
        position:relative;
        background-color:#CFF;
        margin:1em;
        padding:0;
        width: 500px;
        height:400px;
        overflow:hidden;
        border:2px solid silver;
}
span.tag {
        position:absolute;
        top:0;
        left:0;
}
-->
</style>
<script type="text/javascript">
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');
    for (var i = 0; i < arrTags.length; i++){
        var eSpan = document.createElement('span');
        eSpan.className = "tag";
       
        var x = Math.floor(Math.random()*250);
        var y = Math.floor(Math.random()*250);
        eSpan.style.left = x +'px';
        eSpan.style.top = y +'px';
        eSpan.innerHTML = arrTags[i][0];
        tagCont.appendChild(eSpan);
    }
}

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


рони 05.11.2018 17:02

DmitryBelg,
в чём проблема сделать цикл и сложить всё что нужно заранее?

DmitryBelg 05.11.2018 17:26

числа я сложил.
код теперь выглядит вот так:

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');
    for (var i = 0; i < arrTags.length; i++){
        arrTags[i][1] = arrTags[i][1]+arrTags[i][2];
        console.log(arrTags[i][1]);
        var eSpan = document.createElement('span');
        eSpan.className = "tag";
        var x = Math.floor(Math.random()*250);
        var y = Math.floor(Math.random()*250);
        eSpan.style.left = x +'px';
        eSpan.style.top = y +'px';
        eSpan.innerHTML = arrTags[i][0];
        tagCont.appendChild(eSpan);
    }
}


Но как теперь заставить шрифт подгоняться под полученные значения, я не знаю.

рони 05.11.2018 17:35

DmitryBelg,
общую сумму голосов нужно точно? может только первое число?

рони 05.11.2018 17:40

DmitryBelg,
в строке 15 отсортируйте массив и потом
eSpan.style.fontSize = (12 + i * 2.3) + 'px';

MC-XOBAHCK 05.11.2018 17:42

А что эти числа означают? За-против? Тогда наверное вычитать нужно а не складывать.

рони 05.11.2018 17:55

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]
    }
    var obj = arrTags.reduce(function(a, b) {
        b = sum(b);
        if (a.min > b) a.min = b;
        if (a.max < b) a.max = b;
        return a
    }, {
        max: sum(arrTags[0]),
        min: sum(arrTags[0])
    });
    for (var i = 0; i < arrTags.length; i++) {
        var eSpan = document.createElement("span");
        eSpan.className = "tag";
        var font = 12 + 52 * (arrTags[i][1] - obj.min) / (obj.max - obj.min);
        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>

DmitryBelg 05.11.2018 17:57

Цитата:

Сообщение от рони (Сообщение 497901)
DmitryBelg,
общую сумму голосов нужно точно? может только первое число?

Да, точно) Это голоса. Я из условия задания сразу и не понял о чем речь!)

DmitryBelg 05.11.2018 17:59

воууу)) блин) я аж подавился, когда увидел результат) Сейчас буду вникать.
Спасибо в очередной раз тебе и все остальным, кто не прошел мимо)

рони 05.11.2018 18:04

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>

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, время: 09:39.