如何将d3.js柱状图创建为Svelte组件并正确绑定d3到SVG?

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

how to create d3.js bar chart as a Svelte component and bind d3 to SVG properly?

问题

以下是d3.js图表的代码部分的中文翻译:

const data = [{
    "date": "2010-08-06",
    "count": 32348
  },
  {
    "date": "2010-08-07",
    "count": 32454
  },
  {
    "date": "2010-08-08",
    "count": 32648
  },
  {
    "date": "2010-08-09",
    "count": 32812
  },
  {
    "date": "2010-08-10",
    "count": 32764
  },
  {
    "date": "2010-08-11",
    "count": 32668
  },
  {
    "date": "2010-08-12",
    "count": 32484
  },
  {
    "date": "2010-08-13",
    "count": 32167
  },
  {
    "date": "2010-08-14",
    "count": 32304
  },
  {
    "date": "2010-08-15",
    "count": 32446
  },
  {
    "date": "2010-08-16",
    "count": 32670
  },
  {
    "date": "2010-08-17",
    "count": 32778
  },
  {
    "date": "2010-08-18",
    "count": 32756
  },
  {
    "date": "2010-08-19",
    "count": 32580
  }
]

const formatDate4 = d3.timeFormat("%m%d%Y")
const formatDate5 = d3.timeFormat('%Y-%m-%d')
const formatDate6 = d3.timeFormat("%b %d, %Y");

const bisectDate = d3.bisector(function(d) {
  return d.date;
}).left;

var parseTime = d3.timeParse("%Y-%m-%d")

const pageWidth = window.innerWidth

let wrapWidth = 969,
  mapRatio = .51

let margin = {
  top: 0,
  right: 0,
  bottom: 10,
  left: 0
}

let timeW = 960,
  timeH = 450

let timeMargin = {
    top: 20,
    right: 250,
    bottom: 80,
    left: 60
  },
  timeMargin2 = {
    top: 410,
    right: 250,
    bottom: 30,
    left: 60
  },
  timeWidth = timeW - timeMargin.left - timeMargin.right,
  timeHeight = timeH - timeMargin.top - timeMargin.bottom,
  timeHeight2 = timeH - timeMargin2.top - timeMargin2.bottom;

let timeseries = d3.select("#timeseries-container").append('svg')
  .attr('id', 'timeseries')
  .attr("width", timeWidth + timeMargin.left + timeMargin.right)
  .attr("height", timeHeight + timeMargin.top + timeMargin.bottom)

var graph = timeseries.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

var parseDate = d3.timeParse("%Y-%m-%d");

var x2 = d3.scaleTime().range([0, timeWidth]),
  x3 = d3.scaleTime().range([0, timeWidth]),
  y2 = d3.scaleLinear().range([timeHeight, 0]),
  y3 = d3.scaleLinear().range([timeHeight2, 0]);

var xAxis2 = d3.axisBottom(x2).ticks(5)
  .tickFormat(d3.timeFormat("%b %d %Y")),
  yAxis2 = d3.axisLeft(y2).ticks(3);

timeseries.append("defs").append("clipPath")
  .attr("id", "clip")
  .append("rect")
  .attr("width", timeWidth)
  .attr("height", timeHeight)

var chartfocus = timeseries.append("g")
  .attr("class", "chartfocus")
  .attr("transform", "translate(" + timeMargin.left + "," + timeMargin.top + ")");

const natlData = data;

console.log('natlData', natlData)
updateChart(natlData)

function updateChart(data) {

  const dataOld = data.map(a => ({ ...a }));

  data.forEach(d => {
    d.date = parseTime(d.date);
  })

  x2.domain(d3.extent(data, function(d) {
    return d.date;
  }));

  const charttooltip = d3.select("#time").append("div")
    .attr("class", "charttooltip");

  const chartguideline = d3.select("body").append("div")
    .attr("class", "chartguideline");

  let topY = d3.max(data, d => d.count)

  timeseries.selectAll(".axis").remove();

  let bisectDate = d3.bisector(function(d) {
    return d.date;
  }).left;

  chartfocus
    .on("mouseover", mouseover)
    .on("mousemove", mousemove)
    .on("mouseout", mouseout)

  y2.domain([0, topY]).nice();
  d3.select(".axis--y")
    .transition(1000)
    .call(yAxis2)

  d3.selectAll('.bar').remove()

  let dailyBars = chartfocus.selectAll(".bar")
    .data(data)
    .enter().append("rect")
    .attr("clip-path", "url(#clip)")
    .attr('class', d => 'bar d' + formatDate4(d.date))

  y2.domain([0, topY]).nice();

  d3.select(".axis--y")
    .transition(1000)
    .call(yAxis2)

  dailyBars.transition()
    .attr("width", d => x2(d3.timeDay.offset(d.date)) - x2(d.date))
    .attr('x', d => x2(d.date))
    .attr("y", d => y2(d.count))
    .attr("height", d => timeHeight - y2(d.count))

  chartfocus.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + timeHeight + ")")
    .call(xAxis2);

  chartfocus.append("g")
    .attr("class", "axis axis--y")
    .call(yAxis2);

  chartfocus.append("rect")
    .attr("class", "chartzoom")
    .attr("width", timeWidth)
    .attr("height", timeHeight)

  d3.selectAll('.focus').remove()

  function mouseover(event) {}

  function mousemove(event, d) {

    let outerMargins = pageWidth - wrapWidth
    let outerLeftMargin = outerMargins / 2

    let ex = event.x - timeMargin.left - outerLeftMargin

    var x0 = x2.invert(ex),
      i = bisectDate(data, x0, 1),
      d0 = data[i - 1],


<details>
<summary>英文:</summary>

I have a focus/context bar chart that I created in d3. I want to put it in Svelte so that I can use it as a component. But I&#39;m getting stuck figuring out how to bind the elements created in d3 to the html in the svelte component.

Below is the code for the d3.js chart

&lt;!-- begin snippet: js hide: true console: true babel: false --&gt;

&lt;!-- language: lang-js --&gt;

    const data = [{
        &quot;date&quot;: &quot;2010-08-06&quot;,
        &quot;count&quot;: 32348
      },
      {
        &quot;date&quot;: &quot;2010-08-07&quot;,
        &quot;count&quot;: 32454
      },
      {
        &quot;date&quot;: &quot;2010-08-08&quot;,
        &quot;count&quot;: 32648
      },
      {
        &quot;date&quot;: &quot;2010-08-09&quot;,
        &quot;count&quot;: 32812
      },
      {
        &quot;date&quot;: &quot;2010-08-10&quot;,
        &quot;count&quot;: 32764
      },
      {
        &quot;date&quot;: &quot;2010-08-11&quot;,
        &quot;count&quot;: 32668
      },
      {
        &quot;date&quot;: &quot;2010-08-12&quot;,
        &quot;count&quot;: 32484
      },
      {
        &quot;date&quot;: &quot;2010-08-13&quot;,
        &quot;count&quot;: 32167
      },
      {
        &quot;date&quot;: &quot;2010-08-14&quot;,
        &quot;count&quot;: 32304
      },
      {
        &quot;date&quot;: &quot;2010-08-15&quot;,
        &quot;count&quot;: 32446
      },
      {
        &quot;date&quot;: &quot;2010-08-16&quot;,
        &quot;count&quot;: 32670
      },
      {
        &quot;date&quot;: &quot;2010-08-17&quot;,
        &quot;count&quot;: 32778
      },
      {
        &quot;date&quot;: &quot;2010-08-18&quot;,
        &quot;count&quot;: 32756
      },
      {
        &quot;date&quot;: &quot;2010-08-19&quot;,
        &quot;count&quot;: 32580
      }
    ]

    const formatDate4 = d3.timeFormat(&quot;%m%d%Y&quot;)
    const formatDate5 = d3.timeFormat(&#39;%Y-%m-%d&#39;)
    const formatDate6 = d3.timeFormat(&quot;%b %d, %Y&quot;);

    const bisectDate = d3.bisector(function(d) {
      return d.date;
    }).left;

    var parseTime = d3.timeParse(&quot;%Y-%m-%d&quot;)

    const pageWidth = window.innerWidth

    let wrapWidth = 969,
      mapRatio = .51

    let margin = {
      top: 0,
      right: 0,
      bottom: 10,
      left: 0
    }

    let timeW = 960,
      timeH = 450

    let timeMargin = {
        top: 20,
        right: 250,
        bottom: 80,
        left: 60
      },
      timeMargin2 = {
        top: 410,
        right: 250,
        bottom: 30,
        left: 60
      },
      timeWidth = timeW - timeMargin.left - timeMargin.right,
      timeHeight = timeH - timeMargin.top - timeMargin.bottom,
      timeHeight2 = timeH - timeMargin2.top - timeMargin2.bottom;

    let timeseries = d3.select(&quot;#timeseries-container&quot;).append(&#39;svg&#39;)
      .attr(&#39;id&#39;, &#39;timeseries&#39;)
      .attr(&quot;width&quot;, timeWidth + timeMargin.left + timeMargin.right)
      .attr(&quot;height&quot;, timeHeight + timeMargin.top + timeMargin.bottom)

    var graph = timeseries.append(&#39;g&#39;).attr(&#39;transform&#39;, &#39;translate(&#39; + margin.left + &#39;,&#39; + margin.top + &#39;)&#39;);

    var parseDate = d3.timeParse(&quot;%Y-%m-%d&quot;);

    var x2 = d3.scaleTime().range([0, timeWidth]),
      x3 = d3.scaleTime().range([0, timeWidth]),
      y2 = d3.scaleLinear().range([timeHeight, 0]),
      y3 = d3.scaleLinear().range([timeHeight2, 0]);

    var xAxis2 = d3.axisBottom(x2).ticks(5)
      .tickFormat(d3.timeFormat(&quot;%b %d %Y&quot;)),
      yAxis2 = d3.axisLeft(y2).ticks(3);

    timeseries.append(&quot;defs&quot;).append(&quot;clipPath&quot;)
      .attr(&quot;id&quot;, &quot;clip&quot;)
      .append(&quot;rect&quot;)
      .attr(&quot;width&quot;, timeWidth)
      .attr(&quot;height&quot;, timeHeight)

    var chartfocus = timeseries.append(&quot;g&quot;)
      .attr(&quot;class&quot;, &quot;chartfocus&quot;)
      .attr(&quot;transform&quot;, &quot;translate(&quot; + timeMargin.left + &quot;,&quot; + timeMargin.top + &quot;)&quot;);

    const natlData = data;

    console.log(&#39;natlData&#39;, natlData)
    updateChart(natlData)

    function updateChart(data) {

      const dataOld = data.map(a =&gt; ({ ...a
      }));

      data.forEach(d =&gt; {
        d.date = parseTime(d.date);
      })

      x2.domain(d3.extent(data, function(d) {
        return d.date;
      }));

      const charttooltip = d3.select(&quot;#time&quot;).append(&quot;div&quot;)
        .attr(&quot;class&quot;, &quot;charttooltip&quot;);

      const chartguideline = d3.select(&quot;body&quot;).append(&quot;div&quot;)
        .attr(&quot;class&quot;, &quot;chartguideline&quot;);

      let topY = d3.max(data, d =&gt; d.count)

      timeseries.selectAll(&quot;.axis&quot;).remove();

      let bisectDate = d3.bisector(function(d) {
        return d.date;
      }).left;

      chartfocus
        .on(&quot;mouseover&quot;, mouseover)
        .on(&quot;mousemove&quot;, mousemove)
        .on(&quot;mouseout&quot;, mouseout)

      y2.domain([0, topY]).nice();
      d3.select(&quot;.axis--y&quot;)
        .transition(1000)
        .call(yAxis2)

      d3.selectAll(&#39;.bar&#39;).remove()

      let dailyBars = chartfocus.selectAll(&quot;bar&quot;)
        .data(data)
        .enter().append(&quot;rect&quot;)
        .attr(&quot;clip-path&quot;, &quot;url(#clip)&quot;)
        .attr(&#39;class&#39;, d =&gt; &#39;bar d&#39; + formatDate4(d.date))

      y2.domain([0, topY]).nice();

      d3.select(&quot;.axis--y&quot;)
        .transition(1000)
        .call(yAxis2)

      dailyBars.transition()
        .attr(&quot;width&quot;, d =&gt; x2(d3.timeDay.offset(d.date)) - x2(d.date))
        .attr(&#39;x&#39;, d =&gt; x2(d.date))
        .attr(&quot;y&quot;, d =&gt; y2(d.count))
        .attr(&quot;height&quot;, d =&gt; timeHeight - y2(d.count))

      chartfocus.append(&quot;g&quot;)
        .attr(&quot;class&quot;, &quot;axis axis--x&quot;)
        .attr(&quot;transform&quot;, &quot;translate(0,&quot; + timeHeight + &quot;)&quot;)
        .call(xAxis2);

      chartfocus.append(&quot;g&quot;)
        .attr(&quot;class&quot;, &quot;axis axis--y&quot;)
        .call(yAxis2);

      chartfocus.append(&quot;rect&quot;)
        .attr(&quot;class&quot;, &quot;chartzoom&quot;)
        .attr(&quot;width&quot;, timeWidth)
        .attr(&quot;height&quot;, timeHeight)

      d3.selectAll(&#39;.focus&#39;).remove()

      function mouseover(event) {}

      function mousemove(event, d) {

        let outerMargins = pageWidth - wrapWidth
        let outerLeftMargin = outerMargins / 2

        let ex = event.x - timeMargin.left - outerLeftMargin

        var x0 = x2.invert(ex),
          i = bisectDate(data, x0, 1),
          d0 = data[i - 1],
          d1 = data[i],
          d = x0 - d0.date &gt; d1.date - x0 ? d1 : d0;

        let dateX = d.date

        let dateXSimple = formatDate5(dateX)

        indvData = dataOld.filter(d =&gt; d.date == dateXSimple)

        dailyCount = []
        dailyCounts = []

        let tooltip_str = formatDate5(dateX) + &quot;&lt;/p&gt;Count: &lt;strong&gt;&quot; + indvData[0].count

        charttooltip
          .style(&#39;left&#39;, event.x - outerLeftMargin + &#39;px&#39;)
          .style(&quot;top&quot;, 0)
          .attr(&#39;x&#39;, 0)
          .html(d =&gt; tooltip_str)

        chartguideline
          .style(&#39;left&#39;, event.x - 1 + &#39;px&#39;)
          .style(&quot;top&quot;, timeMargin.top + &#39;px&#39;)
          .attr(&quot;width&quot;, d =&gt; x2(d3.timeDay.offset(dateX)) - x2(dateX))
          .style(&#39;height&#39;, timeHeight + timeMargin.top - timeMargin.bottom + 45 + &#39;px&#39;)
          .attr(&#39;x&#39;, 0)

        charttooltip
          .style(&quot;visibility&quot;, &quot;visible&quot;)

        chartguideline
          .style(&quot;visibility&quot;, &quot;visible&quot;)

      }

      function mouseout() {

        d3.selectAll(&#39;.bar&#39;).style(&#39;fill&#39;, &#39;#aaa&#39;)

        d3.selectAll(&#39;.bar&#39;).transition()
          .attr(&quot;width&quot;, d =&gt; x2(d3.timeDay.offset(d.date)) - x2(d.date))

        charttooltip.transition()
          .duration(0)
          .style(&quot;visibility&quot;, &quot;hidden&quot;)

        chartguideline.transition()
          .duration(0)
          .style(&quot;visibility&quot;, &quot;hidden&quot;)
      }

    };

&lt;!-- language: lang-css --&gt;

    .chartzoom {
      cursor: move;
      fill: none;
      pointer-events: all;
    }

    .charttooltip {
      position: absolute;
      pointer-events: none;
      padding: 10px;
      background: #fff;
      visibility: hidden;
      opacity: .9;
      -moz-box-shadow: 0 0 15px #aaa;
      -webkit-box-shadow: 0 0 15px #aaa;
      box-shadow: 0 0 15px #aaa;
      margin-left: 10px;
    }

    .charttooltip:before {
      right: 100%;
      top: 50%;
      border: solid transparent;
      content: &quot; &quot;;
      height: 0;
      width: 0;
      position: absolute;
      pointer-events: none;
      border-color: rgba(255, 255, 255, 0);
      border-right-color: #ffffff;
      border-width: 10px;
      margin-top: -10px;
    }

    .chartguideline {
      position: absolute;
      width: 1px;
      opacity: .5;
      pointer-events: none;
      background: #ef4136;
      visibility: hidden;
    }

    .bar {
      fill: #aaa;
      stroke-width: 1px;
      stroke: #000;
    }

&lt;!-- language: lang-html --&gt;

    &lt;script src=&quot;https://d3js.org/d3.v7.min.js&quot;&gt;&lt;/script&gt;

    &lt;div id=&quot;time&quot;&gt;
      &lt;div id=&quot;timeseries-container&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;

&lt;!-- end snippet --&gt;

The code I&#39;m using for the Svelte component is below. I&#39;m trying to put the main SVG in the body, and then bind my d3 code to it, but I&#39;m not able to get that working. Any help would be greatly appreciated.

I also made a [REPL][1], if that&#39;s easier.

        &lt;script&gt;
          import * as d3 from &quot;d3&quot;;
          const data = [
            {
              date: &quot;2010-08-06&quot;,
              count: 32348,
            },
            {
              date: &quot;2010-08-07&quot;,
              count: 32454,
            },
            {
              date: &quot;2010-08-08&quot;,
              count: 32648,
            },
            {
              date: &quot;2010-08-09&quot;,
              count: 32812,
            },
            {
              date: &quot;2010-08-10&quot;,
              count: 32764,
            },
            {
              date: &quot;2010-08-11&quot;,
              count: 32668,
            },
            {
              date: &quot;2010-08-12&quot;,
              count: 32484,
            },
            {
              date: &quot;2010-08-13&quot;,
              count: 32167,
            },
            {
              date: &quot;2010-08-14&quot;,
              count: 32304,
            },
            {
              date: &quot;2010-08-15&quot;,
              count: 32446,
            },
            {
              date: &quot;2010-08-16&quot;,
              count: 32670,
            },
            {
              date: &quot;2010-08-17&quot;,
              count: 32778,
            },
            {
              date: &quot;2010-08-18&quot;,
              count: 32756,
            },
            {
              date: &quot;2010-08-19&quot;,
              count: 32580,
            },
          ];

          let el;

          const formatDate4 = d3.timeFormat(&quot;%m%d%Y&quot;);
          const formatDate5 = d3.timeFormat(&quot;%Y-%m-%d&quot;);
          const formatDate6 = d3.timeFormat(&quot;%b %d, %Y&quot;);

          const bisectDate = d3.bisector(function (d) {
            return d.date;
          }).left;

          var parseTime = d3.timeParse(&quot;%Y-%m-%d&quot;);

          const pageWidth = window.innerWidth;

          let wrapWidth = 969,
            mapRatio = 0.51;

          let margin = { top: 0, right: 0, bottom: 10, left: 0 };

          let timeW = 960,
            timeH = 450;

          let timeMargin = { top: 20, right: 250, bottom: 80, left: 60 },
            timeMargin2 = { top: 410, right: 250, bottom: 30, left: 60 },
            timeWidth = timeW - timeMargin.left - timeMargin.right,
            timeHeight = timeH - timeMargin.top - timeMargin.bottom,
            timeHeight2 = timeH - timeMargin2.top - timeMargin2.bottom;

          let timeseries = d3
            .select(el)
            .attr(&quot;id&quot;, &quot;timeseries&quot;)
            .attr(&quot;width&quot;, timeWidth + timeMargin.left + timeMargin.right)
            .attr(&quot;height&quot;, timeHeight + timeMargin.top + timeMargin.bottom);

          var graph = timeseries
            .append(&quot;g&quot;)
            .attr(&quot;transform&quot;, &quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);

          var parseDate = d3.timeParse(&quot;%Y-%m-%d&quot;);

          var x2 = d3.scaleTime().range([0, timeWidth]),
            x3 = d3.scaleTime().range([0, timeWidth]),
            y2 = d3.scaleLinear().range([timeHeight, 0]),
            y3 = d3.scaleLinear().range([timeHeight2, 0]);

          var xAxis2 = d3.axisBottom(x2).ticks(5).tickFormat(d3.timeFormat(&quot;%b %d %Y&quot;)),
            yAxis2 = d3.axisLeft(y2).ticks(3);

          timeseries
            .append(&quot;defs&quot;)
            .append(&quot;clipPath&quot;)
            .attr(&quot;id&quot;, &quot;clip&quot;)
            .append(&quot;rect&quot;)
            .attr(&quot;width&quot;, timeWidth)
            .attr(&quot;height&quot;, timeHeight);

          var chartfocus = timeseries
            .append(&quot;g&quot;)
            .attr(&quot;class&quot;, &quot;chartfocus&quot;)
            .attr(
              &quot;transform&quot;,
              &quot;translate(&quot; + timeMargin.left + &quot;,&quot; + timeMargin.top + &quot;)&quot;
            );

          const natlData = data;

          console.log(&quot;natlData&quot;, natlData);
          updateChart(natlData);

          function updateChart(data) {
            const dataOld = data.map((a) =&gt; ({ ...a }));

            data.forEach((d) =&gt; {
              d.date = parseTime(d.date);
            });

            x2.domain(
              d3.extent(data, function (d) {
                return d.date;
              })
            );

            const charttooltip = d3
              .select(&quot;#time&quot;)
              .append(&quot;div&quot;)
              .attr(&quot;class&quot;, &quot;charttooltip&quot;);

            const chartguideline = d3
              .select(&quot;body&quot;)
              .append(&quot;div&quot;)
              .attr(&quot;class&quot;, &quot;chartguideline&quot;);

            let topY = d3.max(data, (d) =&gt; d.count);

            timeseries.selectAll(&quot;.axis&quot;).remove();

            let bisectDate = d3.bisector(function (d) {
              return d.date;
            }).left;

            chartfocus
              .on(&quot;mouseover&quot;, mouseover)
              .on(&quot;mousemove&quot;, mousemove)
              .on(&quot;mouseout&quot;, mouseout);

            y2.domain([0, topY]).nice();
            d3.select(&quot;.axis--y&quot;).transition(1000).call(yAxis2);

            d3.selectAll(&quot;.bar&quot;).remove();

            let dailyBars = chartfocus
              .selectAll(&quot;bar&quot;)
              .data(data)
              .enter()
              .append(&quot;rect&quot;)
              .attr(&quot;clip-path&quot;, &quot;url(#clip)&quot;)
              .attr(&quot;class&quot;, (d) =&gt; &quot;bar d&quot; + formatDate4(d.date));

            y2.domain([0, topY]).nice();

            d3.select(&quot;.axis--y&quot;).transition(1000).call(yAxis2);

            dailyBars
              .transition()
              .attr(&quot;width&quot;, (d) =&gt; x2(d3.timeDay.offset(d.date)) - x2(d.date))
              .attr(&quot;x&quot;, (d) =&gt; x2(d.date))
              .attr(&quot;y&quot;, (d) =&gt; y2(d.count))
              .attr(&quot;height&quot;, (d) =&gt; timeHeight - y2(d.count));

            chartfocus
              .append(&quot;g&quot;)
              .attr(&quot;class&quot;, &quot;axis axis--x&quot;)
              .attr(&quot;transform&quot;, &quot;translate(0,&quot; + timeHeight + &quot;)&quot;)
              .call(xAxis2);

            chartfocus.append(&quot;g&quot;).attr(&quot;class&quot;, &quot;axis axis--y&quot;).call(yAxis2);

            chartfocus
              .append(&quot;rect&quot;)
              .attr(&quot;class&quot;, &quot;chartzoom&quot;)
              .attr(&quot;width&quot;, timeWidth)
              .attr(&quot;height&quot;, timeHeight);

            d3.selectAll(&quot;.focus&quot;).remove();

            function mouseover(event) {}

            function mousemove(event, d) {
              let outerMargins = pageWidth - wrapWidth;
              let outerLeftMargin = outerMargins / 2;

              let ex = event.x - timeMargin.left - outerLeftMargin;

              var x0 = x2.invert(ex),
                i = bisectDate(data, x0, 1),
                d0 = data[i - 1],
                d1 = data[i],
                d = x0 - d0.date &gt; d1.date - x0 ? d1 : d0;

              let dateX = d.date;

              let dateXSimple = formatDate5(dateX);

              indvData = dataOld.filter((d) =&gt; d.date == dateXSimple);

              dailyCount = [];
              dailyCounts = [];

              let tooltip_str =
                formatDate5(dateX) + &quot;&lt;/p&gt;Count: &lt;strong&gt;&quot; + indvData[0].count;

              charttooltip
                .style(&quot;left&quot;, event.x - outerLeftMargin + &quot;px&quot;)
                .style(&quot;top&quot;, 0)
                .attr(&quot;x&quot;, 0)
                .html((d) =&gt; tooltip_str);

              chartguideline
                .style(&quot;left&quot;, event.x - 1 + &quot;px&quot;)
                .style(&quot;top&quot;, timeMargin.top + &quot;px&quot;)
                .attr(&quot;width&quot;, (d) =&gt; x2(d3.timeDay.offset(dateX)) - x2(dateX))
                .style(
                  &quot;height&quot;,
                  timeHeight + timeMargin.top - timeMargin.bottom + 45 + &quot;px&quot;
                )
                .attr(&quot;x&quot;, 0);

              charttooltip.style(&quot;visibility&quot;, &quot;visible&quot;);

              chartguideline.style(&quot;visibility&quot;, &quot;visible&quot;);
            }

            function mouseout() {
              d3.selectAll(&quot;.bar&quot;).style(&quot;fill&quot;, &quot;#aaa&quot;);

              d3.selectAll(&quot;.bar&quot;)
                .transition()
                .attr(&quot;width&quot;, (d) =&gt; x2(d3.timeDay.offset(d.date)) - x2(d.date));

              charttooltip.transition().duration(0).style(&quot;visibility&quot;, &quot;hidden&quot;);

              chartguideline.transition().duration(0).style(&quot;visibility&quot;, &quot;hidden&quot;);
            }
          }
        &lt;/script&gt;

        &lt;div id=&quot;time&quot;&gt;
          &lt;div id=&quot;timeseries-container&quot;&gt;
            &lt;svg width=&quot;100%&quot; viewBox=&quot;0 0 {timeW} {timeH}&quot; id=&quot;chart&quot; bind:this={el} /&gt;
          &lt;/div&gt;
        &lt;/div&gt;

        &lt;style&gt;
          .chartzoom {
            cursor: move;
            fill: none;
            pointer-events: all;
          }

          .charttooltip {
            position: absolute;
            pointer-events: none;
            padding: 10px;
            background: #fff;
            visibility: hidden;
            opacity: 0.9;
            -moz-box-shadow: 0 0 15px #aaa;
            -webkit-box-shadow: 0 0 15px #aaa;
            box-shadow: 0 0 15px #aaa;
            margin-left: 10px;
          }

          .charttooltip:before {
            right: 100%;
            top: 50%;
            border: solid transparent;
            content: &quot; &quot;;
            height: 0;
            width: 0;
            position: absolute;
            pointer-events: none;
            border-color: rgba(255, 255, 255, 0);
            border-right-color: #ffffff;
            border-width: 10px;
            margin-top: -10px;
          }

          .chartguideline {
            position: absolute;
            width: 1px;
            opacity: 0.5;
            pointer-events: none;
            background: #ef4136;
            visibility: hidden;
          }

          .bar {
            fill: #aaa;
            stroke-width: 1px;
            stroke: #000;
          }


  [1]: https://svelte.dev/repl/5e39f2a5622a43ba830b055b8fd6acf9?version=4.1.2

</details>


# 答案1
**得分**: 2

你已经非常接近了只需等待HTML元素被挂载到页面上然后再执行任何d3渲染逻辑因为它们会操作DOM在它们存在之前你不能操作它们对吗

我看到你将svg绑定到了`el`变量上在初始化阶段它是`undefined`所以`d3.select(el)`会选择到空内容这就是原因

要解决这个问题只需将任何涉及DOM的操作包装在`onMount`生命周期回调中

<details>
<summary>英文:</summary>

Youre very close! Just need to wait for the HTML elements to be mounted onto the page before executing any d3 render logics, cus they manipulate the DOM. You cant manipulate things before they even exist, can you?

I see that you bind the svg to `el` variable. Its `undefined` during the initialization phase, so `d3.select(el)` would select nothing, thats why.

To fix it, simply wrap anything DOM related in the `onMount` lifecycle callback. 

import { onMount } from "svelte"
// ……

let el;
let timeW = 960,
timeH = 450;

// ……
onMount(() => {
let timeseries = d3
.select(el)
// …

})

答案2

得分: 1

你也可以只使用一个action。这不需要任何导入、状态变量声明或this绑定:

&lt;script&gt;
  // ...
  function initialize(svg) {
    d3.select(svg)...
  }
&lt;/script&gt;

&lt;svg use:initialize ...&gt;
英文:

You also just use an action. This does not require any imports, state variable declarations or this bindings:

&lt;script&gt;
  // ...
  function initialize(svg) {
    d3.select(svg)...
  }
&lt;/script&gt;

&lt;svg use:initialize ...&gt;

(Also, by using d3 like this you are losing most of the advantages of Svelte. Unless any of its advanced algorithms are used, I would recommend just building the SVG directly in Svelte markup. That way it stays declarative and more readable.)

huangapple
  • 本文由 发表于 2023年8月10日 23:40:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76877308.html
匿名

发表评论

匿名网友

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

确定