修正D3.js/SVG可平铺图案:实现路径均匀一致填充
问题描述
现有一组不同尺寸的随机路径,需要为它们设置统一、均匀的填充图案。
当前填充效果:
预期填充效果:
尝试调整preserveAspectRatio和viewBox属性均未成功,且要求仅使用pattern实现需求(不使用clip-path克隆图案的方案)。
现有代码
JavaScript
const data = { poly0: [{"x":107,"y":392.2},{"x":186,"y":361},{"x":257,"y":355.3},{"x":257,"y":333.9},{"x":107,"y":372.4}], poly1: [{"x":107,"y":406.5},{"x":257,"y":368},{"x":257,"y":355.3},{"x":186,"y":361},{"x":107,"y":392.2}], poly2: [{"x":107,"y":463.8},{"x":107,"y":521},{"x":257,"y":467},{"x":360,"y":527},{"x":488,"y":486},{"x":621,"y":540},{"x":677,"y":486},{"x":677,"y":453},{"x":677,"y":420.1},{"x":621,"y":433.9},{"x":488,"y":370.9},{"x":360,"y":435.5},{"x":257,"y":368},{"x":107,"y":406.5}], poly3: [{"x":257,"y":368},{"x":360,"y":435.5},{"x":488,"y":370.9},{"x":488,"y":338.5},{"x":360,"y":363.5},{"x":257,"y":333.9}], poly4: [{"x":488,"y":354.5},{"x":621,"y":386.3},{"x":677,"y":388.5},{"x":677,"y":362.5},{"x":621,"y":338.5},{"x":488,"y":338.5}], poly5: [{"x":488,"y":370.9},{"x":621,"y":433.9},{"x":677,"y":420.1},{"x":677,"y":388.5},{"x":621,"y":386.3},{"x":488,"y":354.5}] }; let svg, g2, EPS = 1E-5, curve = d3.line().curve(d3.curveCatmullRom).x(d_ => { return d_.x; }).y(d_ => { return d_.y; }); svg = d3.select("#scene"); let colors = ["#005f73", "#0a9396", "#94d2bd", "#e9d8a6", "#ee9b00", "#ca6702", "#bb3e03", "#ae2012"]; let paths = svg.selectAll(".path") .data(Object.keys(data)) .enter() .append("path") .attr("id", (d_) => { return `path_${d_}`; }) .attr("class", "path") .attr("d", (d_) => { return generatePathFromPoints(data[d_], true); }) .attr("stroke", "#000000") .attr("fill", "url(#star)"); function generatePathFromPoints(points_, closed_){ let d = `M${points_[0].x} ${points_[0].y}`; for(let i = 1; i < points_.length; i++) { d += `L${points_[i].x} ${points_[i].y}`; } if(closed_) { d += "Z"; } return d; }
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg id="scene" viewBox="0 0 800 800" preserveAspectRatio="xMinYMin meet"> <defs> <pattern id="star" viewBox="0 0 10 10" width="10%" height="10%" preserveAspectRatio="none"> <polygon points="0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2" fill="#000000"/> </pattern> </defs> </svg>
解决方案
问题根源在于SVG pattern的默认坐标系统:默认的patternUnits="objectBoundingBox"会让图案以每个路径的边界框为基准缩放重复,导致不同路径内的图案大小、排列不统一。
只需修改pattern的两个关键属性,即可实现全局统一的填充效果:
- 将
patternUnits设置为userSpaceOnUse,让图案基于SVG全局用户空间坐标系统重复,而非单个路径的边界框; - 把width和height从百分比改为固定像素值(比如20),确保图案的重复间隔固定,不随路径尺寸变化。
修改后的pattern代码:
<pattern id="star" viewBox="0 0 10 10" width="20" height="20" patternUnits="userSpaceOnUse"> <polygon points="0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2" fill="#000000"/> </pattern>
完整修改后的HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg id="scene" viewBox="0 0 800 800" preserveAspectRatio="xMinYMin meet"> <defs> <pattern id="star" viewBox="0 0 10 10" width="20" height="20" patternUnits="userSpaceOnUse"> <polygon points="0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2" fill="#000000"/> </pattern> </defs> </svg>
这样所有路径的填充图案就会保持统一的大小和均匀的排列,完全符合预期效果。
内容的提问来源于stack exchange,提问作者toowren
相关产品推荐
相关产品推荐

