D3.js(3) path/折线图

news2024/12/23 17:11:33

一、概念

path 元素是用来绘制各种形状(例如线条、曲线、弧形、圆弧等)的元素。path 元素的 d 属性用来定义绘制的路径。具体来说,d 属性是一个字符串,包含一系列的命令和参数,用来描述路径的形状。

1.1 d属性

M=moveto(M x,y)将画笔移到相对于svg的坐标位置
L=lineto(L x,y)画直线到指定的坐标位置
H=horizontal lineto(H x)画水平线到指定x坐标位置
V=vertical lineto(V y)画垂直线到指定的y坐标位置
Z=closepath()关闭路径
<svg>
  <path d="M 10 10 L 100 10 L 100 100 L 10 100 Z" />
</svg>
  • M 10 10 表示将画笔移动到坐标为 (10, 10) 的点。
  • L 100 10 表示从当前点绘制一条直线到坐标为 (100, 10) 的点。
  • L 100 100L 10 100 依次表示绘制两条直线。
  • Z 表示关闭路径,即从当前点绘制一条直线到路径的起点,完成矩形的绘制。

1.2 path生成器

d3.line().x().y()//折线图

d3.geoPath().projection()//用于地图

d3.area()// 绘制平面

d3.arc().innerRadius().outerRadius() //饼图

d3.lineRadial().angle().radius() //极坐标系

文档:https://github.com/d3/d3-shape/tree/v1.3.7

案例 

调试的时候把定时器的时间设置的比transition短很多,感觉图像每次都没画到位,在这停了好久。。太蠢了。。

最后那个所有折线的汇总和颜色标签是修改了源代码自己添加的效果,学有成效就好~

<!DOCTYPE html>
<html>
  <head>
    <title>Line</title>
    
  </head>
  <body>
    <svg width="1600" height="800" id="mainsvg" class="svgs"></svg>
    <script src="/static/js/d3.min.js"></script>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script>
      const svg = d3.select('#mainsvg');
      const width = +svg.attr('width');
      const height = +svg.attr('height');
      const margin = {top: 120, right: 160, bottom: 50, left: 150};
      const innerWidth = width - margin.left - margin.right;
      const innerHeight = height - margin.top - margin.bottom;
      const g = svg.append('g').attr('id', 'maingroup')
      .attr('transform', `translate(${margin.left}, ${margin.top})`);
      const xValue = (datum) => {return datum['日期']};
      const yValue = (datum) => {return datum['现有确诊']};
      let xScale, yScale; 
      let alldates;
      let allkeys; 
      const yAxisLabel = '现有确诊人数';

      const colors = [
        '#FF0000', '#FFA500', '#FFFF00', '#008000', '#00FFFF', '#0000FF', '#FF00FF',
        '#800080', '#C0C0C0', '#808080', '#800000', '#FFD700', '#00FF00', '#00CED1',
        '#1E90FF', '#ADD8E6', '#FFC0CB', '#FA8072', '#FFA07A', '#FF69B4', '#8B008B',
        '#EE82EE', '#8FBC8F', '#3CB371', '#BDB76B', '#CD5C5C', '#F0E68C', '#7B68EE',
        '#4169E1', '#A0522D', '#2F4F4F', '#D3D3D3', '#4682B4'
      ];

      const provinces1 = ["安徽","澳门","北京","福建","甘肃","广东","广西","贵州",
        "海南","河北" ,"河南","黑龙江","湖南" ,"吉林","江苏","江西","辽宁","内蒙古",
        "宁夏","青海","山东","山西","陕西","上海","四川","台湾","天津","西藏","香港",
        "新疆","云南","浙江","重庆"
      ]


      const render_init = function(data){
        xScale = d3.scaleTime() //与scaleLiner()相似,但是domain必须是日期
        .domain(d3.extent(data, xValue))
        .range([0, innerWidth])
        .nice();

        yScale = d3.scaleLinear()
        .domain([d3.max(data, yValue), d3.min(data, yValue)])
        .range([0, innerHeight])
        .nice();

        // Adding axes
        const xAxis = d3.axisBottom(xScale)
        .ticks(Math.floor(alldates.length) / 4)
        //.tickFormat(d3.timeFormat('%b-%d'))
        .tickSize(-innerHeight)
        const xAxisGroup = g.append('g').call(xAxis)
        .attr('transform', `translate(0, ${innerHeight})`);

        const yAxis = d3.axisLeft(yScale).tickSize(-innerWidth);
        const yAxisGroup = g.append('g')
        .call(yAxis)
        .append('text')
        .attr('font-size', '2em')
        .attr('transform', `rotate(-90)`)
        .attr('x', -innerHeight / 2)
        .attr('y', -60)
        .attr('fill', '#333333')
        .text(yAxisLabel)
        .attr('text-anchor', 'middle') // Make label at the middle of axis. 
        yAxisGroup.selectAll('.domain').remove(); // we can select multiple tags using comma to seperate them and we can use space to signify nesting; 
            

        g.selectAll('.tick text').attr('font-size', '2em');
        g.append('path').attr('id', 'alterPath');

        
        let legend = d3.select('#maingroup').selectAll(".legend")
        .data(provinces1)
        .enter().append("g")
        .attr("class", "legend")
        .attr("transform", function(d, i) { 
          if(i >= 20)return "translate(" + (innerWidth + 90) + "," + ((i - 20) * 25) + ")"
          return "translate(" + (innerWidth + 10) + "," + (i * 25) + ")"; });
      
        // draw legend colored rectangles
        legend.append("rect")
        .datum(provinces1) 
        .attr("x", 0)
        .attr("y", 0)
        .attr("width", 20)
        .attr("height", 20)
        .style("fill", function (d,i) {return colors[i]});

        // draw legend text
        legend.append("text")
        .datum(provinces1) 
        .attr('class', 'legend_text')
        .attr("x", 30)
        .attr("y", 9)
        .attr("dy", ".5em")
        .style("text-anchor", "start")
        .text(function (d,i) { return provinces1[i]});
      }; 

      // const render_update = function(data){
      //     const line = d3.line()
      //     .x(d => {return xScale(xValue(d))})
      //     .y(d => {return yScale(yValue(d))})
      //     .curve(d3.curveCardinal.tension(0.5))

      //     // lineEmpty is typically used for the first animation that raise the line up; 
      //     const lineEmpty = d3.line()
      //     .x(d => {return xScale(xValue(d))})
      //     .y(d => {return yScale(0)})
      //     .curve(d3.curveCardinal.tension(0.5))

      //     const maingroup = d3.select('#maingroup');
      //     const pathupdate = maingroup.selectAll('.datacurve').data([data])
          
      //     const pathenter = pathupdate.enter().append('path')
      //     .attr('class', 'datacurve')
      //     .attr("fill", "none") 
      //     .attr("stroke", "steelblue")
      //     .attr("stroke-width", 2.5)
      //     .attr("d", lineEmpty)

      //     pathupdate.merge(pathenter)
      //     .transition().duration(2000).ease(d3.easeLinear)
      //     .attr("d", line)
      // };

      const render_update_alter = function(data, color){
        console.log(data)
        const line1 = d3.line()
          .x(d => {return xScale(xValue(d))})
          .y(d => {return yScale(yValue(d))})
          //.curve(d3.curveBasis)
          .curve(d3.curveCardinal.tension(0.5)) //把离散点连成线

        // See https://github.com/d3/d3-shape/blob/v1.3.7/README.md#curves
        d3.select('#alterPath').datum(data) //data()绑定的是数组 datum()绑定的是单个数据
        .attr('class', 'datacurve')
        .attr("fill", "none") //设置填充为无
        .attr("stroke", color) //画笔颜色
        .attr("stroke-width", 1.5) //画笔宽度
        .transition().duration(2000) //这个要设置的比定时器时间短
        .attr("d", line1) //line的输入就是数据绑定的图元

        province = data[0]['省份'];
        g.selectAll('.province_text').remove();
        g.append("text")
        .data(data) 
        .attr('class', 'province_text')
        .attr("x", innerWidth / 4)
        .attr("y", - 20)
        .style("text-anchor", "end")
        .attr("fill", "lightskyblue")
        .attr('font-size', '4em')
        .attr('font-weight', 'bold')
        .text('省份:'+province);
      }

      const renderEnd = function(data, color){
        const line = d3.line()
          .x(d => {return xScale(xValue(d))})
          .y(d => {return yScale(yValue(d))})
          //.curve(d3.curveBasis)
          .curve(d3.curveCardinal.tension(0.5))

        g.append('path').datum(data) //data()绑定的是数组 datum()绑定的是单个数据
        .attr("class", "datacurve")
        .attr("fill", "none") //设置填充为无
        .attr("stroke", color) //画笔颜色
        .attr("stroke-width", 1.5) //画笔宽度
        .attr("d", line) //line的输入就是数据绑定的图元


      }

      d3.csv('./province.csv').then(function(data){

        //暂时删除湖北 数据太大会影响比例
        data = data.filter(datum => {return datum['省份'] !== '总计'}); 
        data = data.filter(datum => {return datum['省份'] !== '湖北'}); 
        //日期要单独拿出来做横轴的比例尺
        alldates = Array.from(new Set( data.map( d => xValue(d) ) ));

        //数据类型转换
        data.forEach( datum => {
          datum['现有确诊'] = +(datum['现有确诊']);
          datum['日期'] = new Date(datum['日期']);
        } ); 

        let provinces = {}; 
        allkeys = Array.from(new Set( data.map( d => d['省份'] ) ))
        console.log(allkeys)
        allkeys.forEach( key => {provinces[key] = []} );
        //数据按照省份归类
        data.forEach( d => { provinces[d['省份']].push(d) } )
        //确保每个省份的数据按照日期顺序排序
        allkeys.forEach( key => provinces[key].sort(function(a,b){
              return a['日期'] - b['日期'];
        })); 
        render_init(data);
        let c = 0;
        let intervalId = setInterval(() => {
            if(c >= allkeys.length){
                clearInterval(intervalId);
                g.selectAll('.province_text').remove();
                g.selectAll('#alterPath').remove();
                let i = 0
                allkeys.forEach(item => {
                  renderEnd(provinces[item],colors[i])
                  i++
                })
            }else{
                let key = allkeys[c];
                render_update_alter(provinces[key], colors[c]);
                c = c + 1;
            }
        }, 3000);
      });

    </script>
  </body>
</html>

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/454586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux子进程信号处理机制

Linux中子进程的信号处理与父进程的联系有以下三条&#xff1a; fork后子进程会继承父进程绑定的信号处理函数&#xff08;很好解释&#xff0c;子进程会拷贝父进程的代码&#xff0c;包括信号处理函数&#xff09;如果子进程调用exec族函数&#xff0c;子进程代码段被新的程序…

Qt 项目Mingw编译器转换为VS编译器时的错误及解决办法

错误 在mingw生成的项目&#xff0c;转换为VS编译器时通常会报些以下错误&#xff08;C4819警告&#xff0c;C2001错误&#xff0c;C2143错误&#xff09; 原因及解决方式 这一般是由于字符编码引起的&#xff0c;在源代码文件中包含了中文字符导致的。Qt Creator 生成的代码文…

算法的时间复杂度和空间复杂度分析

文章目录 实验目的实验内容实验过程运行结果复杂度分析 实验目的 通过本次实验&#xff0c;了解算法复杂度的分析方法&#xff0c;掌握递归算法时间复杂度的递推计算过程。 实验内容 二路归并排序的算法设计和复杂度分析。 实验过程 1.算法设计 归并排序&#xff1a;是指将…

活动回顾|多模态 AI 开发者的线下聚会@深圳站(内含福利)

回顾来了&#xff01; 4 月 22 日&#xff0c;由 Jina AI 和 OpenMMLab 联合主办的 「多模态 AI 」Office Hours 深圳站圆满结束&#xff0c;迎来了将近 60 位开发者的热情参与&#xff01;现场不仅有别开生面的「开发者集市」供大家打卡赢取好礼&#xff0c;更有四场干货满满的…

传统机器学习(六)集成算法(1)—随机森林算法及案例详解

传统机器学习(六)集成算法(1)—随机森林算法及案例详解 1、概述 集成学习&#xff08;Ensemble Learning&#xff09;就是通过某种策略将多个模型集成起来&#xff0c;通过群体决策来提高决策准确率。 集成学习首要的问题是选择什么样的学习器以及如何集成多个基学习器&…

杂谈 看唯工具论的问题 与 瑞典马工的一些言论 如何辩证看

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

华为OD机试真题(Java),根据员工出勤信息,判断本次是否能获得出勤奖(100%通过+复盘思路)

一、题目描述 公司用一个字符串来标识员工的出勤信息 absent&#xff1a;缺勤late&#xff1a;迟到leaveearly&#xff1a;早退present&#xff1a;正常上班 现需根据员工出勤信息,判断本次是否能获得出勤奖&#xff0c;能获得出勤奖的条件如下&#xff1a; 缺勤不超过1次没…

闲聊之π和e到底是个啥

π和e 1. 圆周率π 耳熟能详的π&#xff0c;到底是什么&#xff0c;怎么来的&#xff1f; 圆周率π&#xff0c;圆的周长C2πr&#xff0c;其中r是圆的半径 1.1 刘徽割圆术 如图中所示&#xff0c;作出圆内的正十二边形&#xff0c;正二十四边形&#xff0c;…&#xff0c;用…

倾斜摄影三维模型OSGB格式 到OBJ 格式转换几个软件操作方法

倾斜摄影三维模型OSGB格式 到OBJ 格式转换几个软件操作方法 倾斜摄影三维模型是一种重要的三维地理信息数据&#xff0c;通常以OSGB格式保存。但在不同的三维软件中使用时&#xff0c;需要将其转换为更通用的OBJ格式。本文将介绍在技术上如何将OSGB格式的倾斜摄影三维模型转换为…

手机投屏到电脑的实用工具

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 背景说明 最近在项目开发工作中需要将手机屏幕投影到PC端&#xff0c;并通过PC端操作手机。为了满足该项开发需求&#xff0c;在项目小组中采用了Vysor作为工具。 Vysor介…

手写axios源码系列四:interceptor拦截器

文章目录 一、拦截器 interceptor1、创建 InterceptorManager.js 文件2、Axios 中实例化 InterceptorManager 类3、总结 在本系列的第一篇章节 手写axios源码系列一&#xff1a;axios核心知识点 中已经介绍过一些拦截器的基础知识&#xff0c;可知拦截器分为&#xff1a; 请求…

24从零开始学Java之如何正确地使用一维数组

作者&#xff1a;孙玉昌&#xff0c;昵称【一一哥】&#xff0c;另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在之前的文章中&#xff0c;壹哥给大家讲解了java里的顺序结构、分支结构、循环结构等内容&#xff0…

diffusion扩散模型之hello world

以mnist图像生成样本为例&#xff0c;详细解释diffusion的每个步骤和过程 扩散模型包括两个过程&#xff1a;前向过程&#xff08;forward process&#xff09;和反向过程&#xff08;reverse process&#xff09;&#xff0c;其中前向过程又称为扩散过程&#xff08;diffusio…

Taro React组件开发(9) —— RuiCountDown 倒计时

1. 需求实现 根据传入的格式,返回倒计时的文本字段;时间格式需要自定义,需要返回对应时间的值;对毫秒级的时间进行渲染;自定义时间的样式;手动控制倒计时的开始、暂停和重置。2. 需求实现 查找网上类似组件 uView CountDown 倒计时;由于 uView CountDown 倒计时 是使用 …

深度学习 - 42.特征交叉与 SetNET、Bilinear Interaction 与 FiBiNet

目录 一.引言 二.摘要 - ABSTRACT 三.介绍 - INTRODUCTION 四.相关工作 - RELATED WORK 1.因式分解机及其变体 - Factorization Machine and Its relevant variants 2. 基于深度学习的点击率模型 - Deep Learning based CTR Models 3.SENET Module 五.FiBiNet Model 1…

【嵌入式】HC32F定时器PWM捕获+APC芯片实现模拟AD采样

目录 一 项目背景 二 原理说明 三 设计实现——定时器初始化 四 设计实现——PWM捕获 五 梳理总结 一 项目背景 目前使用了TI的ADC采样芯片ADS1018实现模拟量4-20mA/0-20mA的采样&#xff0c;原理是将外部输入的模拟量信号4-20mA&#xff0c;经由并联的两个100Ω电阻&#…

day-01 one-day projects

个人名片&#xff1a; &#x1f60a;作者简介&#xff1a;一名大一在校生&#xff0c;web前端开发专业 &#x1f921; 个人主页&#xff1a;python学不会123 &#x1f43c;座右铭&#xff1a;懒惰受到的惩罚不仅仅是自己的失败&#xff0c;还有别人的成功。 &#x1f385;**学习…

AIGC席卷,抖快、阅文、知乎大战网文圈

配图来自Canva可画 成熟的网文市场&#xff0c;时不时进来一条鲶鱼。 经历了二十几个夏秋秋冬&#xff0c;网文市场形成了阅文、晋江、七猫、番茄等平台割据一方稳定的市场格局。后来暗自布局网文市场的知乎、抖音、快手等新玩家开始浮出水面&#xff0c;未来的市场纷争下或许…

Docker持久化方式-v和-volume的区别

docker数据的持久化一直用的是-v的方式&#xff0c;又叫Bind Mounts&#xff08;目录绑定&#xff09;&#xff0c;偶然间发现还有一种通过卷轴来实现持久化的方式&#xff0c;翻了下资料&#xff0c;整理了一下两种方式使用的场景。 -v&#xff08;Bind Mounts&#xff09; …

使用 Apache PDFBox 操作PDF文件

简介 Apache PDFBox库是一个用于处理PDF文档的开源Java工具。该项目允许创建新的PDF文档&#xff0c;操作现有PDF文档&#xff0c;并从PDF文档中提取内容。Apache PDFBox还包括几个命令行实用程序。 Apache PDFBox的主要功能如下&#xff1a; 从PDF文件中提取Unicode文本。将…