1. 程式人生 > >D3.js的V5版本-Vue框架中使用(第九章) ---力導向圖

D3.js的V5版本-Vue框架中使用(第九章) ---力導向圖

一. Api使用

  • d3.forceSimulation() ,新建一個力導向圖,
  • d3.forceSimulation().force(),新增或者移除一個力
  • d3.forceSimulation().nodes(),輸入是一個數組,然後將這個輸入的陣列進行一定的資料轉換
  • d3.forceLink.links(),這裡輸入的也是一個數組(邊集),然後對輸入的邊集進行轉換
  • tick函式,這個函式對於力導向圖來說非常重要,因為力導向圖是不斷運動的,每一時刻都在發生更新,所以需要不斷更新節點和連線的位置
  • d3.drag(),是力導向圖可以被拖動   

二. vue使用

<template lang='pug'>
    div.force-pane(:id="id")
        svg
</template>
<script>
/**
 * 力導向圖
 */
import * as d3 from 'd3'
let gs = '',
    forceSimulation = '',
    links = '',
    linksText = ''
let nodes = [{ name: '湖南' }, { name: '毛澤東' }, { name: '主席' }]

let edges = [
    { source: 0, target: 1, relation: '籍貫', value: 1.3 },
    { source: 1, target: 2, relation: '職責', value: 1 }
]
export default {
    name: 'Scale',
    data() {
        return {
            id: ''
        }
    },
    methods: {
        uuid() {
            function s4() {
                return Math.floor((1 + Math.random()) * 0x10000)
                    .toString(16)
                    .substring(1)
            }
            return (
                s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4()
            )
        },
        ticked() {
            links
                .attr('x1', function(d) {
                    return d.source.x
                })
                .attr('y1', function(d) {
                    return d.source.y
                })
                .attr('x2', function(d) {
                    return d.target.x
                })
                .attr('y2', function(d) {
                    return d.target.y
                })

            linksText
                .attr('x', function(d) {
                    return (d.source.x + d.target.x) / 2
                })
                .attr('y', function(d) {
                    return (d.source.y + d.target.y) / 2
                })

            gs.attr('transform', d => {
                return 'translate(' + d.x + ',' + d.y + ')'
            })
        },
        dragStart(d) {
            if (!d3.event.active) {
                //設定衰減係數,對節點位置移動過程的模擬,數值越高移動越快,數值範圍[0,1]
                forceSimulation.alphaTarget(0.8).restart()
            }
            d.fx = d.x
            d.fy = d.y
        },
        dragEnd(d) {
            d.fx = d3.event.x
            d.fy = d3.event.y
        },
        drag(d) {
            if (!d3.event.active) {
                forceSimulation.alphaTarget(0)
            }
            d.fx = null
            d.fy = null
        }
    },
    created() {
        this.id = this.uuid()
    },
    mounted() {
        //1.建立svg畫布
        let marge = { top: 160, bottom: 60, left: 160, right: 60 }
        let width = document.getElementById(this.id).clientWidth
        let height = document.getElementById(this.id).clientHeight
        const svg = d3
            .select(this.$el)
            .select('svg')
            .attr('width', width)
            .attr('height', height)
        let g = svg
            .append('g')
            .attr(
                'transform',
                'translate(' + marge.top + ',' + marge.left + ')'
            )

        //2.設定一個color的顏色比例尺,為了讓不同的扇形呈現不同的顏色
        var colorScale = d3
            .scaleOrdinal()
            .domain(d3.range(nodes.length))
            .range(d3.schemeCategory10)

        //3.新建一個力導向圖
        forceSimulation = d3
            .forceSimulation()
            .force('link', d3.forceLink())
            .force('charge', d3.forceManyBody())
            .force('center', d3.forceCenter())

        //4. 初始化力導向圖
        //生成節點資料
        forceSimulation.nodes(nodes).on('tick', this.ticked)
        //生成邊資料
        forceSimulation
            .force('link')
            .links(edges)
            .distance(function(d) {
                //每一邊的長度
                return d.value * 100
            })
        //設定圖形的中心位置
        forceSimulation
            .force('center')
            .x(width / 4)
            .y(height / 4)

        //5. 繪製邊(有了節點和邊的資料後)
        links = g
            .append('g')
            .selectAll('line')
            .data(edges)
            .enter()
            .append('line')
            .attr('stroke', function(d, i) {
                return colorScale(i)
            })
            .attr('stroke-width', 1)
        linksText = g
            .append('g')
            .selectAll('text')
            .data(edges)
            .enter()
            .append('text')
            .text(function(d) {
                return d.relation
            })

        //6. 繪製節點, 先為節點和節點上的文字分組
        gs = g
            .selectAll('.circleText')
            .data(nodes)
            .enter()
            .append('g')
            .attr('transform', function(d, i) {
                var cirX = d.x
                var cirY = d.y
                return 'translate(' + cirX + ',' + cirY + ')'
            })
            .call(
                d3
                    .drag()
                    .on('start', this.dragStart)
                    .on('drag', this.drag)
                    .on('end', this.dragEnd)
            )

        //繪製節點
        gs.append('circle')
            .attr('r', 10)
            .attr('fill', function(d, i) {
                return colorScale(i)
            })
        //文字
        gs.append('text')
            .attr('x', -10)
            .attr('y', -20)
            .attr('dy', 10)
            .text(function(d) {
                return d.name
            })
    }
}
</script>
<style lang='scss' scoped>
.force-pane {
    width: 100%;
    height: 1000px;
}
</style>