/* v3 比例尺 */
d3.scale.linear()
/* v4 比例尺 */
d3.scaleLinear()
墨卡托投影绘制的地球及风场动画的制作:帕特森投影绘制的地球及风场动画:
// 返回匹配选择器的第一个元素
d3.select('.class')
// 返回匹配选择器的所有元素
d3.selectAll('.class')
var data = [{value: 11}, {value: 12}]
// 选择集的每一个元素都绑定相同的数据
selection.datum(data)
// 选择集的每一个元素都绑定values的每一项
selection.data(data)
// 颜色插值
var compute = d3.interpolate(d3.rgb(255, 255, 153), d3.rgb(255, 204, 102));
// 颜色比例尺
var linear = d3.scaleLinear()
.domain([0, 10])
.range([0, 1]);
// 根据数据计算圆心x坐标
function calCX(d, i) {
return i * 30 + 5
}
// 根据数据计算圆心y坐标
function calCY(d) {
return (d - 1) * 30 + 5
}
// 根据数据计算圆形半径
function calCR(d){
return d * 2.5
}
// 根据数据计算填充颜色
function calFill(d){
return compute(linear(d))
}
// 根据data绘制图形
function render(data){
var circles = d3.select('#target').selectAll('circle').data(data)
// 处理待更新的数据集
circles.style('fill', 'rgb(255,102,102)')
.attr('r', calCR)
.attr('cx', calCX)
.attr('cy', calCY)
// 处理新添加的数据集
circles.enter()
.append('circle')
.style('fill', calFill)
.attr('r', calCR)
.attr('cx', calCX)
.attr('cy', calCY)
// 处理已退出的数据集
circles.exit()
.remove()
}
// 添加Svg画布
var svg = d3.select('body')
.append("svg")
.attr("id", "target")
.attr("width", "300px")
.attr("height", "300px")
.attr("style", "border:1px solid black")
// 绘制数据
render([1, 5, 10])
render([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
运行结果如下:
// 数据处理
var hierarchy = d3.hierarchy(dataset)
console.log('hierarchy', hierarchy)
打印结果如下:
// 将数据转换为绘制所需要的数据格式
var createTree = d3.tree()
.size([200, 200])
.separation(function(a, b){ return a.parent == b.parent? 1: 2})
var tree = createTree(hierarchy)
console.log('tree', tree)
// 生成节点所需数据
var descendants = tree.descendants()
console.log('descendants', descendants)
// 生成连线所需数据
var links = tree.links()
console.log('links', links)
打印结果如下:
经过绘制:
// 创建各种格式的颜色
var color = d3.color('steelblue')
// D3提供的颜色主题 配合比例尺进行绘制
var scheme = d3.schemeCategory10
var scale = d3.scaleOrdinal(scheme)]
经过绘制:
// 饼状图布局(数据转换)生成arc所需要的数据格式
var createPie = d3.pie()
.value(function(d){ return d.value })
var pie = createPie(dataset)
// 路经生成器
var arc = d3.arc()
.innerRadius(0)
.outerRadius(142)
经过绘制:
// 线性比例尺
var linear = d3.scaleLinear()
.domain([10, 130])
.range([0, 960])
linear(20) // 80
linear(50) // 320
// 坐标轴比例尺
var xScale = d3.scaleLinear()
.domain([-50, 50])
.range([0, 300])
var yScale = d3.scaleLinear()
.domain([-50, 50])
.range([300, 0])
// x、y坐标轴
var xAxis = d3.axisBottom(xScale)
.ticks(11)
var yAxis = d3.axisLeft(yScale)
.ticks(11)
经过绘制:
示例代码如下,省略了部分工具函数代码。
// D3-geo模块投影相关功能(将笛卡尔坐标系的坐标投影至其他坐标系)
const projection = d3.geoOrthographic()
.rotate([0, 40])
.translate([250, 250])
.fitExtent([[20,20], [500-20, 500-20]], {type:"Sphere"})
// 利用Canvas的context2D绘制三角形
function drawTriangle([p0, p1, p2]){
context.moveTo(...p0)
context.lineTo(...p1)
context.lineTo(...p2)
context.closePath()
}
// 获得可绘制的三角形面坐标
const faces = geodesic(10)
// 渲染函数,可增加设置投影代码,进而增加动效。
function render(){
const triangles = faces.map((d,i)=>(d=d.map(projection),d.index = i, d))
.filter(d=>d3.polygonArea(d) < 0)
context.clearRect(0,0,500,500)
for(const t of triangles){
context.beginPath()
drawTriangle(t)
context.fillStyle = d3.interpolateRainbow(faces[t.index][0][0] / 360);
context.fill()
}
context.canvas
}
render()
经过绘制:
示例代码如下(齿轮转动)。
// 初始参数设置
let angle = 0
let frameAngle = 0
let x = 0.8660254037844387
let y = -0.499999999999999
let toothRadius = 0.008
let holeRadius = 0.02
let speed = 0.05
gears = [
{fill: "rgb(142,211,199)", teeth: 80, radius: -0.5, origin: [0, 0], annulus: true},
{fill: "rgb(252,180,98)", teeth: 16, radius: +0.1, origin: [0,0]},
{fill: "rgb(189,186,217)", teeth: 32, radius: -0.2, origin: [0, -0.3]},
{fill: "rgb(250,129,113)", teeth: 32, radius: -0.2, origin: [-0.3 * x, -0.3 * y]},
{fill: "rgb(127,178,211)", teeth: 32, radius: -0.2, origin: [0.3 * x, -0.3 * y]}
]
// 生成齿轮路经
function gear({teeth, radius, annulus, origin}) {
const n = teeth
let r2 = Math.abs(radius)
let r0 = r2 - toothRadius
let r1 = r2 + toothRadius
let r3 = holeRadius
if(annulus) {
r3 = r0
r0 = r1
r1 = r3
r3 = r2 + toothRadius * 3
}
const da = Math.PI / n
let a0 = -Math.PI / 2 + (annulus ? Math.PI / n : 0)
const path = ["M", r0 * Math.cos(a0), ",", r0 * Math.sin(a0)]
let i = -1
while(++i < n) {
path.push(
"A", r0, ",", r0, " 0 0,1", r0 * Math.cos(a0 += da), ",", r0 * Math.sin(a0),
"L", r2 * Math.cos(a0), ",", r2 * Math.sin(a0),
"L", r1 * Math.cos(a0 += da/3), ",", r1 * Math.sin(a0),
"A", r1, ",", r1, " 0 0,1", r1 * Math.cos(a0 += da / 3), "," , r1 * Math.sin(a0),
"L", r2 * Math.cos(a0 += da / 3), ",", r2 * Math.sin(a0),
"L", r0 * Math.cos(a0), "," , r0*Math.sin(a0)
)
}
path.push("M0,", -r3, "A", r3, ",", r3, " 0 0,0 0,", r3, "A", r3, ",", r3, " 0 0,0 0,", -r3, "2")
return path.join("")
}
// 挂载Svg
const svg = d3.select("body").append("svg")
.attr("viewBox", [-0.53, -0.53, 1.06, 1.06])
.attr("stroke", "black")
.attr("stroke-width", 1 / 640)
.attr("max-width", "640px")
.style("display", "block")
.style("margin", "auto")
// 绘制
const frame = svg.append("g")
.attr("transform", `rotate(${frameAngle % 360})`);
const path = frame.selectAll("path")
.data(gears)
.join("path")
.attr("fill", d=>d.fill)
.attr("d", gear)
.attr("transform", d=>`translate(${d.origin}) rotate(${(angle/d.radius) % 360})`)
// 动画
function update(){
path.attr("transform", d=>`translate(${d.origin}) rotate(${(angle/d.radius) % 360})`)
angle += speed
frameAngle += speed
window.requestAnimationFrame(update)
}
update()
经过绘制: