传递给d3.dispatch的条件

huangapple go评论58阅读模式
英文:

Passing on condition to d3.dispatch

问题

以下是使用 d3.dispatch 的部分代码:

//with dispatch
const dispatch = d3.dispatch('one', 'two');

dispatch.on('one', function() {
    this.attr('opacity', '0.5');
});
dispatch.on('two', function(val) {
    this.attr('opacity', (() => {
        return val;
    }))
});
listener.on('mouseover', function() {
        dispatch.call('one', rect)
    })
    .on('mouseout', function() {
        dispatch.call('one', rect)
    })
    .on('mousemove', function(event) {
        const points = d3.pointer(event);
        const pointY = points[1];
        const selOne = [rect.nodes()][0].filter((a) => {
            const y = a.y.baseVal.value <= pointY && pointY <= a.height.baseVal.value + a.y.baseVal.value;
            return y;
        });
        const currData = (selOne.length === 1) ? selOne[0].__data__['id'] : null;
        const val = (d, i) => (d.id === currData) ? '1' : '0.5';
        dispatch.call('two', d3.selectAll('.rectContainer>rect'))
    });

这是使用 d3.dispatch 实现的交互部分的代码。

英文:

I am trying to see how can I author an interaction to d3.dispatch.

Without dispatch, the interaction is following

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-js -->

const data = [
    { &quot;id&quot;: 1 },
    { &quot;id&quot;: 2 },
    { &quot;id&quot;: 3 },
    { &quot;id&quot;: 4 },
    { &quot;id&quot;: 5 },
    { &quot;id&quot;: 6 },
    { &quot;id&quot;: 7 },
    { &quot;id&quot;: 8 }
];

const svgns = &#39;http://www.w3.org/2000/svg&#39;;
const width = 600;
const height = 450;

const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;

const svg = d3.select(&#39;body&#39;)
    .append(&#39;svg&#39;)
    .attr(&#39;xmlns&#39;, svgns)
    .attr(&#39;viewBox&#39;, `0 0 ${width} ${height}`);

const bgRect = svg.append(&#39;rect&#39;)
    .classed(&#39;boundary&#39;, true)
    .attr(&#39;x&#39;, &#39;0&#39;)
    .attr(&#39;y&#39;, &#39;0&#39;)
    .attr(&#39;width&#39;, `${width}`)
    .attr(&#39;height&#39;, `${height}`)
    .attr(&#39;fill&#39;, `#E6E6E6`);

//-------------BUILD COLOR SCALE---------------------------//
const interpolatorSequentialMultiHueName = [
    &#39;d3.interpolateViridis&#39;
]

const xColorScale = d3.scaleSequential().domain(d3.extent(data, d =&gt; d.id))
    .interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));

//-------------BUILD RECT---------------------------//   

const rect = svg.append(&#39;g&#39;)
    .classed(&#39;rectContainer&#39;, true)
    .selectAll(&#39;rect&#39;)
    .data(data)
    .join(&#39;rect&#39;)
    .attr(&#39;x&#39;, `${posX}`)
    .attr(&#39;y&#39;, ((d, i) =&gt; posY + heightRect * i + padding * i))
    .attr(&#39;width&#39;, `${widthRect}`)
    .attr(&#39;height&#39;, `${heightRect}`)
    .attr(&#39;fill&#39;, (d =&gt; xColorScale(d.id)))
    .attr(&#39;opacity&#39;, &#39;0.5&#39;);

//listener
const listener =
    d3.select(&#39;rect&#39;)
    .clone()
    .raise()
    .attr(&#39;x&#39;, `${posX}`)
    .attr(&#39;y&#39;, ((d, i) =&gt; { return posY }))
    .attr(&#39;width&#39;, `${widthRect}`)
    .attr(&#39;height&#39;, ((d, i) =&gt; { const node = rect.nodes(); const len = node.length - 1; return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value }))
    .attr(&#39;fill&#39;, &#39;none&#39;)
    .attr(&#39;class&#39;, &#39;listener&#39;)
    .style(&quot;pointer-events&quot;, &quot;all&quot;);

// interaction without dispatch    

listener.on(&#39;mouseover&#39;, function() {
        rect.attr(&#39;opacity&#39;, &#39;0.5&#39;)
    })
    .on(&#39;mouseout&#39;, function(event) {
        rect.attr(&#39;opacity&#39;, &#39;0.5&#39;);

    })
    .on(&#39;mousemove&#39;, function(event) {
        const points = d3.pointer(event);
        const pointY = points[1];
       
        // what is the current selection 
        const selOne = [rect.nodes()][0].filter((a) =&gt; {
            const y = a.y.baseVal.value &lt;= pointY &amp;&amp; pointY &lt;= a.height.baseVal.value + a.y.baseVal.value;
            return y;
        });
        //what is the data of the curent selection
        const currData = (selOne.length === 1) ? selOne[0].__data__[&#39;id&#39;] : null;
        //conditional statement to control opacity based on current selection
        d3.selectAll(&#39;.rectContainer&gt;rect&#39;)
            .attr(&#39;opacity&#39;, ((d, i) =&gt; (d.id === currData) ? &#39;1&#39; : &#39;0.5&#39;))

    });

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;

&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;https://d3js.org/d3.v7.min.js&quot;&gt;&lt;/script&gt;


&lt;body&gt;
&lt;/body&gt;

&lt;script src=&quot;min.js&quot;&gt;
&lt;/script&gt;

&lt;/html&gt;

<!-- end snippet -->

I tried to convert the above to d3.dispatch and this is what I tried which did not work

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const data = [
    { &quot;id&quot;: 1 },
    { &quot;id&quot;: 2 },
    { &quot;id&quot;: 3 },
    { &quot;id&quot;: 4 },
    { &quot;id&quot;: 5 },
    { &quot;id&quot;: 6 },
    { &quot;id&quot;: 7 },
    { &quot;id&quot;: 8 }
];

const svgns = &#39;http://www.w3.org/2000/svg&#39;;
const width = 600;
const height = 450;

const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;


const svg = d3.select(&#39;body&#39;)
    .append(&#39;svg&#39;)
    .attr(&#39;xmlns&#39;, svgns)
    .attr(&#39;viewBox&#39;, `0 0 ${width} ${height}`);

const bgRect = svg.append(&#39;rect&#39;)
    .classed(&#39;boundary&#39;, true)
    .attr(&#39;x&#39;, &#39;0&#39;)
    .attr(&#39;y&#39;, &#39;0&#39;)
    .attr(&#39;width&#39;, `${width}`)
    .attr(&#39;height&#39;, `${height}`)
    .attr(&#39;fill&#39;, `#E6E6E6`);

//-------------BUILD COLOR SCALE---------------------------//
const interpolatorSequentialMultiHueName = [
    &#39;d3.interpolateViridis&#39;
]

const xColorScale = d3.scaleSequential().domain(d3.extent(data, d =&gt; d.id))
    .interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));

//-------------BUILD RECT---------------------------//   

const rect = svg.append(&#39;g&#39;)
    .classed(&#39;rectContainer&#39;, true)
    .selectAll(&#39;rect&#39;)
    .data(data)
    .join(&#39;rect&#39;)
    .attr(&#39;x&#39;, `${posX}`)
    .attr(&#39;y&#39;, ((d, i) =&gt; posY + heightRect * i + padding * i))
    .attr(&#39;width&#39;, `${widthRect}`)
    .attr(&#39;height&#39;, `${heightRect}`)
    .attr(&#39;fill&#39;, (d =&gt; xColorScale(d.id)))
    .attr(&#39;opacity&#39;, &#39;0.5&#39;);

//listener
const listener =
    d3.select(&#39;rect&#39;)
    .clone()
    .raise()
    .attr(&#39;x&#39;, `${posX}`)
    .attr(&#39;y&#39;, ((d, i) =&gt; { return posY }))
    .attr(&#39;width&#39;, `${widthRect}`)
    .attr(&#39;height&#39;, ((d, i) =&gt; { const node = rect.nodes(); const len = node.length - 1; return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value }))
    .attr(&#39;fill&#39;, &#39;none&#39;)
    .attr(&#39;class&#39;, &#39;listener&#39;)
    .style(&quot;pointer-events&quot;, &quot;all&quot;);

//with dispatch
const dispatch = d3.dispatch(&#39;one&#39;, &#39;two&#39;);

dispatch.on(&#39;one&#39;, function() {
    this.attr(&#39;opacity&#39;, &#39;0.5&#39;);
});
dispatch.on(&#39;two&#39;, function(val) {
    this.attr(&#39;opacity&#39;, (() =&gt; {
        return val;
    }))
});
listener.on(&#39;mouseover&#39;, function() {
        dispatch.call(&#39;one&#39;, rect)
    })
    .on(&#39;mouseout&#39;, function() {
        dispatch.call(&#39;one&#39;, rect)
    })
    .on(&#39;mousemove&#39;, function(event) {
        const points = d3.pointer(event);
        const pointY = points[1];
        const selOne = [rect.nodes()][0].filter((a) =&gt; {
            const y = a.y.baseVal.value &lt;= pointY &amp;&amp; pointY &lt;= a.height.baseVal.value + a.y.baseVal.value;
            return y;
        });
        const currData = (selOne.length === 1) ? selOne[0].__data__[&#39;id&#39;] : null;
        const val = (d, i) =&gt; (d.id === currData) ? &#39;1&#39; : &#39;0.5&#39;;
        dispatch.call(&#39;two&#39;, d3.selectAll(&#39;.rectContainer&gt;rect&#39;))
    })

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;

&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;https://d3js.org/d3.v7.min.js&quot;&gt;&lt;/script&gt;


&lt;body&gt;
&lt;/body&gt;

&lt;script src=&quot;min.js&quot;&gt;
&lt;/script&gt;

&lt;/html&gt;

<!-- end snippet -->

答案1

得分: 1

希望这只是一个练习,因为老实说,这段代码对于生产来说过于繁琐,这里有很多东西需要重构。

关于你的问题,问题在于你忘记了值,这是dispatch.call中的第三个参数。但即使你传递了val,也不会起作用,因为整个选择中的值都是相同的。

因此,传递currData

dispatch.call('two', d3.selectAll('.rectContainer>rect'), currData)

然后,在你的dispatch中:

dispatch.on('two', function(currData) {
    this.attr('opacity', (d, i) => (d.id === currData) ? '1' : '0.5')
});

以下是经过这些更改的代码:

const data = [{
    "id": 1
  },
  {
    "id": 2
  },
  {
    "id": 3
  },
  {
    "id": 4
  },
  {
    "id": 5
  },
  {
    "id": 6
  },
  {
    "id": 7
  },
  {
    "id": 8
  }
];

const svgns = 'http://www.w3.org/2000/svg';
const width = 600;
const height = 450;

const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;

const svg = d3.select('body')
  .append('svg')
  .attr('xmlns', svgns)
  .attr('viewBox', `0 0 ${width} ${height}`);

const bgRect = svg.append('rect')
  .classed('boundary', true)
  .attr('x', '0')
  .attr('y', '0')
  .attr('width', `${width}`)
  .attr('height', `${height}`)
  .attr('fill', `#E6E6E6`);

//-------------构建颜色比例---------------------------//
const interpolatorSequentialMultiHueName = [
  'd3.interpolateViridis'
];

const xColorScale = d3.scaleSequential().domain(d3.extent(data, d => d.id))
  .interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));

//-------------构建矩形---------------------------//   

const rect = svg.append('g')
  .classed('rectContainer', true)
  .selectAll('rect')
  .data(data)
  .join('rect')
  .attr('x', `${posX}`)
  .attr('y', ((d, i) => posY + heightRect * i + padding * i))
  .attr('width', `${widthRect}`)
  .attr('height', `${heightRect}`)
  .attr('fill', (d => xColorScale(d.id)))
  .attr('opacity', '0.5');

//listener
const listener =
  d3.select('rect')
  .clone()
  .raise()
  .attr('x', `${posX}`)
  .attr('y', ((d, i) => {
    return posY
  }))
  .attr('width', `${widthRect}`)
  .attr('height', ((d, i) => {
    const node = rect.nodes();
    const len = node.length - 1;
    return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value
  }))
  .attr('fill', 'none')
  .attr('class', 'listener')
  .style("pointer-events", "all");

//带有dispatch
const dispatch = d3.dispatch('one', 'two');

dispatch.on('one', function() {
  this.attr('opacity', '0.5');
});
dispatch.on('two', function(currData) {
  this.attr('opacity', (d, i) => (d.id === currData) ? '1' : '0.5')
});
listener.on('mouseover', function() {
    dispatch.call('one', rect)
  })
  .on('mouseout', function() {
    dispatch.call('one', rect)
  })
  .on('mousemove', function(event) {
    const points = d3.pointer(event);
    const pointY = points[1];
    const selOne = [rect.nodes()][0].filter((a) => {
      const y = a.y.baseVal.value <= pointY && pointY <= a.height.baseVal.value + a.y.baseVal.value;
      return y;
    });
    const currData = (selOne.length === 1) ? selOne[0].__data__['id'] : null;
    dispatch.call('two', d3.selectAll('.rectContainer>rect'), currData)
  });

我已经为你翻译好了。如果你有任何其他问题,请随时提问。

英文:

I hope this is just an exercise, because honestly this code is way too cumbersome for production, lots of things here should be refactored.

Regarding your question, the issue is that you're forgetting the value, which is the third argument in dispatch.call. But even if you passed val that will not work, because you would have the same value for the entire selection.

Thus, pass the currData:

dispatch.call(&#39;two&#39;, d3.selectAll(&#39;.rectContainer&gt;rect&#39;), currData)

And then, in your dispatch:

dispatch.on(&#39;two&#39;, function(currData) {
this.attr(&#39;opacity&#39;, (d, i) =&gt; (d.id === currData) ? &#39;1&#39; : &#39;0.5&#39;)
});

Here's your code with those changes:

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-js -->

const data = [{
&quot;id&quot;: 1
},
{
&quot;id&quot;: 2
},
{
&quot;id&quot;: 3
},
{
&quot;id&quot;: 4
},
{
&quot;id&quot;: 5
},
{
&quot;id&quot;: 6
},
{
&quot;id&quot;: 7
},
{
&quot;id&quot;: 8
}
];
const svgns = &#39;http://www.w3.org/2000/svg&#39;;
const width = 600;
const height = 450;
const posX = 40;
const posY = 20;
const heightRect = 40;
const widthRect = 40;
const padding = 10;
const svg = d3.select(&#39;body&#39;)
.append(&#39;svg&#39;)
.attr(&#39;xmlns&#39;, svgns)
.attr(&#39;viewBox&#39;, `0 0 ${width} ${height}`);
const bgRect = svg.append(&#39;rect&#39;)
.classed(&#39;boundary&#39;, true)
.attr(&#39;x&#39;, &#39;0&#39;)
.attr(&#39;y&#39;, &#39;0&#39;)
.attr(&#39;width&#39;, `${width}`)
.attr(&#39;height&#39;, `${height}`)
.attr(&#39;fill&#39;, `#E6E6E6`);
//-------------BUILD COLOR SCALE---------------------------//
const interpolatorSequentialMultiHueName = [
&#39;d3.interpolateViridis&#39;
]
const xColorScale = d3.scaleSequential().domain(d3.extent(data, d =&gt; d.id))
.interpolator(eval(`${interpolatorSequentialMultiHueName[0]}`));
//-------------BUILD RECT---------------------------//   
const rect = svg.append(&#39;g&#39;)
.classed(&#39;rectContainer&#39;, true)
.selectAll(&#39;rect&#39;)
.data(data)
.join(&#39;rect&#39;)
.attr(&#39;x&#39;, `${posX}`)
.attr(&#39;y&#39;, ((d, i) =&gt; posY + heightRect * i + padding * i))
.attr(&#39;width&#39;, `${widthRect}`)
.attr(&#39;height&#39;, `${heightRect}`)
.attr(&#39;fill&#39;, (d =&gt; xColorScale(d.id)))
.attr(&#39;opacity&#39;, &#39;0.5&#39;);
//listener
const listener =
d3.select(&#39;rect&#39;)
.clone()
.raise()
.attr(&#39;x&#39;, `${posX}`)
.attr(&#39;y&#39;, ((d, i) =&gt; {
return posY
}))
.attr(&#39;width&#39;, `${widthRect}`)
.attr(&#39;height&#39;, ((d, i) =&gt; {
const node = rect.nodes();
const len = node.length - 1;
return node[len].y.baseVal.value + node[len].height.baseVal.value - node[0].y.baseVal.value
}))
.attr(&#39;fill&#39;, &#39;none&#39;)
.attr(&#39;class&#39;, &#39;listener&#39;)
.style(&quot;pointer-events&quot;, &quot;all&quot;);
//with dispatch
const dispatch = d3.dispatch(&#39;one&#39;, &#39;two&#39;);
dispatch.on(&#39;one&#39;, function() {
this.attr(&#39;opacity&#39;, &#39;0.5&#39;);
});
dispatch.on(&#39;two&#39;, function(currData) {
this.attr(&#39;opacity&#39;, (d, i) =&gt; (d.id === currData) ? &#39;1&#39; : &#39;0.5&#39;)
});
listener.on(&#39;mouseover&#39;, function() {
dispatch.call(&#39;one&#39;, rect)
})
.on(&#39;mouseout&#39;, function() {
dispatch.call(&#39;one&#39;, rect)
})
.on(&#39;mousemove&#39;, function(event) {
const points = d3.pointer(event);
const pointY = points[1];
const selOne = [rect.nodes()][0].filter((a) =&gt; {
const y = a.y.baseVal.value &lt;= pointY &amp;&amp; pointY &lt;= a.height.baseVal.value + a.y.baseVal.value;
return y;
});
const currData = (selOne.length === 1) ? selOne[0].__data__[&#39;id&#39;] : null;
dispatch.call(&#39;two&#39;, d3.selectAll(&#39;.rectContainer&gt;rect&#39;), currData)
})

<!-- language: lang-html -->

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;

&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;https://d3js.org/d3.v7.min.js&quot;&gt;&lt;/script&gt;


&lt;body&gt;
&lt;/body&gt;

&lt;script src=&quot;min.js&quot;&gt;
&lt;/script&gt;

&lt;/html&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年3月7日 22:58:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75663619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定