目录
简介
getting started
npm install d3
<!DOCTYPE html>
<div id="container"></div>
<script type="module">
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
// Declare the chart dimensions and margins.
const width = 640;
const height = 400;
const marginTop = 20;
const marginRight = 20;
const marginBottom = 30;
const marginLeft = 40;
// Declare the x (horizontal position) scale.
const x = d3.scaleUtc()
.domain([new Date("2023-01-01"), new Date("2024-01-01")])
.range([marginLeft, width - marginRight]);
// Declare the y (vertical position) scale.
const y = d3.scaleLinear()
.domain([0, 100])
.range([height - marginBottom, marginTop]);
// Create the SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
// Add the x-axis.
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x));
// Add the y-axis.
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y));
// Append the SVG element.
container.append(svg.node());
</script>
demo
tree组件
import Tree from '@components/react/d3/tree'
<Tree client:load/>
齿轮组件
tsx component
import * as d3 from "d3";
import { useEffect, useRef } from "react"
const Gear = () => {
const ref = useRef<SVGSVGElement | null>(null);
function gear({ teeth, radius, annulus, toothRadius, holeRadius }: any) {
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, "Z");
return path.join("");
}
const graphic = () => {
const x = Math.sin(2 * Math.PI / 3);
const y = Math.cos(2 * Math.PI / 3);
const svgElement = d3.select(ref.current);
const svg = svgElement
.attr("width", 640)
.attr("viewBox", [-0.53, -0.53, 1.06, 1.06])
.attr("stroke", "black")
.attr("stroke-width", 1 / 640)
.attr("style", `width:${100}vw;max-width: 100%; height: calc( 100vh - 100px);`);
const frame = svg.append("g");
const path = frame.selectAll()
.data([
{ fill: "#c6dbef", teeth: 80, radius: -0.5, origin: [0, 0], annulus: true },
{ fill: "#6baed6", teeth: 16, radius: +0.1, origin: [0, 0] },
{ fill: "#9ecae1", teeth: 32, radius: -0.2, origin: [0, -0.3] },
{ fill: "#9ecae1", teeth: 32, radius: -0.2, origin: [-0.3 * x, -0.3 * y] },
{ fill: "#9ecae1", teeth: 32, radius: -0.2, origin: [0.3 * x, -0.3 * y] }
])
.join("path")
.attr("fill", d => d.fill)
.attr("d", d => gear({ ...d, toothRadius: 0.008, holeRadius: 0.02 }));
Object.assign(svg.node() as SVGSVGElement, {
update({ angle, frameAngle }: { angle: number, frameAngle: number }) {
path.attr("transform", d => `translate(${d.origin}) rotate(${(angle / d.radius) % 360})`);
frame.attr("transform", `rotate(${frameAngle % 360})`);
}
});
}
useEffect(() => {
graphic();
let id = 0;
const animate = () => {
const speed = 0.08;
let angle = 0;
let frameAngle = 0;
let start = 0;
const draw = function (timestamp: number) {
if (start === 0) {
start = timestamp;
}
(d3.select(ref.current).node() as any).update({ angle, frameAngle });
angle += speed;
frameAngle += speed / 360;
id = requestAnimationFrame(draw)
}
id = requestAnimationFrame(draw)
}
animate();
return () => {
cancelAnimationFrame(id);
}
})
return (
<svg ref={ref} >
</svg>
)
}
export default Gear;
数据可视化
html + css
<div class="relative w-full border-solid border-lightgrey bg-skin-fill border-[1px]">
<div class="w-[95%] bg-skin-accent h-3"/>
</div>
css-doodle
@grid: 1 / 100% 100vmin / #0a0c27; background-size: 200px 200px; background-image: @doodle( @grid: 6 / 100%; @size: 4px; font-size: 4px; color: hsl(@r240, 30%, 50%); box-shadow: @m3x5( calc(4em - @nx * 1em) calc(@ny * 1em) @p(@m3(currentColor), @m2(transparent)), calc(2em + @nx * 1em) calc(@ny * 1em) @lp ); );
import Common from '@components/react/doodle/common.tsx';
<Common client:load>
@grid: 1 / 100vmin 100vmin / #0a0c27;
background-size: 200px 200px;
background-image: @doodle(
@grid: 6 / 100%;
@size: 4px;
font-size: 4px;
color: hsl(@r240, 30%, 50%);
box-shadow: @m3x5(
calc(4em - @nx * 1em) calc(@ny * 1em)
@p(@m3(currentColor), @m2(transparent)),
calc(2em + @nx * 1em) calc(@ny * 1em)
@lp
);
);
</Common>
@grid: 1x1 / 100% 600px; gap: 2px; background: conic-gradient( at @r(30%, 70%) 0, @stripe( transparent 25%, @m32.@p( #781217, #9A1320, #0A2667, #D8A088, #D6DDD1, #E95A22, #CA4122, #C74523, #2CACC9, #008C3B, #F5D700, #CA2821, #EADF93, #003A59, #00609A, #6E746E, #E2E0B8, #1E272B, #468495, #E1E0CA, #666F75, #84A16A, #413B4E, #98AEC7, #979DA4 ), transparent 25% ) );
webgl
svg
d3基础
d3.arc() 弧
practice-arc
import * as d3 from "d3";
import { useEffect, useRef } from "react"
export default () => {
const ref = useRef<SVGSVGElement | null>(null);
useEffect(() => {
const svgElement = d3.select(ref.current);
const g = svgElement
.attr("width", "100")
.attr("height", "100")
.append("path")
g.attr("d", d3.arc()({
innerRadius: 19,
outerRadius: 40,
startAngle: -Math.PI / 2,
endAngle: Math.PI / 2
}))
.attr("fill", "cornflowerblue")
.attr("style", "transform: translate(50%, 50%)"
)
})
return (
<svg ref={ref} >
</svg>
)
}
d3.axisBottom() 底部坐标轴
practice-axis-bottom
import * as d3 from "d3";
import { useEffect, useRef } from "react"
const Template = () => {
const ref = useRef<SVGSVGElement | null>(null);
useEffect(() => {
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([10, 290])
const svgElement = d3.select(ref.current).attr("style", "width:100%;max-height:4em");
const axisGenerator = d3.axisBottom(xScale);
svgElement.append("g")
.call(axisGenerator)
return () => {
svgElement.remove();
}
})
return (
<svg ref={ref} >
</svg>
)
}
export default Template;