d3js 实现水球图

在这里插入图片描述

d3js 源码地址:: http://bl.ocks.org/brattonc/5e5ce9beee483220e2f6

官方提供的代码不能直接用,版本3.x 这是调整后v5能使用的。

<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <meta http-equiv='X-UA-Compatible' content='ie=edge'>
  <title>7</title>
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <link rel="stylesheet" type="text/css" href="/css.css" />
</head>

<body>
  <svg id="svg" width="300" height="300" onclick="javascript:gauge.update(NewValue())"></svg>
  <span>点击SVG更新数据</span>
 </body>
<script type='text/javascript'>

  var gauge = loadLiquidFillGauge("svg", 55, {
    circleColor: "#FF7777",
    textColor: "#FF4444",
    waveTextColor: "#FFAAAA",
    waveColor: "#FFDDDD",
    circleThickness: 0.02,
    circleFillGap: 0,
    textVertPosition: 0.2,
    waveAnimateTime: 1000,
    waveRiseTime: 3000
  });

  function NewValue() {
    if (Math.random() > .5) {
      return Math.round(Math.random() * 100);
    }
    return (Math.random() * 100).toFixed(1);
  }

  function loadLiquidFillGauge(elementId, value, config) {

    config = Object.assign({}, {
      minValue: 0, // 量规最小值.
      maxValue: 100, // 量规最大值.
      circleThickness: 0.05, // 外圆的厚度与半径的百分比.
      circleFillGap: 0.05, // 外圆与波圆之间的间隙大小与外圆半径的百分比.
      circleColor: "#178BCA", // 外圆的颜色.
      waveHeight: 0.05, // 波高与波圆半径的百分比.
      waveCount: 2, // 每波圈宽度的全波数.
      waveRiseTime: 1000, // 波从0上升到最终高度所需的时间,以毫秒为单位.
      waveAnimateTime: 18000, //  一个完整的波进入波圈的时间(以毫秒为单位).
      waveRise: true, // 控制波浪是否应该从0上升到它的全部高度,或从它的全部高度开始
      waveHeightScaling: true, // 控制波大小缩放在低和高填充百分比。当真值时,波高在50%填充时达到最大值,在0%和100%填充时达到最小值。这有助于防止波使波圈出现完全满或空时,接近它的最小或最大填充。
      waveAnimate: true, // 控制波形是滚动还是静态.
      waveColor: "#178BCA", // 填充波的颜色。
      waveOffset: 0, // 最初抵消波的量。0 = 无偏移。1 = 一个完整波的偏移量
      textVertPosition: .5, // 在波圈内显示百分比文本的高度。0 =下,1 =上.
      textSize: 1, // 要在波圈中显示的文本的相对高度。1 = 50%
      valueCountUp: true, // 如果为真,则显示的值从0计数到加载时的最终值。如果为false,则显示最终值。
      displayPercent: true, // 如果为真,值后面会显示%符号。
      textColor: "#045681", // 当波浪不重叠值文本时的颜色。
      waveTextColor: "#A4DBf8" // 当波浪与值文本重叠时的颜色.
    }, config);

    var gauge = d3.select("#" + elementId);
    var radius = Math.min(parseInt(gauge.style("width")), parseInt(gauge.style("height"))) / 2;
    var locationX = parseInt(gauge.style("width")) / 2 - radius;
    var locationY = parseInt(gauge.style("height")) / 2 - radius;
    var fillPercent = Math.max(config.minValue, Math.min(config.maxValue, value)) / config.maxValue;

    var waveHeightScale;
    if (config.waveHeightScaling) {
      waveHeightScale = d3.scaleLinear().range([0, config.waveHeight, 0]).domain([0, 50, 100]);
    } else {
      waveHeightScale = d3.scaleLinear().range([config.waveHeight, config.waveHeight]).domain([0, 100]);
    }

    var textPixels = (config.textSize * radius / 2);
    var textFinalValue = parseFloat(value).toFixed(2);
    var textStartValue = config.valueCountUp ? config.minValue : textFinalValue;
    var percentText = config.displayPercent ? "%" : "";
    var circleThickness = config.circleThickness * radius;
    var circleFillGap = config.circleFillGap * radius;
    var fillCircleMargin = circleThickness + circleFillGap;
    var fillCircleRadius = radius - fillCircleMargin;
    var waveHeight = fillCircleRadius * waveHeightScale(fillPercent * 100);

    var waveLength = fillCircleRadius * 2 / config.waveCount;
    var waveClipCount = 1 + config.waveCount;
    var waveClipWidth = waveLength * waveClipCount;

    // 四舍五入函数,以便在数值累计时始终显示正确的小数点后位数。
    var textRounder = (value) => Math.round(value);

    if (parseFloat(textFinalValue) != parseFloat(textRounder(textFinalValue))) {
      textRounder = (value) => parseFloat(value).toFixed(1);
    }
    if (parseFloat(textFinalValue) != parseFloat(textRounder(textFinalValue))) {
      textRounder = (value) => parseFloat(value).toFixed(2);
    }

    // 建立clip wave区域的数据。
    var data = Array.from({ length: 41 * waveClipCount }, (_, i) => ({ x: i / (40 * waveClipCount), y: (i / (40)) }));

    // 绘制外圆的比例。
    var gaugeCircleX = d3.scaleLinear().range([0, 2 * Math.PI]).domain([0, 1]);
    var gaugeCircleY = d3.scaleLinear().range([0, radius]).domain([0, radius]);

    // 用于控制剪切路径大小的尺度。
    var waveScaleX = d3.scaleLinear().range([0, waveClipWidth]).domain([0, 1]);
    var waveScaleY = d3.scaleLinear().range([0, waveHeight]).domain([0, 1]);

    // 用于控制裁剪路径位置的刻度。
    var waveRiseScale = d3.scaleLinear()
      // 剪辑区域的大小是填充圆的高度+波的高度,所以我们定位剪辑波这样,它将重叠填充圆在所有0%时,并将完全覆盖填充圆在100%。
      .range([(fillCircleMargin + fillCircleRadius * 2 + waveHeight), (fillCircleMargin - waveHeight)]).domain([0, 1]);

    var waveAnimateScale = d3.scaleLinear()
      .range([0, waveClipWidth - fillCircleRadius * 2]) // 将夹子区域推一个完整的波,然后弹回来。
      .domain([0, 1]);

    // 用于控制文本在量规内的位置的刻度。
    var textRiseScaleY = d3.scaleLinear()
      .range([fillCircleMargin + fillCircleRadius * 2, (fillCircleMargin + textPixels * 0.7)])
      .domain([0, 1]);

    // 在父SVG中居中测量。
    var gaugeGroup = gauge.append("g").attr('transform', `translate(${locationX},${locationY})`);


    // 画一个外圆。
    var gaugeCircleArc = d3.arc()({
      startAngle: gaugeCircleX(0),
      endAngle: gaugeCircleX(1),
      outerRadius: gaugeCircleY(radius),
      innerRadius: gaugeCircleY(radius - circleThickness),
    })
    gaugeGroup.append("path")
      .attr("d", gaugeCircleArc)
      .style("fill", config.circleColor)
      .attr('transform', `translate(${radius},${radius})`);

    // 波不重叠的文本。
    var text1 = gaugeGroup.append("text")
      .text(textRounder(textStartValue) + percentText)
      .attr("class", "liquidFillGaugeText")
      .attr("text-anchor", "middle")
      .attr("font-size", textPixels + "px")
      .style("fill", config.textColor)
      .attr('transform', `translate(${radius},${textRiseScaleY(config.textVertPosition)})`);

    // 剪切波面积。
    var clipArea = d3.area()
      .x(d => waveScaleX(d.x))
      .y0(d => waveScaleY(Math.sin(Math.PI * 2 * config.waveOffset * -1 + Math.PI * 2 * (1 - config.waveCount) + d.y * 2 * Math.PI)))
      .y1(d => (fillCircleRadius * 2 + waveHeight));

    var waveGroup = gaugeGroup.append("defs").append("clipPath").attr("id", "clipWave" + elementId);

    var wave = waveGroup.append("path")
      .datum(data)
      .attr("d", clipArea)
      .attr("T", 0);

    // 带有剪切波的内圆。
    var fillCircleGroup = gaugeGroup.append("g").attr("clip-path", "url(#clipWave" + elementId + ")");

    fillCircleGroup.append("circle")
      .attr("cx", radius)
      .attr("cy", radius)
      .attr("r", fillCircleRadius)
      .style("fill", config.waveColor);

    // 波重叠的文本。
    var text2 = fillCircleGroup.append("text")
      .text(textRounder(textStartValue) + percentText)
      .attr("class", "liquidFillGaugeText")
      .attr("text-anchor", "middle")
      .attr("font-size", textPixels + "px")
      .style("fill", config.waveTextColor)
      .attr('transform', `translate(${radius},${textRiseScaleY(config.textVertPosition)})`);

    // 把数值加起来。
    if (config.valueCountUp) {
      var textTween = function () {
        var i = d3.interpolate(this.textContent, textFinalValue);
        return function (t) {
          this.textContent = textRounder(i(t)) + percentText;
        }
      };
      text1.transition().duration(config.waveRiseTime).tween("text", textTween);
      text2.transition().duration(config.waveRiseTime).tween("text", textTween);
    }

    // 让波浪上升。波和波组是分开的,水平和垂直运动可以独立控制。
    var waveGroupXPosition = fillCircleMargin + fillCircleRadius * 2 - waveClipWidth;

    if (config.waveRise) {
      waveGroup.attr('transform', `translate(${waveGroupXPosition},${waveRiseScale(0)})`)
        .transition()
        .duration(config.waveRiseTime)
        .ease(d3.easeLinear)
        .attr('transform', `translate(${waveGroupXPosition},${waveRiseScale(fillPercent)})`)
        .on("start", function () {// 当wave Rise = true和 wave Animate=false时,这个变换是必要的,以使剪辑波正确定位。如果没有这个,波将无法正确定位,但我们不清楚为什么这是必要的.
          wave.attr('transform', 'translate(1,0)');
        });
    } else {
      waveGroup.attr('transform', `translate(${waveGroupXPosition},${waveRiseScale(fillPercent)}`);
    }

    if (config.waveAnimate) animateWave();

    function animateWave() {
      wave.attr('transform', `translate(${waveAnimateScale(wave.attr('T'))},0)`);
      wave.transition()
        .duration(config.waveAnimateTime * (1 - wave.attr('T')))
        .ease(d3.easeLinear)
        .attr("transform", `translate(${waveAnimateScale(1)},0)`)
        .attr('T', 1)
        .on('end', function () {
          wave.attr('T', 0);
          animateWave();
        });
    }

    function GaugeUpdater() {
      this.update = function (value) {
        var newFinalValue = parseFloat(value).toFixed(2);
        var textRounderUpdater = (value) => Math.round(value);
        if (parseFloat(newFinalValue) != parseFloat(textRounderUpdater(newFinalValue))) {
          textRounderUpdater = (value) => parseFloat(value).toFixed(1);
        }
        if (parseFloat(newFinalValue) != parseFloat(textRounderUpdater(newFinalValue))) {
          textRounderUpdater = (value) => parseFloat(value).toFixed(2);
        }

        var textTween = function () {
          var interpolate = d3.interpolate(this.textContent, parseFloat(value).toFixed(2));
          return function (t) {
            this.textContent = textRounderUpdater(interpolate(t)) + percentText;
          }
        };

        text1.transition().duration(config.waveRiseTime).tween("text", textTween);
        text2.transition().duration(config.waveRiseTime).tween("text", textTween);

        var fillPercent = Math.max(config.minValue, Math.min(config.maxValue, value)) / config.maxValue;
        var waveHeight = fillCircleRadius * waveHeightScale(fillPercent * 100);
        var waveRiseScale = d3.scaleLinear()
          // 剪辑区域的大小是填充圆的高度+波的高度,所以我们定位剪辑波这样,它将重叠填充圆在所有0%时,并将完全覆盖填充圆在100%.
          .range([(fillCircleMargin + fillCircleRadius * 2 + waveHeight), (fillCircleMargin - waveHeight)])
          .domain([0, 1]);
        var newHeight = waveRiseScale(fillPercent);
        var waveScaleX = d3.scaleLinear().range([0, waveClipWidth]).domain([0, 1]);
        var waveScaleY = d3.scaleLinear().range([0, waveHeight]).domain([0, 1]);
        var newClipArea;
        if (config.waveHeightScaling) {
          newClipArea = d3.area()
            .x(d => waveScaleX(d.x))
            .y0(d => waveScaleY(Math.sin(Math.PI * 2 * config.waveOffset * -1 + Math.PI * 2 * (1 - config.waveCount) + d.y * 2 * Math.PI)))
            .y1(d => (fillCircleRadius * 2 + waveHeight));
        } else {
          newClipArea = clipArea;
        }

        var newWavePosition = config.waveAnimate ? waveAnimateScale(1) : 0;
        wave.transition()
          .duration(0)
          .transition()
          .duration(config.waveAnimate ? (config.waveAnimateTime * (1 - wave.attr('T'))) : (config.waveRiseTime))
          .ease(d3.easeCircle)
          .attr('d', newClipArea)
          .attr('transform', `translate(${newWavePosition},0)`)
          .attr('T', '1')
          .on("end", function () {
            if (config.waveAnimate) {
              wave.attr('transform', `translate(${waveAnimateScale(0)},0)`)
              animateWave();
            }
          });
        waveGroup.transition()
          .duration(config.waveRiseTime)
          .attr('transform', `translate(${waveGroupXPosition},${newHeight})`)
      }
    }

    return new GaugeUpdater();
  }
</script>

</html>

组件版本

<template>
  <div style="width: 100%;height: 300px;" class="p-30"></div>
</template>

<script>
import * as d3 from "d3";

export default {
  name: 'loadLiquidFillGauge',
  data() {
    return {
      instance: null
    }
  },
  props: {
    step: { type: [Number, String], default: 0.01 },
  },

  mounted() {

    this.instance = this.loadLiquidFillGauge(this.$el, this.step, {
      circleThickness: 0.05, // 外圆的厚度与半径的百分比.
      circleFillGap: 0.05, // 外圆与波圆之间的间隙大小与外圆半径的百分比.
      circleColor: "#409EFF", // 外圆的颜色.
      waveHeight: 0.05, // 波高与波圆半径的百分比.
      waveCount: 2, // 每波圈宽度的全波数.
      waveRiseTime: 2000, // 波从0上升到最终高度所需的时间,以毫秒为单位.
      waveAnimateTime: 1000, //  一个完整的波进入波圈的时间(以毫秒为单位).
      waveColor: "#409EFF", // 填充波的颜色。
      displayPercent: "ms", // 如果为真,值后面会显示%符号。
      textColor: "#045681", // 当波浪不重叠值文本时的颜色。
      waveTextColor: "#fff", // 当波浪与值文本重叠时的颜色.
    });
  },

  methods: {
    loadLiquidFillGauge(element, value, config) {
      config = Object.assign({}, {
        minValue: 0, // 量规最小值.
        maxValue: 100, // 量规最大值.
        circleThickness: 0.05, // 外圆的厚度与半径的百分比.
        circleFillGap: 0.05, // 外圆与波圆之间的间隙大小与外圆半径的百分比.
        circleColor: "#178BCA", // 外圆的颜色.
        waveHeight: 0.05, // 波高与波圆半径的百分比.
        waveCount: 2, // 每波圈宽度的全波数.
        waveRiseTime: 1000, // 波从0上升到最终高度所需的时间,以毫秒为单位.
        waveAnimateTime: 18000, //  一个完整的波进入波圈的时间(以毫秒为单位).
        waveRise: true, // 控制波浪是否应该从0上升到它的全部高度,或从它的全部高度开始
        waveHeightScaling: true, // 控制波大小缩放在低和高填充百分比。当真值时,波高在50%填充时达到最大值,在0%和100%填充时达到最小值。这有助于防止波使波圈出现完全满或空时,接近它的最小或最大填充。
        waveAnimate: true, // 控制波形是滚动还是静态.
        waveColor: "#178BCA", // 填充波的颜色。
        waveOffset: 0, // 最初抵消波的量。0 = 无偏移。1 = 一个完整波的偏移量
        textVertPosition: .5, // 在波圈内显示百分比文本的高度。0 =下,1 =上.
        textSize: .8, // 要在波圈中显示的文本的相对高度。1 = 50%
        valueCountUp: true, // 如果为真,则显示的值从0计数到加载时的最终值。如果为false,则显示最终值。
        displayPercent: "%", // 后面会显示%符号。
        textColor: "#045681", // 当波浪不重叠值文本时的颜色。
        waveTextColor: "#A4DBf8" // 当波浪与值文本重叠时的颜色.
      }, config);

      var gauge = d3.select(element).append('svg').attr('width', '100%').attr('height', '100%');
      var radius = Math.min(parseInt(gauge.style("width")), parseInt(gauge.style("height"))) / 2;
      var locationX = parseInt(gauge.style("width")) / 2 - radius;
      var locationY = parseInt(gauge.style("height")) / 2 - radius;
      var fillPercent = Math.max(config.minValue, Math.min(config.maxValue, value)) / config.maxValue;

      var waveHeightScale;
      if (config.waveHeightScaling) {
        waveHeightScale = d3.scaleLinear().range([0, config.waveHeight, 0]).domain([0, 50, 100]);
      } else {
        waveHeightScale = d3.scaleLinear().range([config.waveHeight, config.waveHeight]).domain([0, 100]);
      }

      var textPixels = (config.textSize * radius / 3);
      var textFinalValue = parseFloat(value).toFixed(2);
      var textStartValue = config.valueCountUp ? config.minValue : textFinalValue;
      var percentText = config.displayPercent;
      var circleThickness = config.circleThickness * radius;
      var circleFillGap = config.circleFillGap * radius;
      var fillCircleMargin = circleThickness + circleFillGap;
      var fillCircleRadius = radius - fillCircleMargin;
      var waveHeight = fillCircleRadius * waveHeightScale(fillPercent * 100);

      var waveLength = fillCircleRadius * 2 / config.waveCount;
      var waveClipCount = 1 + config.waveCount;
      var waveClipWidth = waveLength * waveClipCount;

      // 四舍五入函数,以便在数值累计时始终显示正确的小数点后位数。
      var textRounder = (value) => Math.round(value);

      if (parseFloat(textFinalValue) != parseFloat(textRounder(textFinalValue))) {
        textRounder = (value) => parseFloat(value).toFixed(1);
      }
      if (parseFloat(textFinalValue) != parseFloat(textRounder(textFinalValue))) {
        textRounder = (value) => parseFloat(value).toFixed(2);
      }

      // 建立clip wave区域的数据。
      var data = Array.from({ length: 41 * waveClipCount }, (_, i) => ({ x: i / (40 * waveClipCount), y: (i / (40)) }));

      // 绘制外圆的比例。
      var gaugeCircleX = d3.scaleLinear().range([0, 2 * Math.PI]).domain([0, 1]);
      var gaugeCircleY = d3.scaleLinear().range([0, radius]).domain([0, radius]);

      // 用于控制剪切路径大小的尺度。
      var waveScaleX = d3.scaleLinear().range([0, waveClipWidth]).domain([0, 1]);
      var waveScaleY = d3.scaleLinear().range([0, waveHeight]).domain([0, 1]);

      // 用于控制裁剪路径位置的刻度。
      var waveRiseScale = d3.scaleLinear()
        // 剪辑区域的大小是填充圆的高度+波的高度,所以我们定位剪辑波这样,它将重叠填充圆在所有0%时,并将完全覆盖填充圆在100%。
        .range([(fillCircleMargin + fillCircleRadius * 2 + waveHeight), (fillCircleMargin - waveHeight)]).domain([0, 1]);

      var waveAnimateScale = d3.scaleLinear()
        .range([0, waveClipWidth - fillCircleRadius * 2]) // 将夹子区域推一个完整的波,然后弹回来。
        .domain([0, 1]);

      // 用于控制文本在量规内的位置的刻度。
      var textRiseScaleY = d3.scaleLinear()
        .range([fillCircleMargin + fillCircleRadius * 2, (fillCircleMargin + textPixels * 0.7)])
        .domain([0, 1]);

      // 在父SVG中居中测量。
      var gaugeGroup = gauge.append("g").attr('transform', `translate(${locationX},${locationY})`);


      // 画一个外圆。
      var gaugeCircleArc = d3.arc()({
        startAngle: gaugeCircleX(0),
        endAngle: gaugeCircleX(1),
        outerRadius: gaugeCircleY(radius),
        innerRadius: gaugeCircleY(radius - circleThickness),
      })
      gaugeGroup.append("path")
        .attr("d", gaugeCircleArc)
        .style("fill", config.circleColor)
        .attr('transform', `translate(${radius},${radius})`);

      // 波不重叠的文本。
      var text1 = gaugeGroup.append("text")
        .text(textRounder(textStartValue) + percentText)
        .attr("class", "liquidFillGaugeText")
        .attr("text-anchor", "middle")
        .attr("font-size", textPixels + "px")
        .style("fill", config.textColor)
        .attr('transform', `translate(${radius},${textRiseScaleY(config.textVertPosition)})`);

      // 剪切波面积。
      var clipArea = d3.area()
        .x(d => waveScaleX(d.x))
        .y0(d => waveScaleY(Math.sin(Math.PI * 2 * config.waveOffset * -1 + Math.PI * 2 * (1 - config.waveCount) + d.y * 2 * Math.PI)))
        .y1(d => (fillCircleRadius * 2 + waveHeight));

      var waveGroup = gaugeGroup.append("defs").append("clipPath").attr("id", "clipWave" + 'svg');

      var wave = waveGroup.append("path")
        .datum(data)
        .attr("d", clipArea)
        .attr("T", 0);

      // 带有剪切波的内圆。
      var fillCircleGroup = gaugeGroup.append("g").attr("clip-path", "url(#clipWave" + 'svg' + ")");

      fillCircleGroup.append("circle")
        .attr("cx", radius)
        .attr("cy", radius)
        .attr("r", fillCircleRadius)
        .style("fill", config.waveColor);

      // 波重叠的文本。
      var text2 = fillCircleGroup.append("text")
        .text(textRounder(textStartValue) + percentText)
        .attr("class", "liquidFillGaugeText")
        .attr("text-anchor", "middle")
        .attr("font-size", textPixels + "px")
        .style("fill", config.waveTextColor)
        .attr('transform', `translate(${radius},${textRiseScaleY(config.textVertPosition)})`);

      // 把数值加起来。
      if (config.valueCountUp) {
        var textTween = function () {
          var i = d3.interpolate(this.textContent, textFinalValue);
          return function (t) {
            this.textContent = textRounder(i(t)) + percentText;
          }
        };
        text1.transition().duration(config.waveRiseTime).tween("text", textTween);
        text2.transition().duration(config.waveRiseTime).tween("text", textTween);
      }

      // 让波浪上升。波和波组是分开的,水平和垂直运动可以独立控制。
      var waveGroupXPosition = fillCircleMargin + fillCircleRadius * 2 - waveClipWidth;

      if (config.waveRise) {
        waveGroup.attr('transform', `translate(${waveGroupXPosition},${waveRiseScale(0)})`)
          .transition()
          .duration(config.waveRiseTime)
          .ease(d3.easeLinear)
          .attr('transform', `translate(${waveGroupXPosition},${waveRiseScale(fillPercent)})`)
          .on("start", function () {// 当wave Rise = true和 wave Animate=false时,这个变换是必要的,以使剪辑波正确定位。如果没有这个,波将无法正确定位,但我们不清楚为什么这是必要的.
            wave.attr('transform', 'translate(1,0)');
          });
      } else {
        waveGroup.attr('transform', `translate(${waveGroupXPosition},${waveRiseScale(fillPercent)}`);
      }

      if (config.waveAnimate) animateWave();

      function animateWave() {
        wave.attr('transform', `translate(${waveAnimateScale(wave.attr('T'))},0)`);
        wave.transition()
          .duration(config.waveAnimateTime * (1 - wave.attr('T')))
          .ease(d3.easeLinear)
          .attr("transform", `translate(${waveAnimateScale(1)},0)`)
          .attr('T', 1)
          .on('end', function () {
            wave.attr('T', 0);
            animateWave();
          });
      }

      function GaugeUpdater() {
        this.update = function (value) {
          var newFinalValue = parseFloat(value).toFixed(2);
          var textRounderUpdater = (value) => Math.round(value);
          if (parseFloat(newFinalValue) != parseFloat(textRounderUpdater(newFinalValue))) {
            textRounderUpdater = (value) => parseFloat(value).toFixed(1);
          }
          if (parseFloat(newFinalValue) != parseFloat(textRounderUpdater(newFinalValue))) {
            textRounderUpdater = (value) => parseFloat(value).toFixed(2);
          }

          var textTween = function () {
            var interpolate = d3.interpolate(this.textContent, parseFloat(value).toFixed(2));
            return function (t) {
              this.textContent = textRounderUpdater(interpolate(t)) + percentText;
            }
          };

          text1.transition().duration(config.waveRiseTime).tween("text", textTween);
          text2.transition().duration(config.waveRiseTime).tween("text", textTween);

          var _max = Math.max(config.minValue, Math.min(config.maxValue, value));

          var fillPercent = (value <= config.maxValue ? _max : 85) / config.maxValue;
          var waveHeight = fillCircleRadius * waveHeightScale(fillPercent * 100);
          var waveRiseScale = d3.scaleLinear()
            // 剪辑区域的大小是填充圆的高度+波的高度,所以我们定位剪辑波这样,它将重叠填充圆在所有0%时,并将完全覆盖填充圆在100%.
            .range([(fillCircleMargin + fillCircleRadius * 2 + waveHeight), (fillCircleMargin - waveHeight)])
            .domain([0, 1]);
          var newHeight = waveRiseScale(fillPercent);
          var waveScaleX = d3.scaleLinear().range([0, waveClipWidth]).domain([0, 1]);
          var waveScaleY = d3.scaleLinear().range([0, waveHeight]).domain([0, 1]);
          var newClipArea;
          if (config.waveHeightScaling) {
            newClipArea = d3.area()
              .x(d => waveScaleX(d.x))
              .y0(d => waveScaleY(Math.sin(Math.PI * 2 * config.waveOffset * -1 + Math.PI * 2 * (1 - config.waveCount) + d.y * 2 * Math.PI)))
              .y1(d => (fillCircleRadius * 2 + waveHeight));
          } else {
            newClipArea = clipArea;
          }

          var newWavePosition = config.waveAnimate ? waveAnimateScale(1) : 0;
          wave.transition()
            .duration(0)
            .transition()
            .duration(config.waveAnimate ? (config.waveAnimateTime * (1 - wave.attr('T'))) : (config.waveRiseTime))
            .ease(d3.easeCircle)
            .attr('d', newClipArea)
            .attr('transform', `translate(${newWavePosition},0)`)
            .attr('T', '1')
            .on("end", function () {
              if (config.waveAnimate) {
                wave.attr('transform', `translate(${waveAnimateScale(0)},0)`)
                animateWave();
              }
            });
          waveGroup.transition()
            .duration(config.waveRiseTime)
            .attr('transform', `translate(${waveGroupXPosition},${newHeight})`)
        }
      }

      return new GaugeUpdater();
    },
    update(val) {
      this.instance.update(val);
    }
  }
  //  End
}

</script>



// 使用该组件
<load-liquid-fill-gauge ref="gauge" step="80" />
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3是一个流行的JavaScript框架,而D3.js是一个强大的JavaScript形库,用于创建各种类型的数据可视化。在Vue3中使用D3.js可以轻松地创建网络。 以下是使用Vue3和D3.js创建网络的步骤: 1. 首先,您需要在Vue3项目中安装D3.js。可以使用npm包管理器执行以下命令:npm install d3 2. 在Vue组件中导入D3.js:import * as d3 from 'd3' 3. 在组件的mounted生命周期钩子中,使用D3.js创建SVG容器并设置其大小和位置: ``` mounted() { const svg = d3.select('#network-chart') .append('svg') .attr('width', this.width) .attr('height', this.height) } ``` 4. 接下来,您需要将网络数据传递给Vue组件。可以通过props来实现这一点。 5. 使用D3.js创建节点和连线。这可以通过以下代码完成: ``` const node = svg.selectAll('.node') .data(nodes) .enter() .append('circle') .attr('class', 'node') .attr('r', this.nodeRadius) .attr('fill', 'blue') const link = svg.selectAll('.link') .data(links) .enter() .append('line') .attr('class', 'link') .attr('stroke', 'gray') ``` 6. 最后,在组件的updated生命周期钩子中更新节点和连线的位置: ``` updated() { const node = svg.selectAll('.node') .attr('cx', d => d.x) .attr('cy', d => d.y) const link = svg.selectAll('.link') .attr('x1', d => d.source.x) .attr('y1', d => d.source.y) .attr('x2', d => d.target.x) .attr('y2', d => d.target.y) } ``` 以上就是使用Vue3和D3.js实现网络的简单步骤,希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值