d3.js学习汇总

近期在做线路图实时刷新的功能,用到的技术主要有d3、svg、websocket。整体思路就是解析线路图json,使用d3动态生成svg矢量图;当出现报警信息时websocket主动向前端推送变化数据,前端监听到数据变化后进行局部刷新及动态展示。感觉d3比较像jq,可以操作dom,但是使用上又区别于jq。svg、d3也是从头开始学的,在这里对d3进行简单总结,有问题希望给予指正。

1、元素选择

select() -- 选择指定元素 例如:标签,也可以是某个具体的id等。但是只能获取到相匹配的第一个

selectAll() -- 选中所有元素  例如:所有p标签

const pageSvg = d3.select('#svgContent')  //获取#svgContent

const body = d3.select(body) // 获取dom中的body

const p = body.selectAll('p') // 获取body中所有的p标签                             

2、数据绑定

data() -- 将一个数组绑定到选择集上

datum() -- 将单个数据绑定到选择集上

ps:请保证dom中body下有四个标签

const dataList = [1,2,3,4]

d3.select("body").selectAll("p").data(this.dataList).text((d,i) => {

    return '第' + i + '个元素是' + d

})

const str = "112"

d3.select("body").selectAll("p").datum(str).text(function(d,i){

    return '第' + i + '个元素是' + d

})

3、理解update、enter、exit

简单理解为  enter() -- 不够就补     exit -- 多了就删

借用一张大神的图大家就很容易理解


update、enter: data会在依次添加到选择集中,当data数据中个数大于选择集个数时,调用enter().append('p') 会自动补齐p标签

const p = d3.select("#svgContent").selectAll('p');

        const update = p.data(this.dataList);

        const enter = update.enter();

        update.text(function(d,i){

            return i + '--------------' + d

        })

        const pEnter = enter.append('p');  //可以根据数量自动添加p

        pEnter.text(function(d,i){

            return i + '-------------' + d

        })

update、exit: data会在依次添加到选择集中,当出现选择集个数大于data数据个数时,自动删除多余的dom标签,相当于remove()

const p = d3.select("#svgContent").selectAll('p');

        const update = p.data(this.dataList);

        const exit = update.exit();

        update.text(function(d,i){

            return i + '--------------' + d

        })

        exit.text(function(d,i){

            return i + '-------------' + d

        })

4、插入、删除元素

在选择集之后插入元素  append()

d3.select("#svgContent").append('div').style('width','200px').style('height','200px').style('background','red')

在选择集之前插入元素 insert()

d3.select("#svgContent").insert('p','.test').text('ceshi ')

删除选中元素 remove()

d3.select("#svgContent").select('p','.test').remove()

5、比例尺

比例尺就是把输入域映射到输出域的函数。比如将0-10映射到0-100。 其中,domain为输入域,range为输出域,即将domain中的数据映射成range中的数据。

这里主要介绍下线性比例尺scaleLinear() 和 序数比例尺scaleOrdinal()

const datasets = [1.1,0.9,3,2.4,3.1]

   const svg = d3.select('#svgContent') //获取画布

          const g = svg.append('g') // 定义装图形的位置,并修改其位置

          .attr('transform','translate('+ this.marge.top +','+ this.marge.left +')')

          const min = d3.min(datasets);

          const max = d3.max(datasets);

          const scaleLinear = d3.scaleLinear().domain([min,max]).range([0,10])  //定义比例尺,将最小值和最大值映射成0-10

          const rectHeight = 30;

          g.selectAll('rect')

          .data(this.dataset)

          .enter()

          .append('rect')

          .attr('x',20)

          .attr('y',function(d,i){

              return i * rectHeight

          })

          .attr('width',function(d){

              return scaleLinear(d)  // 使用比例尺

          })

          .attr('height',rectHeight - 5)

          .attr('fill','red')

const index = [0,1,2,3,4]

const color = ["red","blue","yellow","black","green"]

const scaleOrdinal = d3.scaleOrdinal()

    .domain(index)

    .range(color);

scaleOrdinal(0)  // red

scaleOrdinal(1)  // blue

6、svg+d3绘制柱状图

实现思路:

(1) 获取svg画布,并获取宽度和高度

(2) 定义一个组,用于绘制图形,并设置组的位置。设置组用于整体的放大、缩小、平移等操作

(3) 定义x坐标轴并绘制

// 定义x坐标轴

      const xScale =

      d3.scaleBand()

    .domain(d3.range(this.dataset.length))

    .rangeRound([0,width-this.marge.left-this.marge.right]);

      const xAxis = d3.axisBottom(xScale)

      g.append('g')

      .attr('transform','translate('+0+','+ (height- top1 - bottom1)+')')

(4) 定义y坐标轴并绘制

// 定义y坐标轴

      const yScale =  d3.scaleLinear()

    .domain([0,d3.max(this.dataset)])

    .range([height-this.marge.top-this.marge.bottom,0]);

      const yAxis = d3.axisLeft(yScale)

      g.append('g').attr('transform','translate(0,0)').call(yAxis)

(5) 为每个矩形和文字设置分组

(6) 设置矩形间距

(7) 绘制矩形

(8) 绘制文本

以下案例在vue中写的

// 获取svg画布

      const svg = d3.select('#svgContent')

      // 获取svg的宽度和高度

      const width = svg.attr('width')

      const height = svg.attr('height')

      // 定义图形展示区域,并设置位置

      const g = svg.append('g')

      .attr('transform','translate('+ this.marge.top +','+ this.marge.left +')')

      // 定义x坐标轴

      const xScale =

      d3.scaleBand()

    .domain(d3.range(this.dataset.length))

    .rangeRound([0,width-this.marge.left-this.marge.right]);

      //d3.scaleBand().domain(d3.range(this.dataset.length)).rangeRound([0,width-this.marge.left - this.marge.right])

      const xAxis = d3.axisBottom(xScale)

      // 定义y坐标轴

      const yScale =  d3.scaleLinear()

    .domain([0,d3.max(this.dataset)])

    .range([height-this.marge.top-this.marge.bottom,0]);

      //d3.scaleLinear().domain([0,d3.max(this.dataset)]).range([height-this.marge.top - this.marge.bottom,0])

      const yAxis = d3.axisLeft(yScale)

            const top1 = this.marge.top

      const bottom1 = this.marge.bottom

      g.append('g')

      .attr('transform','translate('+0+','+ (height- top1 - bottom1)+')')

      .call(xAxis)

      g.append('g').attr('transform','translate(0,0)').call(yAxis)

      // 为每个矩形和文字设置分组

      const gs = g.selectAll('.rect').data(this.dataset).enter().append('g')

      //设置矩形间距

      const rectPadding = 20;

      //绘制矩形

      gs.append('rect')

      .attr('x',function(d, i){     

        return  xScale(i) + rectPadding/2;

      })

      .attr('y',function(d,i){

        return yScale(d)

      })

      .attr('width',function(){

        return xScale.step() - rectPadding

      })

      .attr('height',function(d,i){

        return height - top1- bottom1 - yScale(d)

      })

      .attr('fill','blue')

      //绘制文字

      gs.append('text')

      .attr('x',function(d,i){

        return xScale(i);

      })

      .attr('y',function(d,i){

        return yScale(d) - 28

      })

      .attr('dx',function(){

        return (xScale.step()-rectPadding)/2;

      })

      .attr('dy',function(d,i){

        return 20

      })

      .text(function(d){

        return d

      })

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353