词云

我发现词云特别好用,能迅速构建起一套语言体系。但好像没有很方便的词云生成网站!

效果:词云生成器

需求

  • 可以一行一个输入词句,然后会生成对应的词云
  • 网站可以自适应页面大小,同时支持上传excel文件,和下载生成的词。
  • 词云的结果都是适合中文阅读习惯的,而不是东倒西歪
  • 字体颜色不要固定,可以多整几种适合阅读的颜色随机加上去
  • 现图片有很大区域是空白的,浪费空间,我要求词云是填满整个页面的

结果

太难整了,不断的调整测试调整测试,感觉还是差很多意思,没有达到我想要的效果。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>词云生成器</title>
    <style>
        #wordcloud {
            width: 100%;
            height: 100vh; /* 使词云容器填满整个视口高度 */
            overflow: hidden;
        }
    </style>
</head>
<body>
    <h1>词云生成器</h1>
    <textarea id="inputWords" rows="10" cols="50" placeholder="请输入词句,每行一个"></textarea>
    <br>
    <input type="file" id="fileInput" accept=".xlsx,.xls">
    <br>
    <button onclick="generateWordCloud()">生成词云</button>
    <div id="wordcloud"></div>
    <a id="downloadLink" style="display: none;">下载词云</a>
 
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/d3-cloud@1/build/d3.layout.cloud.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.full.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.2/html2canvas.min.js"></script>
    <script>
        let currentWords = [];
 
        function generateWordCloud() {
            const inputWords = document.getElementById('inputWords').value;
            const wordsArray = inputWords.split('\n').filter(word => word.trim() !== '');
 
            const fileInput = document.getElementById('fileInput');
            if (fileInput.files.length > 0) {
                const file = fileInput.files[0];
                const reader = new FileReader();
                reader.onload = function(e) {
                    const data = new Uint8Array(e.target.result);
                    const workbook = XLSX.read(data, {type: 'array'});
                    const firstSheetName = workbook.SheetNames[0];
                    const worksheet = workbook.Sheets[firstSheetName];
                    const excelData = XLSX.utils.sheet_to_json(worksheet, {header: 1});
                    const excelWords = excelData.flat().filter(word => word.trim() !== '');
                    currentWords = [...wordsArray, ...excelWords];
                    generateCloud(currentWords);
                };
                reader.readAsArrayBuffer(file);
            } else {
                currentWords = wordsArray;
                generateCloud(currentWords);
            }
        }
 
        function generateCloud(wordsArray) {
            const colors = ["#333", "#666", "#999", "#0066CC", "#009933", "#CC6600"]; // 定义颜色数组
 
            const maxFontSize = 100; // 最大字体大小
            const minFontSize = 20; // 最小字体大小,避免字体过小
            const fontSizeScale = d3.scaleLinear()
                .domain([0, wordsArray.length])
                .range([maxFontSize, minFontSize]);
 
            const layout = d3.layout.cloud()
                .size([window.innerWidth, window.innerHeight])
                .words(wordsArray.map(word => ({text: word, size: fontSizeScale(Math.random() * wordsArray.length)})))
                .padding(5)
                .rotate(() => 0) // 确保文字不旋转
                .font("SimHei") // 使用适合中文的字体
                .fontSize(d => d.size)
                .on("end", draw);
 
            layout.start();
 
            function draw(words) {
                d3.select("#wordcloud").selectAll("*").remove();
                const svg = d3.select("#wordcloud")
                    .append("svg")
                    .attr("width", layout.size()[0])
                    .attr("height", layout.size()[1])
                    .append("g")
                    .attr("transform", "translate(" + layout.size()[0] / 2 + "," + layout.size()[1] / 2 + ")")
                    .selectAll("text")
                    .data(words)
                    .enter().append("text")
                    .style("font-size", d => d.size + "px")
                    .style("font-family", "SimHei") // 使用适合中文的字体
                    .style("fill", () => colors[Math.floor(Math.random() * colors.length)]) // 随机选择颜色
                    .attr("text-anchor", "middle")
                    .attr("transform", d => "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")")
                    .text(d => d.text);
 
                // 确保词云容器大小与生成的SVG大小一致
                document.getElementById('wordcloud').style.width = layout.size()[0] + 'px';
                document.getElementById('wordcloud').style.height = layout.size()[1] + 'px';
 
                html2canvas(document.getElementById('wordcloud'), { scale: 2 }).then(canvas => {
                    const url = canvas.toDataURL("image/png");
                    document.getElementById('downloadLink').href = url;
                    document.getElementById('downloadLink').download = "wordcloud.png";
                    document.getElementById('downloadLink').style.display = 'block';
                });
            }
        }
 
        window.addEventListener('resize', () => generateCloud(currentWords));
    </script>
</body>
</html>

解释代码

  1. 自适应页面大小

    • 使用window.innerWidthwindow.innerHeight动态设置词云的大小。
    • 添加window.addEventListener('resize', () => generateCloud(currentWords))监听窗口大小变化,重新生成词云。
  2. 支持上传Excel文件

    • 使用FileReader读取上传的Excel文件。
    • 使用xlsx.js库解析Excel文件内容,并将其转换为词云所需的格式。
  3. 下载生成的词云

    • 使用html2canvas库将生成的SVG词云转换为Canvas元素。
    • 设置html2canvasscale参数为2,以提高图片的分辨率。
    • 将Canvas元素转换为PNG图片,并生成下载链接。
    • 设置下载链接的download属性,以便在点击时自动下载图片。
  4. 适合中文阅读习惯

    • 设置rotate(() => 0)确保文字不旋转。
    • 使用适合中文的字体SimHei(黑体)。
  5. 多样化的字体颜色

    • 定义一个颜色数组colors,包含多种适合阅读的颜色。
    • 在生成词云时,随机选择颜色数组中的颜色。
  6. 解除数量限制

    • 通过调整词云生成的逻辑,确保能够处理任意数量的词汇。
  7. 动态调整字体大小

    • 使用d3.scaleLinear动态调整字体大小,以确保更多的词汇能够显示出来。
    • 设置最小字体大小为20,避免字体过小而难以辨认。
  8. 避免字体互相遮挡

    • 通过调整词云布局算法,确保字体不会互相遮挡。
  9. 确保词云容器大小与生成的SVG大小一致

    • 在生成词云后,调整词云容器的宽度和高度,使其与生成的SVG大小一致。
  10. 填满整个页面

    • 将词云容器的高度设置为100vh,使其填满整个视口高度。