d3.js 教程 模仿echarts折线图

今天我们来仿echarts折线图,这个图在echarts是折线图堆叠,但是我用d3改造成了普通的折线图,只为了大家学习(其实在简单的写一个布局就可以)。废话不多说商行代码。

1 制作 Line 类

class Line {constructor() {this._width = 1100;this._height = 800;this._padding = 10;this._offset = 35;this._margins = {right: 50,bottom: 50,left: 70,top: 100};this._scaleX = d3.scaleBand().range([0, this.quadrantWidth()]).paddingInner(1).align(0);this._scaleY = d3.scaleLinear().range([this.quadrantHeight(), 0]);this._color = d3.scaleOrdinal(d3.schemeCategory10);this._dataX = [];this._series = [];this._svg = null;this._body = null;this._tooltip = null;this._transLine = null;this._activeR = 5;this._ticks = 5;}render() {if(!this._tooltip) {this._tooltip = d3.select('body').append('div').style('left', '40px').style('top', '30px').attr('class', 'tooltip').html('');}if(!this._svg) {this._svg = d3.select('body').append('svg').attr('width', this._width).attr('height', this._height).style('background', '#f3f3f3')this.renderAxes();this.renderClipPath();}this.renderBody();}renderAxes() {let axes = this._svg.append('g').attr('class', 'axes');this.renderXAxis(axes);this.renderYAxis(axes);}renderXAxis(axes) {let xAxis = d3.axisBottom().scale(this._scaleX).ticks(this._dataX.length);axes.append('g').attr('class', 'x axis').attr('transform', `translate(${this.xStart()}, ${this.yStart()})`).call(xAxis)d3.selectAll('g.x .tick text').data(this._dataX).enter()}renderYAxis(axes) {let yAxis = d3.axisLeft().scale(this._scaleY).ticks(this._ticks);axes.append('g').attr('class', 'y axis').attr('transform', `translate(${this.xStart()}, ${this.yEnd()})`).call(yAxis)d3.selectAll('.y .tick').append('line').attr('class', 'grid-line').attr('x1', 0).attr('y1', 0).attr('x2', this.quadrantWidth()).attr('y2', 0)}renderClipPath() {this._svg.append('defs').append('clipPath').attr('id', 'body-clip').append('rect').attr('x', 0 - this._activeR - 1).attr('y', 0).attr('width', this.quadrantWidth() + (this._activeR + 1) * 2).attr('height', this.quadrantHeight())}renderBody() {if(!this._body) {this._body = this._svg.append('g').attr('class', 'body').attr('transform', `translate(${this._margins.left},${this._margins.top})`).attr('clip-path', 'url(#body-clip)')this.renderTransLine()}this.renderLines();this.renderDots();this.listenMousemove();}renderTransLine() {this._transLine = this._body.append('line').attr('class', 'trans-line').attr('x1', 0).attr('y1', 0).attr('x2', 0).attr('y2', this._scaleY(0)).attr('stroke-opacity', 0)}renderLines() {let line = d3.line().x((d,i) => this._scaleX(this._dataX[i])).y(d => this._scaleY(d))let lineElements = this._body.selectAll('path.line').data(this._series);let lineEnter =  lineElements.enter().append('path').attr('class', 'line').attr('d', d => line(d.data.map(v => 0))).attr('stroke', (d,i) => this._color(i))let lineUpdate = lineEnter.merge(lineElements).transition().duration(100).ease(d3.easeCubicOut).attr('d', d => line(d.data))let lineExit = lineElements.exit().transition().attr('d', d => line(d.data)).remove();}renderDots() {this._series.forEach((d,i) => {let dotElements = this._body.selectAll('circle._' + i).data(d.data);let dotEnter =  dotElements.enter().append('circle').attr('class', (v, index) => 'dot _' + i + ' index_' + index).attr('cx', (d,i) => this._scaleX(this._dataX[i])).attr('cy', d => this._scaleY(d)).attr('r', 1e-6).attr('stroke', (d,i) => this._color(i))let dotUpdate = dotEnter.merge(dotElements).transition().duration(100).ease(d3.easeCubicOut).attr('cx', (d,i) => this._scaleX(this._dataX[i])).attr('cy', d => this._scaleY(d)).attr('r', 2)let dotExit = dotElements.exit().transition().attr('r', 0).remove();})this._dataX.forEach((d,i) => {d3.selectAll('circle._' + i).attr('stroke', this._color(i))})}listenMousemove() {this._svg.on('mousemove', () => {let px = d3.event.offsetX;let py = d3.event.offsetY;if(px < this.xEnd() && px > this.xStart() && py < this.yStart() && py > this.yEnd()) {this.renderTransLineAndTooltip(px, py, px - this.xStart());} else {this.hideTransLineAndTooltip();}})}renderTransLineAndTooltip(x, y, bodyX) {//鼠标悬浮的indexlet cutIndex = Math.floor((bodyX + this.everyWidth() / 2) / this.everyWidth());//提示线位置this._transLine.transition().duration(50).ease(d3.easeLinear).attr('x1', cutIndex * this.everyWidth()).attr('x2', cutIndex * this.everyWidth()).attr('stroke-opacity', 1);// dot圆圈动画d3.selectAll('circle.dot').transition().duration(100).ease(d3.easeCubicOut).attr('r', 2)d3.selectAll('circle.index_' + cutIndex).transition().duration(100).ease(d3.easeBounceOut).attr('r', this._activeR)//提示框位置和内容if(x > this.quadrantWidth() - this._tooltip.style('width').slice(0,-2) - this._padding * 2) {x = x - this._tooltip.style('width').slice(0,-2) - this._padding * 2 - this._offset * 2;}if(y > this.quadrantHeight() - this._tooltip.style('height').slice(0,-2) - this._padding * 2) {y = y - this._tooltip.style('height').slice(0,-2) - this._padding * 2 - this._offset * 2;}let str = `<div style="text-align: center">${this._dataX[cutIndex]}</div>`;this._series.forEach((d, i) => {str = str + `<div style="width: 15px;height: 15px;vertical-align: middle;margin-right: 5px;border-radius: 50%;display: inline-block;background: ${this._color(i)};"></div>${d.name}<span style="display: inline-block;margin-left: 20px">${d['data'][cutIndex]}</span><br/>`
        })this._tooltip.html(str).transition().duration(100).ease(d3.easeLinear).style('display', 'inline-block').style('opacity', .6).style('left', `${x + this._offset + this._padding}px`).style('top', `${y + this._offset + this._padding}px`);}hideTransLineAndTooltip() {this._transLine.transition().duration(50).ease(d3.easeLinear).attr('stroke-opacity', 0);d3.selectAll('circle.dot').transition().duration(100).ease(d3.easeCubicOut).attr('r', 2);this._tooltip.transition().duration(100).style('opacity', 0).on('end', function () {d3.select(this).style('display', 'none')});}everyWidth() {return this.quadrantWidth() / (this._dataX.length - 1);}quadrantWidth() {return this._width - this._margins.left - this._margins.right;}quadrantHeight() {return this._height - this._margins.top - this._margins.bottom;}xStart() {return this._margins.left;}xEnd() {return this._width - this._margins.right;}yStart() {return this._height - this._margins.bottom;}yEnd() {return this._margins.top;}scaleX(a) {this._scaleX = this._scaleX.domain(a);}scaleY(a) {this._scaleY = this._scaleY.domain(a)}selectMaxYNumber(arr) {let temp = [];arr.forEach(item => temp.push(...item.data));let max = d3.max(temp);let base = Math.pow(10, Math.floor(max / 4).toString().length - 1);//获取Y轴最大值return Math.floor(max / 4 / base) * 5 * base;}dataX(data) {if(!arguments.length) return this._dataX;this._dataX = data;this.scaleX(this._dataX);return this;}series(series) {if(!arguments.length) return this._series;this._series = series;let maxY = this.selectMaxYNumber(this._series);this.scaleY([0, maxY])return this;}
}

 

2 css 文件

.domain {stroke-width: 2;fill: none;stroke: #888;shape-rendering: crispEdges;
}
.tick text {font-size: 14px;
}
.grid-line {fill: none;stroke: #888;opacity: .4;shape-rendering: crispEdges;
}
.trans-line {fill: none;stroke: #666;opacity: .4;
}
.line {fill: none;stroke-width: 2;
}
.dot {fill: #fff;
}
.tooltip{font-size: 15px;width: auto;padding: 10px;height: auto;position: absolute;background-color: #000000;opacity: .6;border-radius:5px;color: #ffffff;display: none;
}

3 HTML 文件

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>$Title$</title><link rel="stylesheet" type="text/css" href="css/base.css"/><script type="text/javascript" src="js/d3.v4.js"></script><script type="text/javascript" src="js/line.js"></script>
</head>
<body>
<script>var dataX = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];var series = [{name: '邮件营销', data:[120, 132, 101, 134, 90, 230, 210]},{name: '联盟广告', data:[340, 314, 292, 368, 380, 560, 520]},{name: '视频广告', data:[490, 546, 493, 522, 570, 890, 930]},{name: '直接访问', data:[810, 878, 794, 856, 960, 1220, 1250]},{name: '搜索引擎', data:[1640, 1864, 1802, 1868, 2580, 2660, 2640]}]var line = new Line();line.dataX(dataX).series(series).render()setInterval(() => {series = series.map((d,i) => {return {name: d.name,data: new Array(7).fill(1).map((dd, ii) => {return Math.floor(Math.random() * 200) + i * 200})}})console.log(series);line.dataX(dataX).series(series).render()}, 4000)
</script>
</body>
</html>

 

想预览和下载demo的朋友可以移步原文

原文地址 http://www.bettersmile.cn

转载于:https://www.cnblogs.com/vadim-web/p/11463072.html

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

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

相关文章

vue中v-for的使用

本人正在开始学习Vue,每天写写基础的记录,希望对大家有帮助,如有错误还望指出,我也是一个小白,也希望大家能一起进步 v-for指令的使用: 1.循环普通数组 item in list 中的item是自己个想写什么名写什么名 另一种写法 i 表示索引值 2.循环对象数组 3.循环普通对象 4.迭代数字 注…

js高级第一天

JavaScript面向对象 1.1两大编程思想&#xff1a; 1、面向过程 ​ 面向过程&#xff1a;POP(Process-oriented programming) 面向过程就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候再一个一个的依次调用就可以了。 ​…

d3.js 教程 模仿echarts legend功能

上一节记录没有加上echarts的legend功能&#xff0c;这一小节补一下。 1. 数据 我们可以从echarts中看出&#xff0c;折线数据并不是我们传进入的原始数据&#xff08;多数情况下我们也不会修改原始数据&#xff09;&#xff0c;而是原始数组的一个备份而已。备份数组的方法有很…

小程序2-基本架构讲解(一)WXSS样式

项目里边生成了不同类型的文件: .json 后缀的 JSON 配置文件.wxml 后缀的 WXML 模板文件.wxss 后缀的 WXSS 样式文件.js 后缀的 JS 脚本逻辑文件WXSS 样式 WXSS (WeiXin Style Sheets)是一套样式语言&#xff0c;用于描述 WXML 的组件样式。WXSS 具有 CSS 大部分的特性 新增了尺…

js高级—tab栏切换(面向对象做法)

<main><h4>Js 面向对象 动态添加标签页</h4><div class"tabsbox" id"tab"><!-- tab 标签 --><nav class"fisrstnav"><ul><li class"liactive"><span>测试1</span><sp…

Win10卸载python总是提示error2503失败各种解决办法

最近win10的电脑装了python的3.4&#xff0c;然后想卸载&#xff0c;就总是提示error 2053&#xff0c;类似于这种&#xff1a; 下面是我的坎坷解决之路&#xff1a; 1、网上说&#xff0c;任务管理器 --> 详细信息 --> explorer.exe结束任务&#xff0c;结束资源管理器&…

js高级—查询商品案例

<div class"search">按照价格查询&#xff1a;<input type"text" class"start"> - <input type"text" class"end"><button class"search-price">搜索</button> 按照商品名称查询&a…

js高级第二天

构造函数和原型 构造函数和原型 在典型的OOP 的语言中&#xff08;如Java&#xff09;&#xff0c;都存在类的概念&#xff0c;类就是对象的模板&#xff0c;对象就是类的实例&#xff0c;但在ES6之前&#xff0c;JS 中并没用引入类的概念。ES6&#xff0c;全称ECMAScript6.0…

操作系统原理之文件系统(第五章)

一、文件 1、⽂件系统的⽤户接⼝包括⽂件的命名、类型、属性和对⽂件的操作 2、⽂件命名&#xff1a;所有操作系统都允许⽤1&#xff5e;8个字⺟组成的字符串 3、⽂件扩展名&#xff1a;多数操作系统都⽀持⽂件名⽤圆点隔开分为两部分&#xff0c;圆点后⾯的部分称为⽂件扩展名…

js高级第三天

原型链 作用&#xff1a;提供一个成员的查找机制&#xff0c;或者查找规则含义&#xff1a;由原型所串联起来的链装结构JavaScript 的成员查找机制(规则) 当访问一个对象的属性&#xff08;包括方法&#xff09;时&#xff0c;首先查找这个对象自身有没有该属性。如果没有就查…

为什么大学的计算机老师技术那么厉害,却不愿意当程序员?

不知道大家有多少是从事跟计算机有关的工作的&#xff0c;每次想到大学时的计算机考试&#xff0c;都能令小小编心惊胆战呀&#xff0c;各式代码和计算机语言&#xff0c;真的是很令人头痛了。不过呢&#xff0c;也有很多大神&#xff0c;大学学着其他的专业&#xff0c;却在毕…

DDG全家桶之3022

本篇文章主要根据360Netlab新出的DDG分析文档来复现新变种3022&#xff0c;会涉及部分分析和清除的方法&#xff0c;本篇文章只用于学习交流&#xff0c;为广大受害者提供清除思路 &#xff0c;请勿用于非法用途&#xff0c;产生一切后果与作者无关 详情请参考文档&#xff1a;…

js高级第四天

课程回顾&#xff1a; ​ 原型链&#xff1a;由原型构成链状结构&#xff0c;提供成员查找机制 ​ 继承&#xff1a;组合继承&#xff1a;构造函数和原型对象 ​ 属性&#xff1a;调用父构造函数的时候用call改变this指向 ​ 方法&#xff1a;父实例对象赋值给子原型对象&a…

d3.js 制作简单的俄罗斯方块

d3.js是一个不错的可视化框架&#xff0c;同时对于操作dom也是十分方便的。今天我们使用d3.js配合es6的类来制作一个童年小游戏--俄罗斯方块。话不多说先上图片。 1. js tetris类 由于方法拆分的比较细所以加上了一些备注&#xff08;这不是我的风格&#xff01;&#xff09; c…

Flask中路由系统以及蓝图的使用

一、Flask的路由系统 1.app.route()装饰器中的参数 methods:当前URL地址&#xff0c;允许访问的请求方式 app.route("/info", methods["GET", "POST"]) def student_info():stu_id int(request.args["id"])return f"Hello Old b…

js高级第五天

课程回顾&#xff1a; ​ 原型链&#xff1a;由原型构成链状结构&#xff0c;提供成员查找机制 ​ 继承&#xff1a;组合继承&#xff1a;构造函数和原型对象 ​ 属性&#xff1a;调用父构造函数的时候用call改变this指向 ​ 方法&#xff1a;父实例对象赋值给子原型对象&a…

d3.js 制作简单的贪吃蛇

d3.js是一个不错的可视化框架&#xff0c;同时对于操作dom也是十分方便的。今天我们使用d3.js配合es6的类来制作一个童年小游戏–贪吃蛇。话不多说先上图片。 1. js snaker类 class Snaker {constructor() {this._size 30;this._len 3;this._width 900;this._height 690;th…

js高级第六天

Q课程回顾&#xff1a; ​ 闭包&#xff1a;有权访问另外一个函数的局部变量的函数&#xff0c;作用&#xff1a;延伸变量使用范围 ​ mdn&#xff0c;w3c function fn1 () {var n 3;return function () {console.log(n);} }​ 递归&#xff1a;函数调用其本身 function f…

Chrome 75 lazy-loading

Chrome 75 & lazy-loading https://addyosmani.com/blog/lazy-loading/ https://chromestatus.com/feature/5645767347798016 Chrome 75 将默认启用延迟加载功能 自 Chrome 75 起&#xff0c;将原生支持图片的延迟加载&#xff0c;在代码中编写 <img loading"lazy&…

d3.js 实现烟花鲜果

今天在d3.js官网上看到了一个烟花的DEMO&#xff0c;是canvas制作的&#xff0c;于是我想用d3.js来实现它&#xff0c;js代码只有几行。好了废话不多说&#xff0c;先上图。 1 js 类 因为烟花要有下落的效果&#xff0c;所以里面用到了一些简单的数学和物理知识来模拟重力&…