Перейти к основному содержимому
clawam tools
Инструменты Документы Daoark EN Главная Главная / Текстовые инструменты › Главная manifest.json &"']/g, function(c) { return {'<':'<','>':'>','&':'&','"':'"',"'":'''}[c]; }); } // 简易分词:中文 2-gram + 英文按空格 function tokenize(text) { var tokens = []; // 英文单词 var enMatches = text.match(/[a-zA-Z]+/g); if (enMatches) for (var i = 0; i < enMatches.length; i++) tokens.push(enMatches[i].toLowerCase()); // 中文 2-gram var zh = text.replace(/[a-zA-Z0-9\s\p{P}]/gu, ''); for (var j = 0; j < zh.length - 1; j++) { var t = zh.substring(j, j + 2); if (/^[\u4e00-\u9fa5]{2}$/.test(t)) tokens.push(t); } return tokens; } function countWords(tokens, stops) { var stopsSet = new Set(stops); var c = {}; for (var i = 0; i < tokens.length; i++) { var t = tokens[i]; if (t.length < 2) continue; if (stopsSet.has(t)) continue; c[t] = (c[t] || 0) + 1; } return c; } function topN(c, n) { var arr = []; for (var k in c) arr.push({ word: k, count: c[k] }); arr.sort(function(a, b) { return b.count - a.count; }); return arr.slice(0, n); } // 阿基米德螺旋布局 function placeSpiral(W, H, n, fontSize, angle, shape) { var cx = W / 2, cy = H / 2; var positions = []; for (var s = 0; s < n; s++) { var t = s * 0.18; var r = t; var rad = t * 2.5 + (angle || 0); var x = cx + r * Math.cos(rad) * 8; var y = cy + r * Math.sin(rad) * 8; if (shape === 'circle') { var d = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)); if (d > (Math.min(W, H) / 2 - fontSize)) { // 拉回 x = cx + (x - cx) * (Math.min(W, H) / 2 - fontSize) / d; y = cy + (y - cy) * (Math.min(W, H) / 2 - fontSize) / d; } } // 碰撞检测(简化:与已放置距离 < fontSize 则跳过) var collision = false; for (var p = 0; p < positions.length; p++) { var pp = positions[p]; if (Math.abs(pp.x - x) < (pp.w + fontSize) / 2 && Math.abs(pp.y - y) < fontSize * 0.7) { collision = true; break; } } if (collision) continue; var w = fontSize * (fontSize > 10 ? 1.0 : 0.8); positions.push({ x: x, y: y, w: w }); } return positions; } var COLORS = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#06b6d4', '#6366f1']; var lastSvg = ''; function doGen() { try { var text = getVal('text-a'); if (!text) { showError('请输入文本'); return; } var stopInput = getVal('stopwords'); var stops = stopInput.split(/[,,\s]/).map(function(s) { return s.trim(); }).filter(function(s) { return s.length > 0; }); var topn = parseInt($('topn').value, 10) || 40; var shape = $('shape').value; var tokens = tokenize(text); var c = countWords(tokens, stops); var arr = topN(c, topn); if (arr.length === 0) { showError('无有效词(可能全部被停用词过滤)'); return; } var W = 800, H = 500; var svg = ''; var maxC = arr[0].count; var minC = arr[arr.length - 1].count; for (var i = 0; i < arr.length; i++) { var item = arr[i]; var ratio = (item.count - minC + 1) / (maxC - minC + 1); var fontSize = 12 + ratio * 36; var color = COLORS[i % COLORS.length]; var positions = placeSpiral(W, H, 1, fontSize, item.word.length * 0.3, shape); if (positions.length === 0) continue; var pos = positions[0]; svg += '' + escapeXml(item.word) + ''; } svg += ''; lastSvg = svg; showResult(svg + '\n(本 SVG 已生成,含 ' + arr.length + ' 个词)', arr.length + ' 词 · top ' + topn); } catch (e) { showError('生成失败: ' + (e.message || e)); } } $('btn-main').addEventListener('click', function(e) { e.preventDefault(); doGen(); }); $('btn-copy').addEventListener('click', function() { if (lastSvg && navigator.clipboard) navigator.clipboard.writeText(lastSvg); }); $('btn-download').addEventListener('click', function() { if (!lastSvg) return; var blob = new Blob([lastSvg], { type: 'image/svg+xml;charset=utf-8' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'wordcloud.svg'; a.click(); URL.revokeObjectURL(url); }); $('btn-clear').addEventListener('click', function() { var inputs = document.querySelectorAll('input,textarea,select'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].tagName === 'SELECT') inputs[i].selectedIndex = 0; else inputs[i].value = inputs[i].defaultValue; } lastSvg = ''; var ra = $('result-area'); ra.hidden = true; ra.style.display = 'none'; var em = $('error-msg'); em.hidden = true; em.style.display = 'none'; $('btn-copy').style.display = 'none'; $('btn-download').style.display = 'none'; }); })();