d3遇到的各种问题
一 层叠关系---文字显示不出来层叠关系 如果元素都被g包裹,那么后面g里面的元素会覆盖前面g里面的元素let g=svg.append('g').attr('transform','translate(200,200)')let gl=svg.append('g').attr('transform','translate(200,200)')//如果文字在g中,就会被gl里面的元素覆盖
一 层叠关系---文字显示不出来层叠关系 如果元素都被g包裹,那么后面g里面的元素会覆盖前面g里面的元素let g=svg.append('g').attr('transform','translate(200,200)')let gl=svg.append('g').attr('transform','translate(200,200)')//如果文字在g中,就会被gl里面的元素覆盖
两个点之间的直线长度function length(x0,y0,x1,y1){ x0-=x1;y0-=y1; return Math.sqrt(x0*x0+y0*y0)}一段直线从两端相向缩短r长度function faceToface(dot0,dot1,r){ let difX=dot0[0]-dot1[0], difY=dot0[1]-dot1[1] let bigR=Math.sqrt(difX*difX+difY*difY) let changeY=Math.abs(difY/bigR*r) let changeX=Math.abs(difX/bigR*r) let x0=dot0[0]>dot1[0]?dot0[0]-changeX:dot0[0]+changeX let y0=dot0[1]>dot1[1]?dot0[1]-changeY:dot0[1]+changeY let x1=dot1[0]>dot0[0]?dot1[0]-changeX:dot1[0]+changeX let y1=dot1[1]>dot0[1]?dot1[1]-changeY:dot1[1]+changeY return [[x0,y0],[x1,y1]]}实例验证的代码let circles=[[100,300],[300,100]]let cut=faceToface(circles[0].slice(),circles[1].slice(),50)console.log(cut)this.svg.selectAll('.circle').data(circles).join(enter=>{ enter.append('circle') .attr('cx',d=>d[0]) .attr('cy',d=>d[1]) .attr('r',50) .attr('fill','none') .attr('stroke','#000') }) this.svg.append('path') .attr('d',d3.line()(circles)) .attr('stroke','yellow') this.svg.append('path') .attr('d',d3.line()(cut)) .attr('stroke','red')
declare const d3;export class BarChart { svg: any; svgWidth=400; svgHeight=400; padding={top:30,bottom:30,left:30,right:30} rectWidth=30 axisWidth=300 axisHeight=300 constructor(target: HTMLElement) { this.svg=d3.select(target) .attr('width',this.svgWidth) .attr('height',this.svgHeight) } drawBar(data) { let names=['小明','小红','小圆','小贾','小猪'] //坐标轴 let xscale=d3.scalePoint() .domain(names) .rangeRound([0,this.axisWidth]) .padding(0.8) let xaxis=d3.axisBottom(xscale) this.svg.append('g').attr('transform',`translate(${this.padding.left},${this.svgHeight-this.padding.bottom})`).call(xaxis) let yscale=d3.scaleLinear([100,0],[0,this.axisHeight]) let yaxis=d3.axisLeft(yscale) this.svg.append('g') .call(yaxis) .attr('transform',`translate(${this.padding.left},${this.svgHeight-this.axisHeight-this.padding.bottom})`) //数据缩放 data=data.map(d=>yscale(d)) //console.log(data); //y轴原点的纵坐标 let y0=this.svgHeight-this.padding.bottom-this.axisHeight let rect=this.svg.selectAll('rect').data(data) .join(enter=>{ enter.append('rect').attr('fill','red') .attr('x',(d,i)=>xscale(names[i])+this.padding.left-0.5*this.rectWidth) .attr('y',d=>y0+d) .attr('width',this.rectWidth) .attr('height',d=>this.axisHeight-d) },update=>{ update.attr('fill','red') .attr('x',(d,i)=>xscale(names[i])+this.padding.left-0.5*this.rectWidth) .attr('y',d=>y0+d) .attr('width',this.rectWidth) .attr('height',d=>this.axisHeight-d) },exit=>{ exit.remove() }) //console.log(rect); let text=this.svg.selectAll('.text').data(data) .join(enter=>{ enter.append('text') .text(d=>yscale.invert(d)) .attr('x',(d,i)=>xscale(names[i])+this.padding.left) .attr('y',d=>y0+d) .attr('text-anchor','middle') },update=>{ update .text(d=>yscale.invert(d)) .attr('x',(d,i)=>xscale(names[i])+this.padding.left) .attr('y',d=>y0+d) .attr('text-anchor','middle') },exit=>{ exit.remove() }) //console.log(text); } }
在比例尺上 domain是定义域,range是值域在坐标轴上 domain是刻度值,range是长度:刻度到原点的距离
quantize是等分的意思let scale=d3.scaleQuantize() .domain([0,2,4,10]) .range([1,100])console.log(scale(3));//100解析:定义域里面有三段值[0,2),[2,4),[4,10).值分别域里面有两个数就把这三段值分别均分成两等分,三个数就三等分
官网给的解决方案 <div id="Ford"></div><div id="Jarrah"></div><div id="Kwon"></div><div id="Locke"></div><div id="Reyes"></div><div id="Shephard"></div>var data = [ {name: "Locke", number: 4}, {name: "Reyes", number: 8}, {name: "Ford", number: 15}, {name: "Jarrah", number: 16}, {name: "Shephard", number: 23}, {name: "Kwon", number: 42}];d3.selectAll("div") .data(data, function(d) { return d ? d.name : this.id; }) .text(function(d) { return d.number; });解释说明下:按照一般的排序规则,把d.name给到d3就行了,可是d是undefined。接收不到值,所以把值给d3的任务就交给了后面的this.id。要注意的是前面你要按照d.name排序后面给的值就不能是d.number总结一下:前面的d.name:要按这个排序后面的this.id:就是d.name的值
原文地址:https://geekplux.com/2018/08/28/how-to-implement-sankey-diagram.html什么是桑基图Google 搜索桑基图,可以搜到一大堆定义。简而言之,桑基图是一种数据流图,展示了数据是如何从左到右流向最后的节点,每条边代表一条数据流,宽度代表数据流的大小。桑基图常用于流量分析,可以很清楚的看出数据是如何渐渐分流的。本文着重讲解如何实现,理论方面的东西各位可以自行了解。实现桑基图的关键点关键点有两个:1. 坐标计算桑基图要展现的数据流,算是图(拓扑类、网络型或关系型)数据的一种。实现一个数据可视化图,最重要的就是拆解元素。而实现一个图数据可视化,则最重要的是分清“节点”和“边”。拆解元素之后,最重要的便是坐标的计算,这里包括点和边的坐标。而图形中,任何的元素都可以看作是点连线而成,所以元素坐标的计算实际上就成了点坐标的计算。比如桑基图中,节点是一个矩形,那么只需计算两个点(左上和右下)的坐标(x0, y0),(x1, y1)便可确定;边是一个带形,需要计算四个端点才能确定,带形的弧度则可由简单的三次贝塞尔曲线计算得来。由此观之,实现桑基图的核心在于计算出以上的这些点坐标。其实实现任意一种可视化都是计算点的坐标。2. 减少边交叉当数据量到一定程度的时候, 桑基图中的边会出现重叠现象,造成一定的视觉混乱。如何减少可以阅读本文第二节。一、坐标计算的实现准备工作设计数据结构经典的图数据结构一般是邻接矩阵和邻接表,我们也可以自己设计。我在做拓扑数据可视化的时候,会先和后端或数据同学商定好我需要拿到的数据结构,通常是这个样子:{ nodes: [ { foo: bar }, { foo: baz }, … ], links: [ { source: 0, target: 1, value: 100 }, { source: 1, target: 2, value: 10 }, … ]}而我拿到之后要做的第一步就是先把 nodes 和 links 串联起来,这里每个 link 的 source 和 target 分别是 nodes 的下标,当然你也可以设置其他的引用(指针),总之通过引用讲两者串联起来,变成:{ nodes: [ { foo: bar, column: 0, // 节点所在第几列 row: 0, // 节点所在第几行 value: 100, // 节点数据流大小 sourceLinks: [ { source: 0, target: 1, … } ], targetLinks: [ … ] }, … ], links: [ { source: 0, target: 1, value: 100, sourceNode: { foo: bar, column: 0, row: 0, … }, targetNode: { … } }, … ]}这样,对于某个节点来说,可以直接用 O(1) 的时间复杂度访问到它的任意相邻节点。计算节点数据流大小这里的计算方法可自己定,通常是取该节点入边和出边的数据流大小之和的最小值。计算节点所在行列在桑基图的计算中,我们还需要进行一个关键的计算——计算节点在桑基图中的第几行第几列。第几列的计算,即为节点在图中的深度计算:入度为 0 的节点深度为 0,在第一列出度为 0 的节点深度最大,在最后一列其余节点的深度为他相连源节点的最大深度加 1第几行的计算,涉及到排序的问题,通常某一列中的节点都是按节点数据流大小,从大到小排序。节点坐标计算刚才我们说过,坐标计算可以分为两部分:节点和边。其中,边的坐标位置依赖于节点的坐标,所以应该先计算节点坐标。但在计算坐标之前,首先要明确一个问题:是否限定视图的宽高。这个问题引申出两种节点坐标的计算方式。不限定视图宽高如果不限定宽高,那么节点坐标的计算步骤很简单:设置一个节点的宽度设置节点的水平间距从左至右,根据刚才计算出的节点所在第几列,计算出节点的横坐标(x0, x1),初始的 x0 为 0设定一个比例尺函数(多大的数据流对应屏幕上的多少像素,通常是首先设定一个节点最小高度和一个节点最大高度,然后找出所有节点数据流的最小和最大值,映射成一个定义域为节点数据流大小,值域为节点高度的函数)通过比例尺计算出节点高度设置一个节点垂直间距从上至下,根据刚才计算出的节点所在第几行,计算出节点的纵坐标 (y0, y1),初始的 y0 为 0大致是这个思路,横坐标的计算取决于两个值,节点宽度和 节点水平间距;纵坐标的计算取决于 节点的数据流大小 和 节点垂直间距。具体的计算代码,可根据你自己的数据结构来调整。限定视图宽高如果限定宽高,那么计算步骤需要换个思路:节点的宽度和节点的水平间距需要根据节点的列数和视图宽度来计算,你可以自己手动调整也可以设计个算法来算从左只有,根据节点宽度和节点水平间距,计算出节点横坐标设定一个比例尺函数,计算出节点的高度设置一个节点垂直间距通过高斯-赛德尔迭代(Gauss–Seidel method)计算出纵坐标(大致的思路是,先根据前两步的数值算出一个初始节点坐标,如果总体布局超出视图的下界,则节点高度和节点垂直间距都按比例缩小(如 0.95),并同时上移 n 个像素,如果总体布局超出视图上界,则节点高度和垂直间距都按比例缩小,并同时下移 n 个像素,直到总体的桑基图布局适应一开始限定的视图宽高)这个思路是 d3-sankey 的实现思路。如果你有限定视图宽高的需求,那么可以直接使用 d3-sankey。边的坐标计算只要确定了节点坐标,边的坐标可以根据它源节点和目标节点的坐标来算出:对于一个节点,将它的出边和入边进行排序(排序方法通常是根据相连节点在第几行从上到下排,也可以通过一些其他排序方法减少边的交叉,具体在第二节介绍)计算每个节点中单位数据流占节点高度的比例根据出边入边的数据流大小,乘上一步计算出的比例,则可得到每条边左右两边的高度从上到下,计算每条边的纵坐标每条边四个端点的横坐标分别对应源节点和目标节点的横坐标以上操作可以通过遍历每个 node 的 sourceLinks 和 targetLinks 来计算。得到边的四个端点以后,就可以算出三次贝塞尔曲线的控制点了:二、如何减少交叉通常要减少边的交叉,可以采用下面两种方法:均值排序sugiyama 算法均值排序这个名字是我自己起的。。不过这个方法很实用有效。对于每个源节点来说,都有相连的目标节点。这里的“均值”指的是所有相连目标节点所在行数的平均值(所有目标节点的行数相加,除以目标节点个数),这个平均值可以大致描述该节点每个出边的位置。每条出边都有这样一个值,这个值越小,则说明该出边要连接的目标节点的位置越靠上,反之越靠下。所以可根据这个值,从小到大排出出边在该节点上从上到下的位置。三、具体项目中的交互我参与的 UBA (User Behavior Analytics 内部项目) 项目中,正好用到了桑基图。除了上述的图形绘制之外,主要复杂的是交互。如图所示,除了基本的 hover 交互之外,项目中主要还有minimap 拖拽和刷选主视图的拖拽和缩放左下角的过滤器点击交互,高亮只经过选中节点的路径,并且边上高亮的部分由最后一个选中节点懈怠的数据流值确定,其余部分半透明整个桑基图实现下来发现绘制只是一些计算,交互才是更难抽象和处理的部分。综上,桑基图是一个 展现数据流非常好用的视图,感兴趣的同学可以自己实现一个试试。除了我文章中这些基本的桑基图布局,你还可以试试其他变种,另外交互方面也可以突破刚才我提到的那些,比如我之前实现过点击节点进行折叠/展开的交互。总体来说可视化还是一个比较有意思的方向。本作品采用知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议进行许可。 ...
这篇学习笔记是入门篇的最后一部分,将前几篇的内容整合到一起,绘制带过渡效果的柱状图,这次先给大家看一下结果图。结果前言先放结果图是想反馈一下在整合基础知识绘制完整柱状图遇到的几个问题:整个柱状图的布局,比如哪些元素包在一个<g>标签下,哪些元素是嵌套关系;如果不采用translate,transform 翻转height属性的值,如何让矩形正常方向显示;如何绘制文字;如何为柱状图添加过渡效果;坐标轴的位置如何确定,x轴如何划分刻度,如何让刻度显示在矩形的正下方;!!!! 接下来将逐个解决上述出现的问题!!!!Problem1:柱状图的整体布局Solution(1)为了绘制时,图形四周留有空白区域,我们首先设置一个padding值;var padding={top:40,bottom:40,left:40,right:40};//定义间隔(2)我们考虑在svg画布上进行绘制,采用如下的结构进行绘图: <svg> //将x轴包裹在一个g标签下 <g></g> //将y轴包裹在一个g标签下 <g></g> //将整个柱状图的矩形及文字包裹在一个g标签下 <g> //将柱形图的每个矩形与它相应的值包裹在一个g标签下 <g> <rect> <text> </g> </g> </svg>Problem2:如何按照垂直向上的方向显示矩形Solution之前的几篇文章我都是通过transform变换实现了矩形的翻转,这篇文章介绍一个新的思路。 首先确定一个矩形需要四要素(x,y,width,height),同时我们需要注意,画布的坐标轴方向为水平向右和垂直向下。height是我们数据可视化的展示部分,即数据的绑定部分,x,y确定了绘制矩形的左上角坐标。这里提供一个思路: 如果按照正常垂直向下的方向绘制矩形时,要求矩形的bottom处在同一水平线上,y+height==固定值;也就是数据(height)大的部分,我们希望矩形的绘制起始点(y)的值较小,数据小(height)的部分,我们希望矩形的绘制起始点(y)的值较大。 因此我们可以通过定义比例尺完成这个功能,将dataset中大的数值,映射出range中小的数值。//定义y方向比例尺var yScale=d3.scaleLinear().domain([0,d3.max(dataset)]).range([height-padding.top-padding.bottom,0]);//定义y的值.attr(“y”,function (d,i) { return yScale(d)})//定义height.attr(“height”,function (d,i) { return height-padding.top-padding.bottom-yScale(d);})可以看出来‘y’+‘height’==height-padding.top-padding.bottom(这是一个固定的值)Problem3:如何绘制文字Solution在Problem1中已经解决的布局方案问题,我们的方法是将矩形与文字包在一个g标签下,所以绘制文字与绘制矩形的方法相同,在<g>标签下添加<text>标签,同时需要设定: (1)文字的显示位置:x,y (2)文本信息:text (3)文字位置的偏移值:dx,dygraph.append(“text”).style(“fill”,“pink”).attr(“x”,function(d,i){ return xScale(i);}).text(function (d) { return d}).attr(“y”,function (d,i) { return yScale(d);})Problem4:如何为柱状图添加过渡效果Solution为柱状图添加过渡效果,我们需要调用以下API:.transition():为这个元素添加过渡;.duration():设定元素从起始状态到终止状态的过渡时间;.delay():设定元素执行过渡效果的时间间隔;.ease():设定过渡的动画效果;在为元素添加过渡效果时,初始状态,终止状态尤为重要,柱状图为例分析一下元素的两个状态:明确柱形图为每个矩形添加过渡时,只有两个属性值需要改变,一个是y的值,一个是height的值;起始状态:柱状图的起始状态非常好理解,就是矩形不显示的状态,即y值设定为前文提到的固定值,height设定为0;终止状态:柱状图的终止状态应该是矩形元素和文字都可视化固定显示出来,即为正常绑定元素时设定的相关属性值。//为矩形添加过渡效果.attr(“y”,function (d) { var min=yScale.domain()[0]; return yScale(min);}).attr(“height”,function(d,i){ return 0;}).transition().duration(2000).delay(function(d,i){ return i400;}).ease(d3.easeBackOut).attr(“y”,function (d,i) { return yScale(d)}).attr(“height”,function (d,i) { return height-padding.top-padding.bottom-yScale(d);})Problem5:格式化显示坐标轴Solution在开始学习坐标轴的时候,只实现了添加y轴,在这次完整柱状图实现中,尝试添加x轴却遇到了问题。在这个例子中我们一共绑定了8个数据,那么如何让x轴的刻度均匀的显示在每个矩形的下方呢?在定义x轴的时候我用了ScaleBand()这个方法://在range返回等差数列var xScale=d3.scaleBand().domain(d3.range(dataset.length)).rangeRound([0,dataset.length(rectWidth+(rectPadding/2))]);var xAxis=d3.axisBottom(xScale).ticks(5);既然比例尺返回一个等差数列,所以我们要求在柱状图区域,每个矩形和空白间隔这个整体是相同的,所以我的实现是每个矩形左右是半个rectPadding。先设置x的值,然后width设置成矩形宽度减去半个间隔。(不理解的可以自己画一张图就可以了).attr(“x”,function (d,i) { return (irectWidth)+(i+1)(rectPadding/2);}).attr(“width”,rectWidth-rectPadding/2)代码部分import * as d3 from “d3”;var dataset = [45, 70, 12, 79, 4, 127, 33, 150];var width = 600;//svg画布宽var height = 600;//svg画布高var rectWidth = 50;//每个矩形的默认宽度var rectPadding=10;//每个矩形间的间隔var padding={top:40,bottom:40,left:40,right:40};//定义间隔//定义画布var svg = d3.select(“body”).append(“svg”).attr(“width”, width).attr(“height”, height).style(“background-color”, “yellow”);//定义矩形比例尺var yScale=d3.scaleLinear().domain([0,d3.max(dataset)]).range([height-padding.top-padding.bottom,0]);var yAxis=d3.axisLeft(yScale).ticks(5);svg.append(“g”).attr(“transform”,translate(${padding.top},${padding.left})).call(yAxis);var xScale=d3.scaleBand().domain(d3.range(dataset.length)).rangeRound([0,dataset.length*(rectWidth+(rectPadding/2))]);var xAxis=d3.axisBottom(xScale).ticks(5);svg.append(“g”).attr(“transform”,translate(${padding.left},${height-padding.top})).call(xAxis);//定义矩形var g=d3.selectAll(“svg”).append(“g”).attr(“transform”,translate(${padding.top},${padding.left}));var graph=g.selectAll(“rect”).data(dataset).enter().append(“g”);graph.append(“rect”).style(“fill”,“blue”).attr(“x”,function (d,i) { return (irectWidth)+(i+1)(rectPadding/2);}).attr(“width”,rectWidth-rectPadding/2).attr(“y”,function (d) { var min=yScale.domain()[0]; return yScale(min);}).attr(“height”,function(d,i){ return 0;}).transition().duration(2000).delay(function(d,i){ return i400;})//.ease(d3.easeBackOut).attr(“y”,function (d,i) { return yScale(d)}).attr(“height”,function (d,i) { return height-padding.top-padding.bottom-yScale(d);})graph.append(“text”).style(“fill”,“pink”).attr(“x”,function(d,i){ return xScale(i);}).attr(“y”,function (d) { var min=d3.min(dataset); return yScale(min)}).text(function (d) { return d}).transition().duration(2000).delay(function(d,i){ return i400;}).attr(“y”,function (d,i) { return yScale(d);})附录接下来会写进阶篇的学习笔记 ...
这篇文章继续介绍d3的基础知识比例尺在绘制柱状图时,我们往往会定义很大的画布,然而我们要可视化的数据确很小,这时会出现很多留白的情况。为了根据显示刻度灵活变化宽高,而不是定死,特别是数据差异性很大的时候,我们希望图表显示范围都在画布里面,这时就会引入比例尺的概念来进行缩放(scale)。我们在数学里有函数的概念y=f(x),定义域为x(输入)的取值范围,值域为y(输出)的取值范围。输入x,根据函数规则会输出一个y。d3中的比例尺与数学中的函数类似,首先我们需要指定函数类别,即比例尺的类别,如scaleLinear,scalePow, scaleQuantise, scaleOrdinal, scaleSqrt, scaleLog, scaleSequential等等。然后我们需要给定函数的定义域domain([]),最后需要给定函数的值域range([])。根据这些规则,d3会为我们返回一个比例尺函数。示例var linearScale=d3.scaleLinear().domain([0,d3.max(dataset,function (d) { return d;})]).range([0,height]);//这里linearScale为一个函数,我们可直接执行linearScale(d)完整示例import * as d3 from “d3”;var width=300;//svg画布宽var height=200;//svg画布高var rectWidth=30;//每个矩形的默认宽度var dataset=[45,70,12,79,4,127,33,90];//定义画布var svg=d3.select(“body”).append(“svg”).attr(“width”,width).attr(“height”,height).style(“background-color”,“yellow”);//定义比例尺var linearScale=d3.scaleLinear().domain([0,d3.max(dataset,function (d) { return d;})]).range([0,height]);//绑定数据集 绘制柱形图svg.selectAll(“rect”).data(dataset).enter().append(“rect”).attr(“width”,rectWidth-2).attr(“height”,function (d,i) { return linearScale(d)}).attr(“x”,function (d,i) { return rectWidth*i}).attr(“y”,200).attr(“transform”,function (d,i) { return translate(0,${linearScale(-d)})}).attr(“fill”,“blue”)结果 结果显示,数据集被比例尺进行缩放(scale)可视化出来。 d3还提供很多数据处理的方法,max min extent sum median mean shuffle等等。坐标轴要定义坐标轴,主要两个步骤:(1)定义坐标轴的比例尺;(2)定义坐标轴朝向,刻度;示例import * as d3 from “d3”;var width = 300;//svg画布宽var height = 200;//svg画布高var dataset = [45, 70, 12, 79, 4, 127, 33, 150];var testDataset = [4.5, 7.0, 1.2, 7.9, 0.4, 12.7, 3.3, 9.0];var rectWidth = 30;//每个矩形的默认宽度//定义画布var svg = d3.select(“body”).append(“svg”).attr(“width”, width).attr(“height”, height).style(“background-color”, “yellow”);//定义矩形比例尺var linearScale = d3.scaleLinear().domain([0, d3.max(dataset, function (d) { return d;})]).range([0, 200]);var g = svg.append(“g”).attr(“transform”, “translate(40,0)”);g.selectAll(“rect”).data(dataset).enter().append(“rect”).attr(“width”, rectWidth - 2).attr(“height”, function (d, i) { return linearScale(d)}).attr(“x”, function (d, i) { return rectWidth * i}).attr(“y”, 200).attr(“transform”, function (d, i) { return translate(0,${linearScale(-d)})}).attr(“fill”, “blue”);//定义坐标轴比例尺var yScale = d3.scaleLinear().domain([0, d3.max(dataset, function (d) { return d;})]).range([200,0]);//定义坐标轴var yAxis = d3.axisLeft(yScale).ticks(5);//调用坐标轴g.append(“g”).attr(“transform”, translate(0,0)).call(yAxis);结果补充知识点1.“g”标签g标签是svg的常用标签,相当于一个容器标签,把相关元素进行组合。通过g标签组合在一起的元素,可以通过g标签设置属性等,进行坐标变换等操作,如attr(“transform”,“translate()”),进行元素平移;2.定义坐标轴 var yAxis = d3.axisLeft(yScale) .ticks(5);首先需要设置坐标轴的朝向,这里是向左,将坐标轴的比例尺作为参数传入axisLeft中;通过ticks设置刻度的数目(不过好像并没有什么用处)3.call() g.append(“g”) .attr(“transform”, translate(0,0)) .call(yAxis);yAxis是我们定义的一个坐标轴,其实它本身也是一个函数,将新建的分组<g>传给yAxis()函数,用以绘制,所以这句代码等价于yAixs (g.append(“g”) ) ; ...
《前端每日实战》专栏每天分解一个前端项目,用视频记录编码过程,再配合详细的代码解读,是学习前端开发的活的参考书!以下是 2018 年 8 月份发布的项目:95# 视频演示如何用纯 CSS 和 D3 创作一只扭动的蠕虫96# 视频演示如何用纯 CSS 和 D3 创作一艘遨游太空的宇宙飞船97# 视频演示如何用纯 CSS 创作一组昂首阔步的圆点98# 视频演示如何用纯 CSS 创作一只愤怒小鸟中的绿猪99# 视频演示如何用纯 CSS 创作一个过山车 loader100# 视频演示如何用纯 CSS 创作闪闪发光的霓虹灯文字#101 视频演示如何用 CSS 和 D3 创作一组摆线102# 视频演示如何用纯 CSS 创作一个小和尚103# 视频演示如何用纯 CSS 创作一只监视眼104# 视频演示如何用纯 CSS 创作一个货车 loader105# 视频演示如何用纯 CSS 创作一只玉免106# 视频演示如何用纯 CSS 创作一个没有 DOM 元素的动画107# 视频演示如何用纯 CSS 创作一只单眼怪兽108# 视频演示如何用 CSS 和 D3 创作一个抽象的黑白交叠动画109# 视频演示如何用 CSS 和 D3 创作一个用文字组成的心形图案110# 视频演示如何用纯 CSS 创作一只愤怒小鸟中的黑炮111# 视频演示如何用纯 CSS 创作一只艺术的鸭子112# 视频演示如何用纯 CSS 创作切换背景的按钮悬停效果113# 视频演示如何用纯 CSS 创作一个赛车 loader114# 视频演示如何用纯 CSS 和混色模式创作一个 loader 动画115# 视频演示如何用 CSS 和 D3 创作一组 数字的彩灯116# 视频演示如何用 VanillaJS 开发一个监控网络连接状态的页面117# 视频演示如何用纯 CSS 创作一只愤怒小鸟中的红火118# 视频演示如何用纯 CSS 创作一个沙漏 loader119# 视频演示如何用纯 CSS 创作一个接扎啤的动画(内含2个视频)[120# 视频演示如何用纯 CSS 创作锡纸撕开的文字效果(https://segmentfault.com/a/11…121# 视频演示如何用 CSS 和 D3 创作一个小鱼游动的交互动画122# 视频演示如何用纯 CSS 创作一个苹果系统的相册图标123# 视频演示如何用纯 CSS 创作一架双冀飞机 ...