plotly / plotly/plotly.js

[BUG] Colorbar thickness becomes too wide (~16k px) when marker.colorbar.thicknessmode='pixels' is applied via Plotly.restyle

未关闭
#8,012 3 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

bug P3 size: 1
主要语言
JavaScript
星标
18.3k
派生
2k
平均合并
2 天 12 小时
30 天内合并 PR
28

描述

Description

For a splom trace whose marker.colorbar exists but has no explicit thickness
(plotly.js defaults it), applying marker.colorbar.thicknessmode='pixels' afterwards with
Plotly.restyle collapses the whole splom layout:

  • plotly.js silently writes thickness: 16290 back into the user-visible trace
    (gd.data[1].marker.colorbar.thickness),
  • the colorbar then occupies ~16k px and the splom panels shrink from ~125 px to ~15 px per axis.

Setting the exact same value at newPlot time (Path A in the repro) renders fine.

Reproduced on every version I could test: 2.35.2, 3.5.0, 3.5.1, and current 3.8.2.

Steps to reproduce

Save the following as index.html and open it in a browser
(uses https://cdn.plot.ly/plotly-3.5.0.min.js; the bundled copy from any release works too).
The page renders both paths, then prints the colorbar object and the panel width under each plot.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>plotly.js splom + marker.colorbar restyle bug</title>
<!-- any plotly.js >= 2.35 works: use your own bundle or the CDN below -->
<script src="https://cdn.plot.ly/plotly-3.5.0.min.js"></script>
<style>
  body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; margin: 16px; }
  .row { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
  h3 { margin: 10px 0 4px; font-size: 15px; }
  pre { background: #f6f8fa; border-radius: 6px; padding: 10px; font-size: 12px;
        white-space: pre-wrap; max-width: 780px; }
  code { background: #f6f8fa; padding: 1px 4px; border-radius: 4px; }
</style>
</head>
<body>
<h1>splom layout collapses when <code>marker.colorbar.thicknessmode='pixels'</code>
is applied with <code>Plotly.restyle</code></h1>
<p>Same data + layout, same final <code>marker.colorbar</code> value. Difference is only <b>when</b>
it is applied:</p>
<ul>
  <li><b>Path A (normal):</b> <code>marker.colorbar = {thicknessmode: 'pixels'}</code> is in the
      figure at <code>newPlot</code> time.</li>
  <li><b>Path B (collapsed):</b> figure starts with <code>marker.colorbar = {}</code> (no explicit
      <code>thickness</code>), then <code>Plotly.restyle(gd, {'marker.colorbar.thicknessmode':'pixels'}, [1])</code>.</li>
</ul>
<p>After Path B, plotly.js writes <b>thickness: 16290</b> back into the trace's colorbar and the
splom panels collapse from ~125px to ~15px per axis.</p>

<div class="row">
  <div>
    <h3>Path A — set at newPlot time (normal)</h3>
    <div id="A" class="gd" style="width:750px;height:900px"></div>
    <pre id="mA">…</pre>
  </div>
  <div>
    <h3>Path B — set via Plotly.restyle (collapsed)</h3>
    <div id="B" class="gd" style="width:750px;height:900px"></div>
    <pre id="mB">…</pre>
  </div>
</div>

<script>
var DATA = [{"dimensions": [{"axis": {"matches": true}, "label": "sepal_length", "values": {"dtype": "f8", "bdata": "ZmZmZmZmFECamZmZmZkTQM3MzMzMzBJAZmZmZmZmEkAAAAAAAAAUQJqZmZmZmRVAZmZmZmZmEkAAAAAAAAAUQJqZmZmZmRFAmpmZmZmZE0CamZmZmZkVQDMzMzMzMxNAMzMzMzMzE0AzMzMzMzMRQDMzMzMzMxdAzczMzMzMFkCamZmZmZkVQGZmZmZmZhRAzczMzMzMFkBmZmZmZmYUQJqZmZmZmRVAZmZmZmZmFEBmZmZmZmYSQGZmZmZmZhRAMzMzMzMzE0AAAAAAAAAUQAAAAAAAABRAzczMzMzMFEDNzMzMzMwUQM3MzMzMzBJAMzMzMzMzE0CamZmZmZkVQM3MzMzMzBRAAAAAAAAAFkCamZmZmZkTQAAAAAAAABRAAAAAAAAAFkCamZmZmZkTQJqZmZmZmRFAZmZmZmZmFEAAAAAAAAAUQAAAAAAAABJAmpmZmZmZEUAAAAAAAAAUQGZmZmZmZhRAMzMzMzMzE0BmZmZmZmYUQGZmZmZmZhJAMzMzMzMzFUAAAAAAAAAUQA=="}}, {"axis": {"matches": true}, "label": "sepal_width", "values": {"dtype": "f8", "bdata": "AAAAAAAADEAAAAAAAAAIQJqZmZmZmQlAzczMzMzMCEDNzMzMzMwMQDMzMzMzMw9AMzMzMzMzC0AzMzMzMzMLQDMzMzMzMwdAzczMzMzMCECamZmZmZkNQDMzMzMzMwtAAAAAAAAACEAAAAAAAAAIQAAAAAAAABBAmpmZmZmZEUAzMzMzMzMPQAAAAAAAAAxAZmZmZmZmDkBmZmZmZmYOQDMzMzMzMwtAmpmZmZmZDUDNzMzMzMwMQGZmZmZmZgpAMzMzMzMzC0AAAAAAAAAIQDMzMzMzMwtAAAAAAAAADEAzMzMzMzMLQJqZmZmZmQlAzczMzMzMCEAzMzMzMzMLQGZmZmZmZhBAzczMzMzMEEDNzMzMzMwIQJqZmZmZmQlAAAAAAAAADEDNzMzMzMwIQAAAAAAAAAhAMzMzMzMzC0AAAAAAAAAMQGZmZmZmZgJAmpmZmZmZCUAAAAAAAAAMQGZmZmZmZg5AAAAAAAAACEBmZmZmZmYOQJqZmZmZmQlAmpmZmZmZDUBmZmZmZmYKQA=="}}, {"axis": {"matches": true}, "label": "petal_length", "values": {"dtype": "f8", "bdata": "ZmZmZmZm9j9mZmZmZmb2P83MzMzMzPQ/AAAAAAAA+D9mZmZmZmb2PzMzMzMzM/s/ZmZmZmZm9j8AAAAAAAD4P2ZmZmZmZvY/AAAAAAAA+D8AAAAAAAD4P5qZmZmZmfk/ZmZmZmZm9j+amZmZmZnxPzMzMzMzM/M/AAAAAAAA+D/NzMzMzMz0P2ZmZmZmZvY/MzMzMzMz+z8AAAAAAAD4PzMzMzMzM/s/AAAAAAAA+D8AAAAAAADwPzMzMzMzM/s/ZmZmZmZm/j+amZmZmZn5P5qZmZmZmfk/AAAAAAAA+D9mZmZmZmb2P5qZmZmZmfk/mpmZmZmZ+T8AAAAAAAD4PwAAAAAAAPg/ZmZmZmZm9j8AAAAAAAD4PzMzMzMzM/M/zczMzMzM9D8AAAAAAAD4P83MzMzMzPQ/AAAAAAAA+D/NzMzMzMz0P83MzMzMzPQ/zczMzMzM9D+amZmZmZn5P2ZmZmZmZv4/ZmZmZmZm9j+amZmZmZn5P2ZmZmZmZvY/AAAAAAAA+D9mZmZmZmb2Pw=="}}, {"axis": {"matches": true}, "label": "petal_width", "values": {"dtype": "f8", "bdata": "mpmZmZmZyT+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnJP5qZmZmZmdk/MzMzMzMz0z+amZmZmZnJP5qZmZmZmck/mpmZmZmZuT+amZmZmZnJP5qZmZmZmck/mpmZmZmZuT+amZmZmZm5P5qZmZmZmck/mpmZmZmZ2T+amZmZmZnZPzMzMzMzM9M/MzMzMzMz0z8zMzMzMzPTP5qZmZmZmck/mpmZmZmZ2T+amZmZmZnJPwAAAAAAAOA/mpmZmZmZyT+amZmZmZnJP5qZmZmZmdk/mpmZmZmZyT+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnZP5qZmZmZmbk/mpmZmZmZyT+amZmZmZm5P5qZmZmZmck/mpmZmZmZyT+amZmZmZm5P5qZmZmZmck/mpmZmZmZyT8zMzMzMzPTPzMzMzMzM9M/mpmZmZmZyT8zMzMzMzPjP5qZmZmZmdk/MzMzMzMz0z+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnJPw=="}}], "hovertemplate": "species=setosa<br>%{xaxis.title.text}=%{x}<br>%{yaxis.title.text}=%{y}<extra></extra>", "legendgroup": "setosa", "marker": {"color": "#636efa", "symbol": "circle"}, "name": "setosa", "showlegend": true, "type": "splom"}, {"dimensions": [{"axis": {"matches": true}, "label": "sepal_length", "values": {"dtype": "f8", "bdata": "AAAAAAAAHECamZmZmZkZQJqZmZmZmRtAAAAAAAAAFkAAAAAAAAAaQM3MzMzMzBZAMzMzMzMzGUCamZmZmZkTQGZmZmZmZhpAzczMzMzMFEAAAAAAAAAUQJqZmZmZmRdAAAAAAAAAGEBmZmZmZmYYQGZmZmZmZhZAzczMzMzMGkBmZmZmZmYWQDMzMzMzMxdAzczMzMzMGEBmZmZmZmYWQJqZmZmZmRdAZmZmZmZmGEAzMzMzMzMZQGZmZmZmZhhAmpmZmZmZGUBmZmZmZmYaQDMzMzMzMxtAzczMzMzMGkAAAAAAAAAYQM3MzMzMzBZAAAAAAAAAFkAAAAAAAAAWQDMzMzMzMxdAAAAAAAAAGECamZmZmZkVQAAAAAAAABhAzczMzMzMGkAzMzMzMzMZQGZmZmZmZhZAAAAAAAAAFkAAAAAAAAAWQGZmZmZmZhhAMzMzMzMzF0AAAAAAAAAUQGZmZmZmZhZAzczMzMzMFkDNzMzMzMwWQM3MzMzMzBhAZmZmZmZmFEDNzMzMzMwWQA=="}}, {"axis": {"matches": true}, "label": "sepal_width", "values": {"dtype": "f8", "bdata": "mpmZmZmZCUCamZmZmZkJQM3MzMzMzAhAZmZmZmZmAkBmZmZmZmYGQGZmZmZmZgZAZmZmZmZmCkAzMzMzMzMDQDMzMzMzMwdAmpmZmZmZBUAAAAAAAAAAQAAAAAAAAAhAmpmZmZmZAUAzMzMzMzMHQDMzMzMzMwdAzczMzMzMCEAAAAAAAAAIQJqZmZmZmQVAmpmZmZmZAUAAAAAAAAAEQJqZmZmZmQlAZmZmZmZmBkAAAAAAAAAEQGZmZmZmZgZAMzMzMzMzB0AAAAAAAAAIQGZmZmZmZgZAAAAAAAAACEAzMzMzMzMHQM3MzMzMzARAMzMzMzMzA0AzMzMzMzMDQJqZmZmZmQVAmpmZmZmZBUAAAAAAAAAIQDMzMzMzMwtAzczMzMzMCEBmZmZmZmYCQAAAAAAAAAhAAAAAAAAABEDNzMzMzMwEQAAAAAAAAAhAzczMzMzMBEBmZmZmZmYCQJqZmZmZmQVAAAAAAAAACEAzMzMzMzMHQDMzMzMzMwdAAAAAAAAABEBmZmZmZmYGQA=="}}, {"axis": {"matches": true}, "label": "petal_length", "values": {"dtype": "f8", "bdata": "zczMzMzMEkAAAAAAAAASQJqZmZmZmRNAAAAAAAAAEEBmZmZmZmYSQAAAAAAAABJAzczMzMzMEkBmZmZmZmYKQGZmZmZmZhJAMzMzMzMzD0AAAAAAAAAMQM3MzMzMzBBAAAAAAAAAEEDNzMzMzMwSQM3MzMzMzAxAmpmZmZmZEUAAAAAAAAASQGZmZmZmZhBAAAAAAAAAEkAzMzMzMzMPQDMzMzMzMxNAAAAAAAAAEECamZmZmZkTQM3MzMzMzBJAMzMzMzMzEUCamZmZmZkRQDMzMzMzMxNAAAAAAAAAFEAAAAAAAAASQAAAAAAAAAxAZmZmZmZmDkCamZmZmZkNQDMzMzMzMw9AZmZmZmZmFEAAAAAAAAASQAAAAAAAABJAzczMzMzMEkCamZmZmZkRQGZmZmZmZhBAAAAAAAAAEECamZmZmZkRQGZmZmZmZhJAAAAAAAAAEEBmZmZmZmYKQM3MzMzMzBBAzczMzMzMEEDNzMzMzMwQQDMzMzMzMxFAAAAAAAAACEBmZmZmZmYQQA=="}}, {"axis": {"matches": true}, "label": "petal_width", "values": {"dtype": "f8", "bdata": "ZmZmZmZm9j8AAAAAAAD4PwAAAAAAAPg/zczMzMzM9D8AAAAAAAD4P83MzMzMzPQ/mpmZmZmZ+T8AAAAAAADwP83MzMzMzPQ/ZmZmZmZm9j8AAAAAAADwPwAAAAAAAPg/AAAAAAAA8D9mZmZmZmb2P83MzMzMzPQ/ZmZmZmZm9j8AAAAAAAD4PwAAAAAAAPA/AAAAAAAA+D+amZmZmZnxP83MzMzMzPw/zczMzMzM9D8AAAAAAAD4PzMzMzMzM/M/zczMzMzM9D9mZmZmZmb2P2ZmZmZmZvY/MzMzMzMz+z8AAAAAAAD4PwAAAAAAAPA/mpmZmZmZ8T8AAAAAAADwPzMzMzMzM/M/mpmZmZmZ+T8AAAAAAAD4P5qZmZmZmfk/AAAAAAAA+D/NzMzMzMz0P83MzMzMzPQ/zczMzMzM9D8zMzMzMzPzP2ZmZmZmZvY/MzMzMzMz8z8AAAAAAADwP83MzMzMzPQ/MzMzMzMz8z/NzMzMzMz0P83MzMzMzPQ/mpmZmZmZ8T/NzMzMzMz0Pw=="}}], "hovertemplate": "species=versicolor<br>%{xaxis.title.text}=%{x}<br>%{yaxis.title.text}=%{y}<extra></extra>", "legendgroup": "versicolor", "marker": {"color": "#EF553B", "symbol": "circle"}, "name": "versicolor", "showlegend": true, "type": "splom"}, {"dimensions": [{"axis": {"matches": true}, "label": "sepal_length", "values": {"dtype": "f8", "bdata": "MzMzMzMzGUAzMzMzMzMXQGZmZmZmZhxAMzMzMzMzGUAAAAAAAAAaQGZmZmZmZh5AmpmZmZmZE0AzMzMzMzMdQM3MzMzMzBpAzczMzMzMHEAAAAAAAAAaQJqZmZmZmRlAMzMzMzMzG0DNzMzMzMwWQDMzMzMzMxdAmpmZmZmZGUAAAAAAAAAaQM3MzMzMzB5AzczMzMzMHkAAAAAAAAAYQJqZmZmZmRtAZmZmZmZmFkDNzMzMzMweQDMzMzMzMxlAzczMzMzMGkDNzMzMzMwcQM3MzMzMzBhAZmZmZmZmGECamZmZmZkZQM3MzMzMzBxAmpmZmZmZHUCamZmZmZkfQJqZmZmZmRlAMzMzMzMzGUBmZmZmZmYYQM3MzMzMzB5AMzMzMzMzGUCamZmZmZkZQAAAAAAAABhAmpmZmZmZG0DNzMzMzMwaQJqZmZmZmRtAMzMzMzMzF0AzMzMzMzMbQM3MzMzMzBpAzczMzMzMGkAzMzMzMzMZQAAAAAAAABpAzczMzMzMGECamZmZmZkXQA=="}}, {"axis": {"matches": true}, "label": "sepal_width", "values": {"dtype": "f8", "bdata": "ZmZmZmZmCkCamZmZmZkFQAAAAAAAAAhAMzMzMzMzB0AAAAAAAAAIQAAAAAAAAAhAAAAAAAAABEAzMzMzMzMHQAAAAAAAAARAzczMzMzMDECamZmZmZkJQJqZmZmZmQVAAAAAAAAACEAAAAAAAAAEQGZmZmZmZgZAmpmZmZmZCUAAAAAAAAAIQGZmZmZmZg5AzczMzMzMBECamZmZmZkBQJqZmZmZmQlAZmZmZmZmBkBmZmZmZmYGQJqZmZmZmQVAZmZmZmZmCkCamZmZmZkJQGZmZmZmZgZAAAAAAAAACEBmZmZmZmYGQAAAAAAAAAhAZmZmZmZmBkBmZmZmZmYOQGZmZmZmZgZAZmZmZmZmBkDNzMzMzMwEQAAAAAAAAAhAMzMzMzMzC0DNzMzMzMwIQAAAAAAAAAhAzczMzMzMCEDNzMzMzMwIQM3MzMzMzAhAmpmZmZmZBUCamZmZmZkJQGZmZmZmZgpAAAAAAAAACEAAAAAAAAAEQAAAAAAAAAhAMzMzMzMzC0AAAAAAAAAIQA=="}}, {"axis": {"matches": true}, "label": "petal_length", "values": {"dtype": "f8", "bdata": "AAAAAAAAGEBmZmZmZmYUQJqZmZmZmRdAZmZmZmZmFkAzMzMzMzMXQGZmZmZmZhpAAAAAAAAAEkAzMzMzMzMZQDMzMzMzMxdAZmZmZmZmGEBmZmZmZmYUQDMzMzMzMxVAAAAAAAAAFkAAAAAAAAAUQGZmZmZmZhRAMzMzMzMzFUAAAAAAAAAWQM3MzMzMzBpAmpmZmZmZG0AAAAAAAAAUQM3MzMzMzBZAmpmZmZmZE0DNzMzMzMwaQJqZmZmZmRNAzczMzMzMFkAAAAAAAAAYQDMzMzMzMxNAmpmZmZmZE0BmZmZmZmYWQDMzMzMzMxdAZmZmZmZmGECamZmZmZkZQGZmZmZmZhZAZmZmZmZmFEBmZmZmZmYWQGZmZmZmZhhAZmZmZmZmFkAAAAAAAAAWQDMzMzMzMxNAmpmZmZmZFUBmZmZmZmYWQGZmZmZmZhRAZmZmZmZmFECamZmZmZkXQM3MzMzMzBZAzczMzMzMFEAAAAAAAAAUQM3MzMzMzBRAmpmZmZmZFUBmZmZmZmYUQA=="}}, {"axis": {"matches": true}, "label": "petal_width", "values": {"dtype": "f8", "bdata": "AAAAAAAABEBmZmZmZmb+P83MzMzMzABAzczMzMzM/D+amZmZmZkBQM3MzMzMzABAMzMzMzMz+z/NzMzMzMz8P83MzMzMzPw/AAAAAAAABEAAAAAAAAAAQGZmZmZmZv4/zczMzMzMAEAAAAAAAAAAQDMzMzMzMwNAZmZmZmZmAkDNzMzMzMz8P5qZmZmZmQFAZmZmZmZmAkAAAAAAAAD4P2ZmZmZmZgJAAAAAAAAAAEAAAAAAAAAAQM3MzMzMzPw/zczMzMzMAEDNzMzMzMz8P83MzMzMzPw/zczMzMzM/D/NzMzMzMwAQJqZmZmZmfk/ZmZmZmZm/j8AAAAAAAAAQJqZmZmZmQFAAAAAAAAA+D9mZmZmZmb2P2ZmZmZmZgJAMzMzMzMzA0DNzMzMzMz8P83MzMzMzPw/zczMzMzMAEAzMzMzMzMDQGZmZmZmZgJAZmZmZmZm/j9mZmZmZmYCQAAAAAAAAARAZmZmZmZmAkBmZmZmZmb+PwAAAAAAAABAZmZmZmZmAkDNzMzMzMz8Pw=="}}], "hovertemplate": "species=virginica<br>%{xaxis.title.text}=%{x}<br>%{yaxis.title.text}=%{y}<extra></extra>", "legendgroup": "virginica", "marker": {"color": "#00cc96", "symbol": "circle"}, "name": "virginica", "showlegend": true, "type": "splom"}];
var LAYOUT = {"legend": {"title": {"text": "species"}, "tracegroupgap": 0}, "margin": {"t": 60}, "dragmode": "select", "xaxis": {"matches": "y"}, "xaxis2": {"matches": "y2"}, "xaxis3": {"matches": "y3"}, "xaxis4": {"matches": "y4"}, "height": 900, "width": 750, "selections": [{"x0": 3, "x1": 4, "xref": "x2", "y0": 8, "y1": 6, "yref": "y"}, {"x0": 5, "x1": 1, "xref": "x3", "y0": 5, "y1": 4, "yref": "y"}]};
function clone(o) { return JSON.parse(JSON.stringify(o)); }
function cb(gd) { return JSON.stringify(gd.data[1].marker.colorbar || {}); }
function ax(gd) { return gd._fullLayout.xaxis ? Math.round(gd._fullLayout.xaxis._length) + 'px' : '?'; }
function dump(gd, el) {
  var cbs = gd.querySelectorAll('.colorbar');
  var w = cbs.length ? Math.round(cbs[cbs.length-1].getBoundingClientRect().width) + 'px' : 'none';
  el.textContent = 'trace1.marker.colorbar = ' + cb(gd) +
                   '\npanel width (xaxis._length) = ' + ax(gd) +
                   '\nrendered colorbar width = ' + w;
}

// Path A
var gdA = document.getElementById('A');
var dataA = clone(DATA);
dataA[1].marker.colorbar = { thicknessmode: 'pixels' };
Plotly.newPlot(gdA, dataA, clone(LAYOUT)).then(function () {
  dump(gdA, document.getElementById('mA'));
});

// Path B
var gdB = document.getElementById('B');
var dataB = clone(DATA);
dataB[1].marker.colorbar = {};
Plotly.newPlot(gdB, dataB, clone(LAYOUT)).then(function () {
  dump(gdB, document.getElementById('mB'));
  return Plotly.restyle(gdB, { 'marker.colorbar.thicknessmode': 'pixels' }, [1]);
}).then(function () {
  dump(gdB, document.getElementById('mB'));
});
</script>
</body>
</html>
Actual vs expected (measured with plotly 3.8.2, headless Chrome)
Path A (set at newPlot) Path B (set via restyle)
trace[1].marker.colorbar after render {"thicknessmode":"pixels"} {"thickness":16290,"thicknessmode":"pixels"}
panel width (_fullLayout.xaxis._length) 125 px 15 px
rendered colorbar width 71 px 16331 px

Expected: Path B behaves like Path A — thickness defaulted to ~30 px for pixels mode,
panels stay 125 px.

(Note: with random synthetic splom data the exact numbers differ — e.g. thickness:17250,
axis len ~15 — but the collapse reproduces identically.)

Analysis

colorbar.thickness is size-dependent when left at its default: in fraction mode the
default is ~30 / plotHeight (a small fraction), converted to pixels only at calc time.
When Plotly.restyle flips thicknessmode to pixels on a colorbar that still holds the
fraction-mode default, the default is apparently not re-coerced for the new mode — the
old tiny fraction survives, is interpreted as raw pixels on the next colorbar calc, and the
result (~16k px) is also written back into gd.data (user-visible trace), not just
_fullLayout.

Evidence that fits this reading:

  1. Restyling thickness together with thicknessmode
    ({'marker.colorbar.thickness': 20, 'marker.colorbar.thicknessmode': 'pixels'}) works
    fine — an explicit value bypasses the buggy default path.
  2. After Path B, the wrong value is observable on the user data object
    (gd.data[1].marker.colorbar.thickness === 16290), so a defaults-coercion result leaks
    back into gd.data.

First reported at plotly/plotly.py#5615;
the plotly.py maintainers asked for a pure-JS reproduction here.

Workaround

Include an explicit thickness in the same restyle call:

Plotly.restyle(gd, {
  'marker.colorbar.thicknessmode': 'pixels',
  'marker.colorbar.thickness': 20
}, [1]);

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

首先使用 Plotly.newPlot 和 Plotly.restyle 运行提供的 index.html 复现,然后跟踪 marker.colorbar.thicknessmode 对 splom 轨迹的处理方式。完成的标准是通过 Plotly.restyle 应用该模式时不再写入 thickness: 16290 或导致面板折叠,同时 newPlot 路径仍然正确。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript
领域
data-visualization, frontend
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
活跃
描述清晰度
基本清楚
新手友好度
68/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。