减少代码-重复的代码附加SVG图片

时间:2019-02-12 14:49:03

标签: javascript d3.js svg

我有一个svg图,其中包含几种不同类型的节点,每种类型都有许多不同目的的节点。

产品, 社区, 活动, 项目, 产品组。

我要设置每个节点的背景图像。

每个组的每个单独节点都有自己的唯一ID。 这些节点的类型和ID可用于从端点检索所有类型的图像,但图像位于资产文件夹中的产品组除外。

我正在尝试从1.端点和2.从资产文件夹中调用图像。

我的端点代表不同的类型,并在其中解析ID:

https://tiktok.org/products/${node.id}/standard

https://tiktok.org/communities/${node.id}/standard

我目前执行此操作的方法似乎效率很低,而且我不应该有重复的代码!我“附加”了一个svg图像和一个ID,然后为node.types引用了它们并解析了它们的ID,但这只是减少了代码量(这里以两种类型为例):

   .selectAll('circle.node')
    .data(this.nodes)
    .enter();
  //products
  circle
    .filter(node => node.type === EngagementType.Product)
    .append("pattern")
    .attr("id", d => `insightImage-${d.id}`)
    .attr("patternContentUnits", "objectBoundingBox")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("x", 0)
    .attr("y", 0)
    .append("image")
    .attr("xlink:href", d => `https://tiktok.org/products/${d.id}/standard`) (fake link)
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 1)
    .attr("height", 1)
    .attr('preserveAspectRatio', 'xMidYMid slice');
  //communities
  circle
    .filter(node => node.type === EngagementType.Community)
    .append("pattern")
    .attr("id", d => `insightImageCom-${d.id}`)
    .attr("patternContentUnits", "objectBoundingBox")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("x", 0)
    .attr("y", 0)
    .append("image")
    .attr("xlink:href", d => `https://tiktok.org/communities/${d.id}/standard`)
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 1)
    .attr("height", 1)
    .attr('preserveAspectRatio', 'xMidYMid slice');

然后用如下样式属性填充圆圈:

  circle
    .filter(node => node.depth !== 0 && node.type === EngagementType.Product)
    .append('circle')
    .classed('Engagement-GraphNode', true)
    .classed('Engagement-GraphNodeBackground', true)
    .classed('Engagement-GraphLeaf', node => node && (node.depth === 4 && !node.isExtraNode))
    .style('fill', d => `url(#insightImage-${d.id})`)
    .style('opacity', node => (node.visible) ? 1 : 0)
    .style('visibility', node => (node.visible) ? 'visible' : 'hidden')
    .on('click', node => onClick(node));
  circle
    .filter(node => node.depth !== 0 && node.type === EngagementType.Community)
    .append('circle')
    .classed('Engagement-GraphNode', true)
    .classed('Engagement-GraphNodeBackground', true)
    .classed('Engagement-GraphLeaf', node => node && (node.depth === 4 && !node.isExtraNode))
    .style('fill', d => `url(#insightImageCom-${d.id})`)
    .style('opacity', node => (node.visible) ? 1 : 0)
    .style('visibility', node => (node.visible) ? 'visible' : 'hidden')
    .on('click', node => onClick(node));

我对assets文件夹中的图像有类似的方法,我有一堆images.svg,然后对产品组执行与上述相同的操作,但实际上分别调用它们会占用大量的空间...

在这种情况下,我通过节点的ID调用节点以分别从tha资产文件夹中分配图像:

      circle
    .filter(node => node.type === EngagementType.ProductGroup && node.id === 'a6qb000000003olAAA')
    .append("pattern")
    .attr("id", `insightImageInitiative`)
    .attr("patternContentUnits", "objectBoundingBox")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("x", 0)
    .attr("y", 0)
    .append("image")
    .attr("xlink:href", './assets/d3-icons/initiative.svg')
    .attr("x", 0.2)
    .attr("y", 0.2)
    .attr("width", 0.60)
    .attr("height", 0.60)
    .attr('preserveAspectRatio', 'xMidYMid slice');
  circle
    .filter(node => node.type === EngagementType.ProductGroup && node.id === 'a6qb000000003okAAA')
    .append("pattern")
    .attr("id", `insightImageIndustry`)
    .attr("patternContentUnits", "objectBoundingBox")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("x", 0)
    .attr("y", 0)
    .append("image")
    .attr("xlink:href", './assets/d3-icons/industry.svg')
    .attr("x", 0.2)
    .attr("y", 0.2)
    .attr("width", 0.60)
    .attr("height", 0.60)
    .attr('preserveAspectRatio', 'xMidYMid slice');

我只是尝试使用样式(填充),但这对我不起作用!

    .style('fill', d => `url('./assets/d3-icons/calendar.svg')`) 
    .style('fill', d => `url('https://tiktok.org/products/${d.id}/standard')`) 

什么都没出现!

关于如何不同地设置图像或如何消除如此多的重复的一些建议/帮助将是惊人的!

我确信有一种更简单的方法来设置背景图像,但是端点当然不同。

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

您使用的数据对象可以是任何东西。最合适的模式是在将其附加到元素选择之前进行准备,以使您已经拥有所需的一切。

以下示例定义了一个数据结构,该数据结构从类型到每个类别不同的​​数据进行映射。这仅是一个示例,核心是.map()函数中发生的事情:您向数据对象添加了一个或多个属性,以便可以在node.type与编写方法之间建立联系模式恰好适合每个节点。

将需要一种类似的方式来从ID连接到资产文件夹图像。

为每个节点定义一个模式,但是仅在node.depth !== 0时才绘制一个圆,这有点奇怪,但是为了您理解,仅使用过滤器功能来标识需要使用的非顶级条目呈现出一个圆圈。

const details = new Map([
    [EngagementType.Product, { path: 'products', prefix: 'insightImage'} ],
    [EngagementType.Community, { path: 'comunities', prefix: 'insightImageCom'} ],
    // and so on...
]);

// enhance your data, each node is a shallow copy with extra properties
const renderingData = this.nodes.map(node => Object.assign({
    patternId: `${details.get(node.type).prefix}-${d.id}`,
    imageUrl: `https://tiktok.org/${details.get(node.type).path}/${d.id}/standard`,
    isLeaf: node.depth === 4 && !node.isExtraNode
}, node));

const circle = d3.selectAll('circle.node')
    .data(renderingData)
    .enter();

// now it is straight forward
circle
    .append("pattern")
    .attr("id", d => node.patternId)
    .attr("patternContentUnits", "objectBoundingBox")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("x", 0)
    .attr("y", 0);
    .append("image")
    .attr("xlink:href", d => d.imageUrl)
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 1)
    .attr("height", 1)
    .attr('preserveAspectRatio', 'xMidYMid slice')
  // and now you filter for non-toplevel nodes
  .filter(node => node.depth !== 0)
    .append('circle')
    .classed('Engagement-GraphNode', true)
    .classed('Engagement-GraphNodeBackground', true)
    .classed('Engagement-GraphLeaf', node => node.isLeaf)
    .style('fill', d => `url(#${d.patternId})`)
    .style('opacity', node => (node.visible) ? 1 : 0)
    .style('visibility', node => (node.visible) ? 'visible' : 'hidden')
    .on('click', node => onClick(node));