如何在D3.js中循环点位嵌套插入bseat SVG并设置宽高与坐标?
需求与问题描述
自定义座椅SVG字符串
var seating_svg = '<svg class="bseat" width="20px" height="20px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve"><g><path fill="#000000" d="M129.3,235.5c-26.9,0-53.7-0.2-80.6,0.1c-15.7,0.1-23.5-6-25.3-22.7c-2.2-19.7-7.3-39.2-11.8-58.7c-4.2-18.3-1.2-30.6,20.3-35c5.8-1.2,12.3-11.6,13.6-18.6c2.4-13.4,0.2-27.5,1.7-41.1C50,33.9,63.6,21.2,89.3,20.6c26.8-0.7,53.7-0.7,80.6,0.1c25,0.7,40.6,16.6,42.3,41.7c0.6,8.9,0.3,17.9,0.1,26.9c-0.1,16-2.6,32.4,22.1,33.6c4.5,0.2,12.2,15.1,11.5,22.6c-2.3,25.3-6.4,50.6-13,75.1c-1.8,6.6-14.8,13.9-23.1,14.4C183.1,236.7,156.2,235.6,129.3,235.5L129.3,235.5z M129.3,173.7L129.3,173.7c16.6,0,33-0.2,49.5,0.1c13.3,0.2,20.5-4.7,20.3-19.3c-0.5-30.3,0.1-60.7-0.4-90.9c-0.3-18.9-10.6-29.7-29.5-30.1c-26.8-0.6-53.7-0.5-80.5,0c-19.4,0.3-29.1,12.5-29.7,30.4c-1.1,30.3-0.4,60.6-0.5,90.9c-0.1,12.9,6.6,19,19.2,19C94.7,173.7,112,173.6,129.3,173.7L129.3,173.7z M129.2,224.1L129.2,224.1c23.3,0,46.7,0,70,0c12.7,0,23.4-2.2,23.5-18.7c0.1-16.2-9.6-19.4-22.8-19.4c-47.4,0.1-94.7,0.1-142.1-0.1c-13.1-0.1-21,4.5-21.2,18.8c-0.2,14.3,7.2,19.5,20.5,19.3c24-0.2,48.1-0.1,72-0.1V224.1L129.2,224.1z M50.8,153.1L50.8,153.1c-6.6-7.6-11.2-13-15.9-18.4c-3.8,4.4-11.3,9.7-10.6,13c2.1,8.3,7.9,15.7,12.2,23.5C40.5,165.9,44.7,160.8,50.8,153.1L50.8,153.1z M208,149.6L208,149.6c5.4,9.2,9.1,15.5,12.8,21.8c4-7.6,9.8-15,11.4-23.1c0.8-3.9-6.1-9.4-9.5-14.2C218.3,138.7,213.8,143.4,208,149.6L208,149.6z"/></g></svg>';
核心需求
在svgPolygon容器内,按照ppoints数组指定的坐标循环插入多个上述bseat SVG,同时为每个bseat设置自定义的seat_width和seat_height属性:
var ppoints = [{x:0,y:0},{x:10,y:10},{x:20,y:20}]
尝试的无效代码
var ppoints = [{x:0,y:0},{x:10,y:10},{x:20,y:20}] svgPloygon.selectAll(null).data(seatPoints).enter() .html(seating_svg).select('.bseat').attr('width',seat_width).attr('height',seat_height);
问题点
D3.js的enter()链式调用不支持直接用.html()插入HTML字符串,仅支持.append()类方法,导致无法直接实现需求。
解决方案
方法一:用foreignObject包裹SVG字符串
通过foreignObject容器插入完整SVG字符串,同时设置位置和尺寸:
var seat_width = 30; // 自定义宽度 var seat_height = 30; // 自定义高度 var ppoints = [{x:0,y:0},{x:10,y:10},{x:20,y:20}]; svgPolygon.selectAll('foreignObject.seat-container') .data(ppoints) .enter() .append('foreignObject') .attr('class', 'seat-container') // 设置座椅位置 .attr('x', d => d.x) .attr('y', d => d.y) // 设置容器尺寸匹配座椅 .attr('width', seat_width) .attr('height', seat_height) // 插入SVG字符串 .html(() => seating_svg) // 修改插入的bseat尺寸 .select('.bseat') .attr('width', seat_width) .attr('height', seat_height);
方法二:解析SVG字符串为DOM元素后插入
先将SVG字符串解析为DOM节点,再通过.append()插入,灵活性更高:
var seat_width = 30; var seat_height = 30; var ppoints = [{x:0,y:0},{x:10,y:10},{x:20,y:20}]; // 解析SVG字符串为DOM元素 function parseSeatSVG() { var parser = new DOMParser(); var doc = parser.parseFromString(seating_svg, 'image/svg+xml'); return doc.querySelector('.bseat'); } svgPolygon.selectAll('svg.bseat') .data(ppoints) .enter() .append(() => parseSeatSVG()) // 插入解析后的SVG元素 .attr('x', d => d.x) .attr('y', d => d.y) .attr('width', seat_width) .attr('height', seat_height);
方法三:直接复用path元素(性能最优)
提取原SVG中的path路径,用g容器定位并缩放,避免嵌套SVG的性能损耗:
var seat_width = 30; var seat_height = 30; var ppoints = [{x:0,y:0},{x:10,y:10},{x:20,y:20}]; // 提取原SVG的path路径数据 var seatPathD = "M129.3,235.5c-26.9,0-53.7-0.2-80.6,0.1c-15.7,0.1-23.5-6-25.3-22.7c-2.2-19.7-7.3-39.2-11.8-58.7c-4.2-18.3-1.2-30.6,20.3-35c5.8-1.2,12.3-11.6,13.6-18.6c2.4-13.4,0.2-27.5,1.7-41.1C50,33.9,63.6,21.2,89.3,20.6c26.8-0.7,53.7-0.7,80.6,0.1c25,0.7,40.6,16.6,42.3,41.7c0.6,8.9,0.3,17.9,0.1,26.9c-0.1,16-2.6,32.4,22.1,33.6c4.5,0.2,12.2,15.1,11.5,22.6c-2.3,25.3-6.4,50.6-13,75.1c-1.8,6.6-14.8,13.9-23.1,14.4C183.1,236.7,156.2,235.6,129.3,235.5L129.3,235.5z M129.3,173.7L129.3,173.7c16.6,0,33-0.2,49.5,0.1c13.3,0.2,20.5-4.7,20.3-19.3c-0.5-30.3,0.1-60.7-0.4-90.9c-0.3-18.9-10.6-29.7-29.5-30.1c-26.8-0.6-53.7-0.5-80.5,0c-19.4,0.3-29.1,12.5-29.7,30.4c-1.1,30.3-0.4,60.6-0.5,90.9c-0.1,12.9,6.6,19,19.2,19C94.7,173.7,112,173.6,129.3,173.7L129.3,173.7z M129.2,224.1L129.2,224.1c23.3,0,46.7,0,70,0c12.7,0,23.4-2.2,23.5-18.7c0.1-16.2-9.6-19.4-22.8-19.4c-47.4,0.1-94.7,0.1-142.1-0.1c-13.1-0.1-21,4.5-21.2,18.8c-0.2,14.3,7.2,19.5,20.5,19.3c24-0.2,48.1-0.1,72-0.1V224.1L129.2,224.1z M50.8,153.1L50.8,153.1c-6.6-7.6-11.2-13-15.9-18.4c-3.8,4.4-11.3,9.7-10.6,13c2.1,8.3,7.9,15.7,12.2,23.5C40.5,165.9,44.7,160.8,50.8,153.1L50.8,153.1z M208,149.6L208,149.6c5.4,9.2,9.1,15.5,12.8,21.8c4-7.6,9.8-15,11.4-23.1c0.8-3.9-6.1-9.4-9.5-14.2C218.3,138.7,213.8,143.4,208,149.6L208,149.6z"; svgPolygon.selectAll('g.bseat-group') .data(ppoints) .enter() .append('g') .attr('class', 'bseat-group') // 定位并缩放座椅(原SVG viewBox为256x256,按比例缩放) .attr('transform', d => `translate(${d.x}, ${d.y}) scale(${seat_width/256}, ${seat_height/256})`) .append('path') .attr('fill', '#000000') .attr('d', seatPathD);
注意事项
- 方法一、二需确保
svgPolygon是有效的SVG容器(即<svg>标签),否则foreignObject无法正常工作。 - 方法三通过比例缩放保证图形不变形,缩放比例为目标尺寸/256(原SVG viewBox的宽高)。
内容的提问来源于stack exchange,提问作者B L Praveen
相关产品推荐
相关产品推荐

