(function() { const dom = document.getElementById('chart-vector-field'); const width = dom.clientWidth || 800; const height = 500; const margin = { top: 20, right: 30, bottom: 40, left: 50 }; const innerW = width - margin.left - margin.right; const innerH = height - margin.top - margin.bottom; const svg = d3.select(dom).append('svg').attr('width', width).attr('height', height); const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`); // 生成网格向量场数据:每个格点有 (dx, dy) const cols = 15, rows = 12; const xScale = d3.scaleLinear().domain([0, cols - 1]).range([0, innerW]); const yScale = d3.scaleLinear().domain([0, rows - 1]).range([innerH, 0]); const vectors = []; const cx = (cols - 1) / 2, cy = (rows - 1) / 2; for (let i = 0; i < cols; i++) { for (let j = 0; j < rows; j++) { // 模拟一个源点向外辐射的场 const dx = i - cx, dy = j - cy; const dist = Math.sqrt(dx * dx + dy * dy) || 1; const mag = Math.min(8 / dist, 1.5); vectors.push({ x: i, y: j, dx: dx / dist * mag, dy: dy / dist * mag, mag }); } } const magExtent = d3.extent(vectors, d => d.mag); const colorScale = d3.scaleSequential(d3.interpolateViridis).domain(magExtent); const maxMag = magExtent[1]; const cellW = innerW / (cols - 1), cellH = innerH / (rows - 1); const arrowLen = Math.min(cellW, cellH) * 0.35; // 箭头定义 svg.append('defs').append('marker') .attr('id', 'vf-arrow').attr('viewBox', '0 -4 8 8') .attr('refX', 7).attr('refY', 0).attr('markerWidth', 5).attr('markerHeight', 5) .attr('orient', 'auto') .append('path').attr('d', 'M0,-4L8,0L0,4').attr('fill', '#475569'); g.selectAll('.vector').data(vectors).join('line') .attr('x1', d => xScale(d.x)).attr('y1', d => yScale(d.y)) .attr('x2', d => xScale(d.x) + d.dx / maxMag * arrowLen) .attr('y2', d => yScale(d.y) - d.dy / maxMag * arrowLen) .attr('stroke', d => colorScale(d.mag)) .attr('stroke-width', d => d.mag / maxMag * 3 + 0.5) .attr('marker-end', 'url(#vf-arrow)'); window.addEventListener('resize', () => { const w = dom.clientWidth || 800; svg.attr('width', w); xScale.range([0, w - margin.left - margin.right]); }); })();