d3js scales深入理解
转自:https://www.cnblogs.com/kidsitcn/p/7182274.html
比例尺函数是这样的javascript函数:
- 接收通常是数字,日期,类别等data输入并且:
 - 返回一个代表可视化元素的值,比如坐标,颜色,长度或者半径等
 
比例尺通常用于变换(或者说映射)抽象的数据值到可视量化变量(比如位置,长度,颜色等)
比如,假设我们有以下数组数据:
[ 0, 2, 3, 5, 7.5, 9, 10 ]
我们可以这样创建一个比例尺函数:
var myScale = d3.scaleLinear()
.domain([0, 10])
.range([0, 600]);
d3将创建一个myScale函数用于接收[0,10]之间的数据输入(domain)映射为[0,600]像素的位置数据(range)
我们可以使用myScale函数来计算对应数据的positions数据:
myScale(0); // returns 0
myScale(2); // returns 120
myScale(3); // returns 180
...
myScale(10); // returns

如上面所说,比例尺主要用于将抽象数据映射为可视的量化元素,比如位置,长度,半径,颜色等。比如,他们可以这样应用
- 将抽象数据映射为0到500的长度值以便在bar chart中使用
 - 将抽象数据映射为0到200之间的位置数据值以便作为line charts中点的坐标来使用
 - 将百分比变化数据(+4%,+10%,-5%等)映射为颜色的对应变化(比如使用红色表示为正值,正的越多越红,负值为绿色,负的越多绿色饱和度越高)
 - 将日期数据映射为x轴上的位置
 
Constructing scales
这部分我们集中使用线性比例尺linear scale作为例子探讨scale的相关知识,在后面再探讨其他类型的比例尺
var myScale = d3.scaleLinear();
注意:v4和v3的声明方式是不同的 d3.scaleLinear() in v4 and d3.scale.linear() in
myScale
.domain([0, 100])
.range([0, 800]);
通过上面的代码,myScale就成为有特定意义的比例尺函数了。现在myScale可以接收任何在0,100之间的domain,而映射到0到800的range里面。我们可以像下面一样来调用这个比例尺函数:
myScale(0); // returns 0
myScale(50); // returns 400
myScale(100); // returns 800
D3 scale types
D3大约有12种不同的比例尺类型(scaleLinear, scalePow, scaleQuantise, scaleOrdinal etc.) ,而总体来说我们可以分为3类
- scales with continuous input and continuous output(连续性输入连续性输出)
 - scales with continuous input and discrete output (连续性输入离散性输出)
 - scales with discrete input and discrete output (离散输入离散输出)
 
下面我们一个一个地来仔细学习一下
Scales with continuous input and continuous output(连续性输入连续性输出)
scaleLinear
线性比例尺可能是应用最为广泛的比例尺类型了,因为他们最适合将数据转化为位置和长度。因此往往也以线性比例尺为例去讲解和学习比例次的知识
他们使用一个线性函数(y=m*x+b)来表达(domain)和(range)之间的数学函数关系

var linearScale = d3.scaleLinear()
.domain([0, 10])
.range([0, 600]); linearScale(0); // returns 0
linearScale(5); // returns 300
linearScale(10); // returns 600

典型地,他们被用于将抽象数据转换为位置,长度等可视元素。因此当我们创bar chart,line chart,或者其他很多图标类型时,我们可以使用它。
除了位置长度作为range,也可以使用颜色值哦(实际上颜色也可以作为连续性数据的):

var linearScale = d3.scaleLinear()
.domain([0, 10])
.range(['yellow', 'red']); linearScale(0); // returns "rgb(255, 255, 0)"
linearScale(5); // returns "rgb(255, 128, 0)"
linearScale(10); // returns "rgb(255, 0, 0)"

这个特性常常被用于等值线图(choropleth),当然我们也可以使用scaleQuantize,scaleQuantile和scaleThrshold.
scalePow
scalePow这个比例次使用y = m * x^k + b这个数学函数来表达domain和range之间的数学函数关系。指数k使用.exponent()来设定:
var powerScale = d3.scalePow() .exponent(0.5) .domain([0, 100]) .range([0, 30]);
powerScale(0); // returns 0
powerScale(50); // returns 21.21...
powerScale(100); // returns 30
scaleSqrt
scaleSqrt 比例次是一种scalePow的特殊形式(k=0.5)通常用于通过面积来表征圆(而不用半径)(当使用圆的大小来表达数据值的大小时,通常最佳实践是使用和数据等比例的面积而非半径来表达)

var sqrtScale = d3.scaleSqrt()
.domain([0, 100])
.range([0, 30]); sqrtScale(0); // returns 0
sqrtScale(50); // returns 21.21...
sqrtScale(100); // returns 30

我们可以看到这个例子和上面的例子输出是一样的。

scaleLog
Log scales 使用数学对数函数(y = m * log(x) + b)来映射domain和range.如果数据之间有指数关系的话,这个log比例尺最适合

var logScale = d3.scaleLog()
.domain([10, 100000])
.range([0, 600]); logScale(10); // returns 0
logScale(100); // returns 150
logScale(1000); // returns 300
logScale(100000); // returns 600


scaleTime
scaleTime和scaleLinear是类似的,唯一的区别是domain用于代表date的数组。(通常对于时间序列非常有用)
timeScale = d3.scaleTime() .domain([new Date(2016, 0, 1), new Date(2017, 0, 1)]) .range([0, 700]);
timeScale(new Date(2016, 0, 1)); // returns 0
timeScale(new Date(2016, 6, 1)); // returns 348.00...
timeScale(new Date(2017, 0, 1)); // returns 700

scaleSequential
scaleSequential用于将连续性的数据映射为由预定义或者定制的插值函数决定的range.(一个插值函数是一个接受0到1之间的数值而输出一个在两个数字,两个颜色值或者两个字符串之间的插值的函数)
D3提供了很多预定义的插值函数,其中包含着很多颜色相关的插值函数。例如,我们可以使用d3.interpolateRainbow来创建著名的彩虹色系图:

var sequentialScale = d3.scaleSequential()
.domain([0, 100])
.interpolator(d3.interpolateRainbow); sequentialScale(0); // returns 'rgb(110, 64, 170)'
sequentialScale(50); // returns 'rgb(175, 240, 91)'
sequentialScale(100); // returns 'rgb(110, 64, 170)'


需要注意的是插值函数决定了output range,因此你不需要对这个sequential scale来指定range
下面的列子展示由d3提供的其他颜色插值范围函数:


var linearScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 600]); var sequentialScale = d3.scaleSequential()
.domain([0, 100]); var interpolators = [
'interpolateViridis',
'interpolateInferno',
'interpolateMagma',
'interpolatePlasma',
'interpolateWarm',
'interpolateCool',
'interpolateRainbow',
'interpolateCubehelixDefault'
]; var myData = d3.range(0, 100, 2); function dots(d) {
sequentialScale
.interpolator(d3[d]); d3.select(this)
.append('text')
.attr('y', -10)
.text(d); d3.select(this)
.selectAll('rect')
.data(myData)
.enter()
.append('rect')
.attr('x', function(d) {
return linearScale(d);
})
.attr('width', 11)
.attr('height', 30)
.style('fill', function(d) {
return sequentialScale(d);
});
} d3.select('#wrapper')
.selectAll('g.interpolator')
.data(interpolators)
.enter()
.append('g')
.classed('interpolator', true)
.attr('transform', function(d, i) {
return 'translate(0, ' + (i * 70) + ')';
})
.each(dots);

除了d3定义的这些颜色插值范围函数,也有一个 d3-scale-chromatic plugin, 提供著名的 ColorBrewer colour schemes.
Clamping
默认情况下 scaleLinear, scalePow, scaleSqrt, scaleLog, scaleTime and scaleSequential 允许输入值在domain范围之外比如:

var linearScale = d3.scaleLinear() .domain([0, 10])
.range([0, 100]); linearScale(20); // returns 200
linearScale(-10); // returns -100

在这种情况下scale函数就使用外推算法来返回domain范围之外的输入值对应的返回值。
如果我们希望比例尺函数严格限制输入值必须在domain规定的范围内,我们则可以使用.clamp()调用
linearScale.clamp(true); linearScale(20); // returns 100
linearScale(-10); // returns 0
我们也可以随时通过clamp(false)来关闭这个功能
Nice
如果domain是由实际数据自动算出来的,比如使用d3.extent,d3.min/max来定义,那么起始和结束数据可能并不是整数。这本身并不是什么问题,但是如果使用这个比例尺函数来定义一个坐标轴,则显得很不整洁

var data = [0.243, 0.584, 0.987, 0.153, 0.433];
var extent = d3.extent(data); var linearScale = d3.scaleLinear()
.domain(extent)
.range([0, 100]);


我们通过使用.nice()函数,那么就将domain做了nice处理:
linearScale.nice();
需要注意的是.nice()函数必须在domain更新后每次都必须重新调用!
Multiple segments
The domain and range of scaleLinear, scalePow, scaleSqrt, scaleLog and scaleTime usually consists of two values, but if we provide 3 or more values the scale function is subdivided into multiple segments:
通常scaleLinear,scalePow,scaleSqrt,scaleLog和scaleTime比例尺的domain和range都只包含两个数值:起始和结束值来定义,但是如果我们提供3个甚至更多的值,那么比例尺函数就将被划分为几个段segments:

var linearScale = d3.scaleLinear()
.domain([-10, 0, 10])
.range(['red', '#ddd', 'blue']); linearScale(-10); // returns "rgb(255, 0, 0)"
linearScale(0); // returns "rgb(221, 221, 221)"
linearScale(5); // returns "rgb(128, 128, 255)"

看看一个例子效果:


var xScale = d3.scaleLinear()
.domain([-10, 10])
.range([0, 600]); var linearScale = d3.scaleLinear()
.domain([-10, 0, 10])
.range(['red', '#ddd', 'blue']); var myData = [-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10]; d3.select('#wrapper')
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('r', 10)
.attr('cx', function(d) {
return xScale(d);
})
.style('fill', function(d) {
return linearScale(d);
});

典型地,多segment的比例尺通常用于区分正负值(正如上面例子所示)。只要domain和range的段数是相同的,我们可以使用任意多segments的比例尺.
Inversion
.invert() 方法接受一个range输出来反算对应的input domain

var linearScale = d3.scaleLinear()
.domain([0, 10])
.range([0, 100]); linearScale.invert(50); // returns 5
linearScale.invert(100); // returns 10

A common use case is when we want to convert a user’s click along an axis into a domain value:
这个方法的典型使用场景是我们将用户沿着某坐标轴点击坐标反转为domain值:


var width = 600; var linearScale = d3.scaleLinear()
.domain([-50, 50])
.range([0, width])
.nice(); var clickArea = d3.select('.click-area').node(); function doClick() {
var pos = d3.mouse(clickArea);
var xPos = pos[0];
var value = linearScale.invert(xPos);
d3.select('.info')
.text('You clicked ' + value.toFixed(2));
} // Construct axis
var axis = d3.axisBottom(linearScale);
d3.select('.axis')
.call(axis); // Update click area size
d3.select('.click-area')
.attr('width', width)
.attr('height', 40)
.on('click', doClick);

Scales with continuous input and discrete output
scaleQuantize
scaleQuantize 接受连续性的range输入而输出由range定义的离散输出

var quantizeScale = d3.scaleQuantize()
.domain([0, 100])
.range(['lightblue', 'orange', 'lightgreen', 'pink']); quantizeScale(10); // returns 'lightblue'
quantizeScale(30); // returns 'orange'
quantizeScale(90); // returns 'pink'

Each range value is mapped to an equal sized chunk in the domain so in the example above:
每一个range值都被映射为一个domain的等分量值区间
- 0 ≤ u < 25 is mapped to ‘lightblue’
 - 25 ≤ u < 50 is mapped to ‘orange’
 - 50 ≤ u < 75 is mapped to ‘lightgreen’
 - 75 ≤ u < 100 is mapped to ‘pink’
 
u 是输入domain值

注意由于我们使用了.clamp指示,因此quantizeScale(-10)返回'lightblue',而quantizeScale(110)返回'pink'
scaleQuantile
scaleQuantile 将输入的连续性domain值映射为离散的值。domain是由数组来定义:

var myData = [0, 5, 7, 10, 20, 30, 35, 40, 60, 62, 65, 70, 80, 90, 100]; var quantileScale = d3.scaleQuantile()
.domain(myData)
.range(['lightblue', 'orange', 'lightgreen']); quantileScale(0); // returns 'lightblue'
quantileScale(20); // returns 'lightblue'
quantileScale(30); // returns 'orange'
quantileScale(65); // returns 'lightgreen'



var myData = [0, 5, 7, 10, 20, 30, 35, 40, 60, 62, 65, 70, 80, 90, 100]; var linearScale = d3.scaleLinear()
.domain([0, 100])
.range([0, 600]); var quantileScale = d3.scaleQuantile()
.domain(myData)
.range(['lightblue', 'orange', 'lightgreen']); d3.select('#wrapper')
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('r', 3)
.attr('cx', function(d) {
return linearScale(d);
})
.style('fill', function(d) {
return quantileScale(d);
});

排好序的domain数组被均分为n个子范围,这里n是range数值的个数
这样在上面的例子中domain数组就被均分为3个groups:
- the first 5 values are mapped to ‘lightblue’
 - the next 5 values to ‘orange’ and
 - the last 5 values to ‘lightgreen’.
 
具体的domain均分点可以通过.quantiles()来访问
quantileScale.quantiles(); // returns [26.66..., 63]
如果range包含4个值,那么quantileScale将这样计算quantiles: 最低25%的数据被映射为range[0], 下一个25%则映射为range[1],以此类推。
scaleThreshold
scaleThreshold 映射连续的输入domain为由range来定义的离散值. 如果range值有n个,则将会有n-1个切分点
下面的例子我们在0, 50和100处切分
- u < 0 is mapped to ‘#ccc’
 - 0 ≤ u < 50 to ‘lightblue’
 - 50 ≤ u < 100 to ‘orange’
 - u ≥ 100 to ‘#ccc’
 
这里u 是input value.

var thresholdScale = d3.scaleThreshold()
.domain([0, 50, 100])
.range(['#ccc', 'lightblue', 'orange', '#ccc']); thresholdScale(-10); // returns '#ccc'
thresholdScale(20); // returns 'lightblue'
thresholdScale(70); // returns 'orange'
thresholdScale(110); // returns '#ccc'

详细代码如下:

var linearScale = d3.scaleLinear()
.domain([-10, 110])
.range([0, 600]); var thresholdScale = d3.scaleThreshold()
.domain([0, 50, 100])
.range(['#ccc', 'lightblue', 'orange', '#ccc']); var myData = d3.range(-10, 110, 2); d3.select('#wrapper')
.selectAll('rect')
.data(myData)
.enter()
.append('rect')
.attr('x', function(d) {
return linearScale(d);
})
.attr('width', 9)
.attr('height', 30)
.style('fill', function(d) {
return thresholdScale(d);
});

Scales with discrete input and discrete output
scaleOrdinal
scaleOrdinal 将离散的domain values array映射为离散的range values array. domain input array指定可能的输入value,而range array则定义对应的可能的输出value.如果range array比domain array要短,则range array会重复循环

var myData = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] var ordinalScale = d3.scaleOrdinal()
.domain(myData)
.range(['black', '#ccc', '#ccc']); ordinalScale('Jan'); // returns 'black';
ordinalScale('Feb'); // returns '#ccc';
ordinalScale('Mar'); // returns '#ccc';
ordinalScale('Apr'); // returns 'black';


完整代码如下:

var myData = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] var linearScale = d3.scaleLinear()
.domain([0, 11])
.range([0, 600]); var ordinalScale = d3.scaleOrdinal()
.domain(myData)
.range(['black', '#ccc', '#ccc']); d3.select('#wrapper')
.selectAll('text')
.data(myData)
.enter()
.append('text')
.attr('x', function(d, i) {
return linearScale(i);
})
.text(function(d) {
return d;
})
.style('fill', function(d) {
return ordinalScale(d);
});

By default if a value that’s not in the domain is used as input, the scale will implicitly add the value to the domain:
默认情况下如果输入值不在domain范围内,scale会隐含地添加这个值到domain中去。
ordinalScale('Monday');  // returns 'black';
如果这不是我们想要的行为,我们可以使用.unknown()函数来设定一个unknown values
ordinalScale.unknown('Not a month');
ordinalScale('Tuesday'); // returns 'Not a month'
D3也提供一些预定义好的color scheme
var ordinalScale = d3.scaleOrdinal()
.domain(myData)
.range(d3.schemePaired);

var myData = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] var linearScale = d3.scaleLinear()
.domain([0, 11])
.range([0, 600]); var ordinalScale = d3.scaleOrdinal()
.domain(myData)
.range(d3.schemePaired); d3.select('#wrapper')
.selectAll('text')
.data(myData)
.enter()
.append('text')
.attr('x', function(d, i) {
return linearScale(i);
})
.text(function(d) {
return d;
})
.style('fill', function(d) {
return ordinalScale(d);
});


(Note that the Brewer colour schemes are defined within a separate file d3-scale-chromatic.js.)
scaleBand
当创建一个bar chart时,scaleBand可以帮助我们来决定bar的几何形状,并且已经考虑好了各个bar之间的padding值。
输入的domain通过一个数值数组来指定(每个值都对应一个band)并且range通过bands的最小和最大范围来定义(也就是bar chart的整个宽度)
scaleBand会将range划分为n个bands(n是domain数组的数值个数)并且在考虑padding的情况下计算出每个band的位置和宽度.

var bandScale = d3.scaleBand()
.domain(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'])
.range([0, 200]); bandScale('Mon'); // returns 0
bandScale('Tue'); // returns 40
bandScale('Fri'); // returns 160

每个band的宽度可以使用.bandWidth()来访问。
bandScale.bandwidth(); // returns 40
有两种padding可以被配置:
paddingInnerwhich specifies (as a percentage of the band width) the amount of padding between each bandpaddingOuterwhich specifies (as a percentage of the band width) the amount of padding before the first band and after the last band
我们在上面的例子中添加一点inner padding
bandScale.paddingInner(0.05); bandScale.bandWidth(); // returns 38.38...
bandScale('Mon'); // returns 0
bandScale('Tue'); // returns 40.40...
Putting this all together we can create this bar chart:
上面加起来我们可以得到下面的图表:


var myData = [
{day : 'Mon', value: 10},
{day : 'Tue', value: 40},
{day : 'Wed', value: 30},
{day : 'Thu', value: 60},
{day : 'Fri', value: 30}
]; var bandScale = d3.scaleBand()
.domain(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'])
.range([0, 200])
.paddingInner(0.05); d3.select('#wrapper')
.selectAll('rect')
.data(myData)
.enter()
.append('rect')
.attr('y', function(d) {
return bandScale(d.day);
})
.attr('height', bandScale.bandwidth())
.attr('width', function(d) {
return d.value;
});

scalePoint
scalePoint 将离散的输入数值映射为在range内等距的点:

var pointScale = d3.scalePoint()
.domain(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'])
.range([0, 500]); pointScale('Mon'); // returns 0
pointScale('Tue'); // returns 125
pointScale('Fri'); // returns 500


完整代碼:

var myData = [
{day : 'Mon', value: 10},
{day : 'Tue', value: 40},
{day : 'Wed', value: 30},
{day : 'Thu', value: 60},
{day : 'Fri', value: 30}
]; var pointScale = d3.scalePoint()
.domain(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'])
.range([0, 600]); d3.select('#wrapper')
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', function(d) {
return pointScale(d.day);
})
.attr('r', 4);

点之间的距离可以通过.step()来访问:
pointScale.step(); // returns 125
outside padding可以通过和padding to point spacing的比例来指定。比如,如果希望设定outside padding为point spacing的1/4,那么可以这样设置:
pointScale.padding(0.25);
pointScale('Mon');  // returns 27.77...
pointScale.step();  // returns 111.11...
参考阅读
d3js scales深入理解的更多相关文章
- d3js layout 深入理解
		
D3 layouts help you create more advanced visualisations such as treemaps: D3 layouts帮助您创造更加高级复杂的可视化图 ...
 - d3js shape深入理解
		
本文将视图了解d3js提供的帮助我们创建矢量图形的helper函数,比如下面的: http://d3indepth.com/shapes/ lines curves pie chart segment ...
 - d3js selections深入理解
		
D3 selections选择DOM元素以便可以对这些dom元素做相应的操作,比如:更改其style,修改其属性,执行data-join操作,或者插入.删除相应elements 比如,如果给定5个ci ...
 - d3js data joins深入理解
		
Data joins 给定一个数据数组和一个 D3 selection 我们就可以attach或者说是'join'数组中的每个数据到selection中的每个元素上. 这将使得我们的数据和可视化元素 ...
 - Angular指令渗透式理解
		
通过一段时间对angular指令的使用,理解了angular指令的意义,下面逐一介绍一下. ng-app:定义一个angualr模块,表示angular作用的范围,如下代码: ng-app在html标 ...
 - d3可视化实战02:理解d3数据驱动的真正含义
		
前文中已经提到,SVG从诞生之初起就可以非常方便地使用javascript脚本语言来进行其DOM对象的控制.当然,控制的方法有很多,有直接控制SVG对象的方法,例如使用原生js:有帮你封装一下图形接口 ...
 - D3js初探及数据可视化案例设计实战
		
摘要:本文以本人目前所做项目为基础,从设计的角度探讨数据可视化的设计的方法.过程和结果,起抛砖引玉之效.在技术方案上,我们采用通用web架构和d3js作为主要技术手段:考虑到项目需求,这里所做的可视化 ...
 - d3js可视化策略
		
d3js是数据驱动图形的思路.基本可以这么理解,有什么样的图形,后面基本就有类似结构的数据.大概思路步骤如下: 一.适配数据格式 这一步主要是为第二部服务,第一步的结果作为第二部的入参. 比如,画层级 ...
 - 你可以这么理解五种I/O模型
		
因为项目需要,接触和使用了Netty,Netty是高性能NIO通信框架,在业界拥有很好的口碑,但知其然不知其所以然. 所以本系列文章将从基础开始学起,深入细致的学习NIO.本文主要是介绍五种I/O模型 ...
 
随机推荐
- BFPRT算法
			
解决的问题:在一个数组中找到最小的k个数 常规解法:1.排序,输出前k个数,时间复杂度O(n*log(n)). 2.利用一个大小为k的大根堆,遍历数组维持大根堆,最后返回大根堆就可以了,时间复杂度O( ...
 - Spring之旅第四篇-注解配置详解
			
一.引言 最近因为找工作,导致很长时间没有更新,找工作的时候你会明白浪费的时间后面都是要还的,现在的每一点努力,将来也会给你回报的,但行好事,莫问前程!努力总不会有错的. 上一篇Spring的配置博客 ...
 - QPainterPath 不规则提示框
			
currentPosition()是最后一次绘制后的“结束点”(或初始点),使用moveTo()移动currentPosition()而不会添加任何元素. QPainterPath 合并: 1.方法 ...
 - NavUtils【底部虚拟导航栏工具类】
			
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 获取底部虚拟导航栏的高度值 效果图 代码分析 checkDeviceHasNavigationBar(Context context ...
 - 《HelloGitHub》第 28 期
			
<HelloGitHub>第 28 期 兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程. ...
 - LindAgile.SchedulingTask~设计一个不错的任务调度组件
			
回到目录 SchedulingTask产生的原因 任务调试主要指定期执行某些任务代码,之前用过quartz,感觉有些重,使用时需要添加包包,配置管理项时,对于简单的项目用它就显得有些臃肿了,不如直接上 ...
 - 4.4管道和中间件介绍「深入浅出ASP.NET Core系列」
			
希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,谢谢关注. 管道流 我们知道一个管道可以有一个或多个中间件,而中间件的职责是根据HttpContext处理HTTP请求,然后往 ...
 - Flutter map 妙用及 .. 使用
			
前言 本篇文章对于熟悉 flutter 或者 dart 的小伙伴来说可能觉得比较简单,但是对于初学者或者没用过的小伙伴还是有些收获的. 背景 说到 map 妙用的发现,还要归功于 Tooltip 的研 ...
 - C#保留2位小数几种场景总结
			
场景1: C#保留2位小数,.ToString("f2")确实可以,但是如果这个数字本来就小数点后面三位比如1.253,那么转化之后就会变成1.25.可不可以刚好保留到最后一位不是 ...
 - 基于IIS的WCF
			
(1)创建WCF服务应用程序 (2)配置IIS 将WCF服务应用程序配置IIS网站,需要使用.net4.0集成版本的程序池 (3)使用SvcUtil.exe生成客户端代码和配置 SvcUtil.exe ...
 
			
		