【问题标题】:How can I build a d3.js linechart in Vue.js?如何在 Vue.js 中构建 d3.js 折线图?
【发布时间】:2021-03-25 16:44:21
【问题描述】:

我正在尝试了解如何在 Vue.js 项目中使用 d3.js 库构建折线图。

<template>
    <div ref="d3chart">
        <svg ref="chart" :width="width" :height="height" >
            <g transform="translate(50,50)">
                <g :transform="`translate(0,${svg_height})`" ref="XaxisContainer" />
                <g :transform="`translate(0,0)`" ref="YaxisContainer" />
                <g class="bars">
                    <rect v-for=" (d, index) in bars" :key="index" :x="d.x" :y="d.y" :height="d.height" :width="d.width"></rect>
                </g> 
            </g> 
        </svg>
    </div>
</template>
<script>
import * as d3 from 'd3';
import { mapState } from 'vuex';
import { mapGetters } from 'vuex';


export default {

props:{
    sensorId: Number,
},   

data(){
    return{
        g:null,
        svg: null,
        margin:200,
        width: 900,
        height: 800,
        svg_height: 600
    }
},
computed: {
    bars: function(){
       
        return this.dataset[this.sensorId].map(d => {
            return  {
                ...d,
                x: this.drawX(d),
                y: this.drawY(d),
                width:1,
                height: this.barHeight(d)
            }
        })
    },
    ...mapState(['dataset']),
    ...mapGetters({
        getData: 'getDataset'
    }),
    dataset: function (){
                //return this.$store.getters['getDataset'];
                return this.getData();
            },
},
mounted: function(){

    d3.select(this.$refs.XaxisContainer).call(d3.axisBottom(this.drawScale_x()).tickFormat(function (d) {
        return d+"Hz";
    }).ticks(30));


    d3.select(this.$refs.YaxisContainer)
            .call(d3.axisLeft(this.drawScale_y()).tickFormat(function (d) {
                return d+"g";
            }).ticks(20));

},
methods:{
        drawScale_x: function(){
                return d3.scaleLinear()
                    .domain([0,50]).nice()
                    .range([0,this.width - this.margin]);
        },
        drawScale_y: function(){
                return d3.scaleLinear()
                    .domain([0,500]).nice()
                    .range([this.height - this.margin,0]);
        },
        scale_x: function(temp){
                var scale_x =  d3.scaleLinear()
                    .domain([0,50]).nice()
                    .range([0,this.width - this.margin]);
                    return (scale_x(temp));
        },
        scale_y: function(temp){ 
                var scale_y = d3.scaleLinear()
                    .domain([0,500]).nice()
                    .range([this.height - this.margin,0]);
                return scale_y(temp)
        },
        drawX: function(d) { return this.scale_x(d.x); },
        drawY: function(d) { return this.scale_y(d.y); },
        barHeight: function(d) { return this.svg_height - this.scale_y(d.y); }
},
watch:{
    dataset: {        
        handler: function(){
        return this.dataset[this.sensorId].map(d => {
            return  {
                ...d,
                x: this.drawX(d),
                y: this.drawY(d),
                width:1,
                height: this.barHeight(d)
            }
        })
        },
        deep:true,
        
    }
}
}
</script>

<style>

    .bar {
        fill: steelblue;
    }
    .selectedbar {
        fill: orangered;
    }

</style>

首先,我尝试构建一个条形图,它可以工作,而且它还可以实时更新。但是我不明白如何画一条线而不是条形。我还尝试做一个 init() 方法并将所有内容放在该方法中,但问题是,如果我想要一个实时更新的图表,我需要拆分函数,以便可以单独调用方法来更新图表。

现在是图表:

这就是我想要的:

这是我用作骨架的条形图:

编辑:我创建了一个计算属性来观察存储在 Vuex 中的数据集数组的状态。然后我在计算属性上设置了一个观察者,但图表没有更新。我能做些什么? 谁能帮帮我?

【问题讨论】:

  • 看来您的问题不止一个。请专注于一个特定的问题并阐明您需要解决的问题。
  • 问题是如何在vue.js中构造一个d3.js折线图。我尝试使用条形图的骨架,但我被卡住了,因为我不知道如何绘制线条而不是条形。谢谢!
  • 您能否提供您获得的图片与所需图片的屏幕截图(您可以在 Paint 或类似工具中进行)以帮助我们了解您的需求
  • @MichaelRovinsky 我用所需信息更新了帖子。谢谢你的回答!
  • 你的问题不在于如何在 Vue 中用 d3 画线。这是如何用d3画一条线。时期。 this example 中的第 9 点是您的答案。在您的组件中,将 .bars 迭代替换为您的行的路径元素。 docs heresearch engine here。另请阅读this question 及其接受的答案。

标签: javascript vue.js d3.js charts vuetify.js


【解决方案1】:

这是 Vue.jsD3.js 折线图的示例

const data = [40,45,47,59,54,53,62,69,76,85,94,111,129,144,154,157,161,155,164,174];

const buildChart = ref => {
    const svg = d3.select(ref);
  const xScale = 
    d3.scaleLinear()
    .domain([0, 50])
    .range([0, 300]);
                  
 const xAxis = 
        d3.axisBottom()
    .scale(xScale);

svg.append("g")
    .attr('transform', 'translate(50,210)')
  .call(xAxis);                 

const yScale = 
    d3.scaleLinear()
    .domain([200, 0])
    .range([0, 200]);
                  
 const yAxis = 
        d3.axisLeft()
    .scale(yScale);

svg.append("g")
    .attr('transform', 'translate(50,10)')
  .call(yAxis);         
    
const path = data.reduce((p, v, i) => {
 const x = 300 / data.length * i;
 const y = yScale(v);
 if (i === 0) return `M ${x},${y}`;
 return p + ` L ${x},${y}`;
}, null)

svg.append('g')
    .attr('transform', 'translate(50, 10)')
  .append('path')
  .attr('d', path)
  .style('fill', 'none')
  .style('stroke', 'red')
  .style('stroke-width', 2)
}


Vue.component('d3-component', {
  mounted() {
    buildChart(this.$refs.svg)
  },
  template: '<svg ref="svg" width="400" height="250"></svg>'
})

new Vue({ el: '#d3-component-demo' })
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<div id="d3-component-demo">
  <d3-component/>
</div>

【讨论】:

  • 感谢您的回答!图表有效!如何在 y 轴上映射负值?有没有办法将购物车放在视图框中以使其响应?如果我想平滑线我该怎么办?我需要画一个正弦函数。谢谢!
【解决方案2】:
  1. 您可以将负值设置为带域的 X 轴:

    const xScale = d3.scaleLinear().domain([-50, 50])

  2. 我(个人)不喜欢使用 viewbox。相反,我将宽度和高度属性显式传递给 SVG,并在图表计算中使用它们

  3. 您可以通过 stroke-linejoin='round' 进行四舍五入,但并非在每种情况下都能顺利进行

const data = [40,45,47,59,54,53,62,69,76,85,94,111,129,144,154,157,161,155,164,174];

const buildChart = ref => {
    const svg = d3.select(ref);
  const xScale = 
    d3.scaleLinear()
    .domain([-50, 50])
    .range([0, 300]);
                  
 const xAxis = 
        d3.axisBottom()
    .scale(xScale);

svg.append("g")
    .attr('transform', 'translate(50,210)')
  .call(xAxis);                 

const yScale = 
    d3.scaleLinear()
    .domain([200, 0])
    .range([0, 200]);
                  
 const yAxis = 
        d3.axisLeft()
    .scale(yScale);

svg.append("g")
    .attr('transform', 'translate(50,10)')
  .call(yAxis);         
    
const path = data.reduce((p, v, i) => {
 const x = 300 / data.length * i;
 const y = yScale(v);
 if (i === 0) return `M ${x},${y}`;
 return p + ` L ${x},${y}`;
}, null)

svg.append('g')
    .attr('transform', 'translate(50, 10)')
  .append('path')
  .attr('d', path)
  .attr('stroke-linejoin', 'round')
  .style('fill', 'none')
  .style('stroke', 'red')
  .style('stroke-width', 5)
}


Vue.component('d3-component', {
  mounted() {
    buildChart(this.$refs.svg)
  },
  template: '<svg ref="svg" width="400" height="250"></svg>'
})

new Vue({ el: '#d3-component-demo' })
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<div id="d3-component-demo">
  <d3-component/>
</div>

【讨论】:

  • 非常感谢!可能我解决了这个问题。我使用 curve() 创建曲线而不是直线,然后将负值添加到域。我仍然有一个问题:如果我尝试调整窗口大小,图表没有响应。我能做什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多