【问题标题】:Spline path - 3D effect样条路径 - 3D 效果
【发布时间】:2021-04-17 00:17:08
【问题描述】:

我正在尝试使用 d3、svg 和 css 为样条图实现 the desired result(左),以使其具有“3d”效果。用偏移量覆盖两条样条线会产生接近的结果,但是通过某些数据或缩放,您可以看到底部的“阴影”(右)由于插值而没有跟随主样条线。

我尝试过使用渐变,但它会应用于整个 svg 区域,而不仅仅是水平地应用于路径。代码 sn-p 遵循 d3 的示例 (https://bl.ocks.org/gordlea/27370d1eea8464b04538e6d8ced39e89)。

    // 2. Use the margin convention practice
    var margin = { top: 50, right: 50, bottom: 50, left: 50 }
        , width = window.innerWidth - margin.left - margin.right // Use the window's width
        , height = window.innerHeight - margin.top - margin.bottom; // Use the window's height

    // The number of datapoints
    var n = 21;

    // 5. X scale will use the index of our data
    var xScale = d3.scaleLinear()
        .domain([0, n - 1]) // input
        .range([0, width]); // output

    // 6. Y scale will use the randomly generate number
    var yScale = d3.scaleLinear()
        .domain([0, 1]) // input
        .range([height, 0]); // output

    // 7. d3's line generator
    var line = d3.line()
        .x(function (d, i) { return xScale(i); }) // set the x values for the line generator
        .y(function (d) { return yScale(d.y); }) // set the y values for the line generator
        .curve(d3.curveBasisOpen) // apply smoothing to the line

    // 8. An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
    var dataset = d3.range(n).map(function (d) { return { "y": d3.randomUniform(1)() } })

    // 1. Add the SVG to the page and employ #2
    var svg = d3.select("body").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // 3. Call the x axis in a group tag
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(xScale)); // Create an axis component with d3.axisBottom

    // 4. Call the y axis in a group tag
    svg.append("g")
        .attr("class", "y axis")
        .call(d3.axisLeft(yScale)); // Create an axis component with d3.axisLeft

    // 9. Append the path, bind the data, and call the line generator
    svg.append("path")
        .datum(dataset) // 10. Binds data to the line
        .attr("class", "line") // Assign a class for styling      
        .attr("stroke", "url(#gradID)")
        //.attr("stroke", "red")        
        .attr("d", line); // 11. Calls the line generator


    var defs = svg.append('defs');
    const linearGradient = defs.append("linearGradient");
        
    linearGradient
        .attr("id", "gradID")
        .attr("x1", "0%")
        .attr("y1", "0%")
        .attr("x2", "0%")
        .attr("y2", "100%");
    linearGradient
        .append("stop")
        .attr("offset", "0")
        .attr("style", "stop-color:blue;stop-opacity:1");
    linearGradient
        .append("stop")
        .attr("offset", "1")
        .attr("style", "stop-color:red;stop-opacity:1");
.line {
    fill: none;
    stroke-width: 10;
    stroke-linecap: round;
}
<!DOCTYPE html>
<meta charset="utf-8">

<body>
</body>

<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v5.min.js"></script>

【问题讨论】:

标签: javascript css svg d3.js


【解决方案1】:

感谢@grantD 提供的链接能够达到预期的效果:

更新代码sn-p:

  // 2. Use the margin convention practice
  var margin = { top: 50, right: 50, bottom: 50, left: 50 },
    width = window.innerWidth - margin.left - margin.right, // Use the window's width
    height = window.innerHeight - margin.top - margin.bottom; // Use the window's height

  // The number of datapoints
  var n = 10;

  // 5. X scale will use the index of our data
  var xScale = d3
    .scaleLinear()
    .domain([0, n - 1]) // input
    .range([0, width]); // output

  // 6. Y scale will use the randomly generate number
  var yScale = d3
    .scaleLinear()
    .domain([0, 1]) // input
    .range([height, 0]); // output

  // 7. d3's line generator
  var line = d3
    .line()
    .x(function (d, i) {
      return xScale(i);
    }) // set the x values for the line generator
    .y(function (d) {
      return yScale(d.y);
    }) // set the y values for the line generator
    .curve(d3.curveBasisOpen); // apply smoothing to the line

  // 8. An array of objects of length N. Each object has key -> value pair, the key being "y" and the value is a random number
  var dataset = d3.range(n).map(function (d) {
    return { y: d3.randomUniform(1)() };
  });

  // 1. Add the SVG to the page and employ #2
  var svg = d3
    .select("body")
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var defs = svg.append("defs");

  // create filter with id #drop-shadow
  // height=130% so that the shadow is not clipped
  var filter = defs.append("filter").attr("id", "inner-shadow");

  filter.append("feOffset").attr("dx", "0").attr("dy", "-4");
  filter
    .append("feGaussianBlur")
    .attr("stdDeviation", "0.2")
    .attr("result", "offset-blur");
  filter
    .append("feComposite")
    .attr("operator", "out")
    .attr("in", "SourceGraphic")
    .attr("in2", "offset-blur")
    .attr("result", "inverse");
  filter
    .append("feFlood")
    .attr("flood-color", "orange")
    .attr("flood-opacity", "1")
    .attr("result", "color");
  filter
    .append("feComposite")
    .attr("operator", "in")
    .attr("in", "color")
    .attr("in2", "inverse")
    .attr("result", "shadow");
  const comp = filter
    .append("feComponentTransfer")
    .attr("in", "shadow")
    .attr("result", "shadow");
  comp.append("feFuncA").attr("type", "linear").attr("slope", "100.10");
  filter
    .append("feComposite")
    .attr("operator", "over")
    .attr("in", "shadow")
    .attr("in2", "SourceGraphic");

  // 3. Call the x axis in a group tag
  svg
    .append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(xScale)); // Create an axis component with d3.axisBottom

  // 4. Call the y axis in a group tag
  svg.append("g").attr("class", "y axis").call(d3.axisLeft(yScale)); // Create an axis component with d3.axisLeft

  // 9. Append the path, bind the data, and call the line generator
  svg
    .append("path")
    .datum(dataset) // 10. Binds data to the line
    .attr("class", "line") // Assign a class for styling
    .attr("filter", "url(#inner-shadow)")    
    .attr("d", line); // 11. Calls the line generator

  var defs = svg.append("defs");

  var filter = defs.append("filter").attr("id", "dropshadow"); /// !!! important - define id to reference it later

  // append gaussian blur to filter
  filter
    .append("feGaussianBlur")
    .attr("in", "SourceAlpha")
    .attr("stdDeviation", 10)
    .attr("result", "blur");

  // append offset filter to result of gaussion blur filter
  filter
    .append("feOffset")
    .attr("in", "blur")
    .attr("dx", 20)
    .attr("dy", 30)
    .attr("result", "offsetBlur");

  // merge result with original image
  var feMerge = filter.append("feMerge");

  // first layer result of blur and offset
  feMerge.append("feMergeNode").attr('in", "offsetBlur');

  // original image on top
  feMerge.append("feMergeNode").attr("in", "SourceGraphic");
  
.line {
    fill: none;
    stroke: #ffb653;
    stroke-width: 10;
    stroke-linecap: round;
  }
<body></body>

<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v5.min.js"></script>

【讨论】:

    猜你喜欢
    • 2019-03-25
    • 1970-01-01
    • 2019-03-09
    • 2014-05-05
    • 1970-01-01
    • 1970-01-01
    • 2019-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多