基于SVG彩色与轮廓图开发Illustrator数字填色脚本需求
解决方案:SVG彩色图转数字填色工作表
核心思路
基于SVG的DOM结构,提取彩色图的填充色数据,通过色彩聚类合并相近色调,建立色彩与编号的映射,再将编号标注到轮廓图对应区域,最后生成带编号调色板的完整填色工作表。
具体实现步骤
1. 色彩分组融合
- 提取彩色SVG中所有非透明、非渐变的填充色,统一转换为RGB格式便于计算
- 使用K-means聚类算法对色彩分组,可根据人工编辑需求设置聚类数量(建议20-30组,平衡细节与工作量)
- 聚类时优先保留画面主色调,自动合并HSL空间差值较小的相近色调,每组取中心色作为调色板基准色
2. 调色板生成与色彩映射
- 将聚类后的中心色按顺序编号,在轮廓图底部生成横向排列的调色板,每个色块下方标注对应数字(可设置色块大小、间距统一样式)
- 建立「原色彩→聚类组编号」的映射表,确保同一聚类组内的所有原色彩对应同一个编号
3. 轮廓图区域数字标注
- 提前确保彩色图与轮廓图的区域ID一一对应(若未对应,可通过路径形状相似度、轮廓矩计算匹配)
- 遍历轮廓图的每个区域,根据对应彩色区域的原色彩查找映射编号,在区域中心位置添加文本标注(字号根据区域大小自动适配,避免溢出)
4. 原有脚本的适配修改
- 补充色彩聚类模块:引入
chroma.js简化色彩计算,替换原脚本中直接匹配色彩的逻辑 - 添加区域匹配逻辑:通过ID直接关联或路径特征计算,实现彩色图与轮廓图区域的对应
- 新增调色板生成函数:自动在SVG底部插入色块和文本元素,完成布局
核心代码示例
// 提取彩色SVG中的填充色(排除渐变、透明) function extractFillColors(svgDoc) { const colorList = []; const fillElements = svgDoc.querySelectorAll('[fill]:not([fill="none"]):not([fill^="url("])'); fillElements.forEach(el => { const hexColor = chroma(el.getAttribute('fill')).hex(); colorList.push(hexColor); }); return [...new Set(colorList)]; // 去重 } // K-means色彩聚类,生成调色板与映射表 function buildColorMap(rawColors, clusterCount) { // 将原始颜色聚类为指定数量的调色板色 const palette = chroma.scale(rawColors).mode('lch').colors(clusterCount); const colorToNumber = new Map(); rawColors.forEach(color => { // 找到当前颜色最接近的调色板色 const closestPaletteColor = palette.reduce((prev, curr) => chroma.distance(color, curr) < chroma.distance(color, prev) ? curr : prev ); // 编号从1开始(符合填色习惯) colorToNumber.set(color, palette.indexOf(closestPaletteColor) + 1); }); return { palette, colorToNumber }; } // 为轮廓图区域添加数字标注 function addNumberLabels(outlineSvg, colorMap, regionIdToColor) { const paths = outlineSvg.querySelectorAll('path'); paths.forEach(path => { const regionId = path.getAttribute('id'); const targetColor = regionIdToColor.get(regionId); if (targetColor && colorMap.has(targetColor)) { const number = colorMap.get(targetColor); const bbox = path.getBBox(); // 创建SVG文本元素 const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', bbox.x + bbox.width / 2); text.setAttribute('y', bbox.y + bbox.height / 2); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.setAttribute('font-size', Math.max(8, Math.min(bbox.width, bbox.height) / 6)); text.textContent = number; path.parentNode.appendChild(text); } }); } // 生成底部调色板 function generatePalette(svgDoc, palette, colorToNumber) { const svgWidth = parseFloat(svgDoc.getAttribute('width')); const paletteHeight = 40; const blockWidth = svgWidth / palette.length; // 调整SVG高度,留出调色板空间 svgDoc.setAttribute('height', parseFloat(svgDoc.getAttribute('height')) + paletteHeight + 10); palette.forEach((color, index) => { const number = index + 1; // 创建色块 const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); rect.setAttribute('x', index * blockWidth); rect.setAttribute('y', parseFloat(svgDoc.getAttribute('height')) - paletteHeight); rect.setAttribute('width', blockWidth); rect.setAttribute('height', paletteHeight - 10); rect.setAttribute('fill', color); rect.setAttribute('stroke', '#000'); // 创建数字标签 const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', index * blockWidth + blockWidth / 2); text.setAttribute('y', parseFloat(svgDoc.getAttribute('height')) - 5); text.setAttribute('text-anchor', 'middle'); text.setAttribute('font-size', 12); text.textContent = number; svgDoc.appendChild(rect); svgDoc.appendChild(text); }); }
内容的提问来源于stack exchange,提问作者Ivan Octavian
相关产品推荐
相关产品推荐

