223 lines
7.0 KiB
JavaScript
223 lines
7.0 KiB
JavaScript
/* eslint-disable */
|
|
|
|
import * as d3 from 'd3';
|
|
import { globalSetting } from '../global';
|
|
import { NetlistRender } from '.';
|
|
import { LAYOUT_CONSTANT, LINE_WIDTH } from './layout';
|
|
import { svgResource } from '../skin/draw';
|
|
|
|
export class WireRender {
|
|
/**
|
|
*
|
|
* @param {d3.Selection} selection
|
|
* @param {NetlistRender} rootRender
|
|
*/
|
|
constructor(selection, rootRender) {
|
|
this.selection = selection;
|
|
this.rootRender = rootRender;
|
|
|
|
/**
|
|
* @type {BasicD3DataItem[]}
|
|
*/
|
|
this.data = [];
|
|
|
|
/**
|
|
* @description id 到管理数据项的映射
|
|
* @type {Map<string, BasicD3ManagmentItem[]>}
|
|
*/
|
|
this.id2manager = rootRender.id2manager;
|
|
|
|
this.arrowHeight = 12;
|
|
this.arrowWidth = 12;
|
|
}
|
|
|
|
/**
|
|
* @description 将 elknode 关于 wire 的数据添加为 d3 数据项目
|
|
* @param {ElkPoint[]} points
|
|
* @param {ElkEdge} edge
|
|
*/
|
|
addAsD3DataItems(points, edge) {
|
|
const linePaths = [];
|
|
for (let i = 0; i < points.length; ++ i) {
|
|
// 根据点的信息创建 path
|
|
const command = i === 0 ? 'M': 'L';
|
|
const x = points[i].x;
|
|
const y = points[i].y;
|
|
linePaths.push(`${command}${x},${y}`);
|
|
}
|
|
|
|
const lineSvg = linePaths.join(' ');
|
|
|
|
// 判断当前的朝向
|
|
const endPoint = points.at(-1);
|
|
const direction = endPoint.x > points.at(-2).x ? 'right' : 'left';
|
|
const arrowX = direction === 'right' ? endPoint.x - LAYOUT_CONSTANT.CELL_PORT_WIDTH - this.arrowWidth:
|
|
endPoint.x + LAYOUT_CONSTANT.CELL_PORT_WIDTH
|
|
|
|
const arrowY = endPoint.y - this.arrowHeight / 2;
|
|
|
|
|
|
this.data.push({
|
|
svg: lineSvg,
|
|
endPoint: points.at(-1),
|
|
arrow: {
|
|
icon: direction + '-arrow',
|
|
x: arrowX,
|
|
y: arrowY,
|
|
width: this.arrowWidth,
|
|
height: this.arrowHeight
|
|
},
|
|
active: false
|
|
});
|
|
}
|
|
|
|
render() {
|
|
const data = this.data;
|
|
const id2manager = this.id2manager;
|
|
const _this = this;
|
|
|
|
const arrowHeight = 12;
|
|
const arrowWidth = 12;
|
|
|
|
|
|
let lineSelections = this.selection.selectAll('path.lines')
|
|
.data(data)
|
|
.enter()
|
|
.append('path')
|
|
.attr('d', d => d.svg)
|
|
.attr('class', 'connection-line')
|
|
.attr("fill", "none")
|
|
.attr('stroke', 'var(--wire-color)');
|
|
|
|
let arrowSelections = this.selection.selectAll('svg.line-arrow')
|
|
.data(data)
|
|
.enter()
|
|
.append(data => {
|
|
const arrowInfo = data.arrow;
|
|
|
|
// 获取箭头 svg
|
|
const element = svgResource.get(arrowInfo.icon);
|
|
|
|
element.setAttribute('x', arrowInfo.x);
|
|
element.setAttribute('y', arrowInfo.y);
|
|
element.setAttribute('width', arrowInfo.width);
|
|
element.setAttribute('height', arrowInfo.height);
|
|
element.setAttribute('opacity', 'var(--line-arrow-opacity)');
|
|
|
|
return element;
|
|
});
|
|
|
|
arrowSelections.raise();
|
|
|
|
const id2animation = new Map();
|
|
|
|
lineSelections.on('click', function(_, data) {
|
|
data.active = !data.active;
|
|
|
|
if (data.active) {
|
|
const pathSelection = d3.select(this);
|
|
// 如果当前激活,显示数据流向
|
|
const pathLength = pathSelection.node().getTotalLength();
|
|
|
|
const pivot = _this.selection
|
|
.append('circle')
|
|
.attr('r', 6)
|
|
.attr('fill', 'var(--wire-ball-color)');
|
|
|
|
// 进行一次动画
|
|
function renderOneAnimation() {
|
|
console.log(pathLength);
|
|
// 1400 的长度差不多配 3500 ?
|
|
const duration = pathLength / 14 * 35;
|
|
let transition = pivot.transition().duration(duration);
|
|
transition.attrTween('transform', () => {
|
|
return t => {
|
|
const x = cubicBezierAnimation(t, 0, pathLength);
|
|
const point = pathSelection.node().getPointAtLength(x);
|
|
return `translate(${point.x},${point.y})`;
|
|
}
|
|
});
|
|
transition = transition.on('end', function() {
|
|
if (id2animation.has(data.id)) {
|
|
renderOneAnimation();
|
|
}
|
|
});
|
|
// 动画结束后重新启动动画
|
|
id2animation.set(data.id, {
|
|
pivot,
|
|
transition
|
|
});
|
|
}
|
|
|
|
renderOneAnimation();
|
|
} else {
|
|
if (id2animation.has(data.id)) {
|
|
const { pivot, transition } = id2animation.get(data.id);
|
|
pivot.remove();
|
|
id2animation.delete(data.id);
|
|
}
|
|
}
|
|
});
|
|
|
|
lineSelections.on('mouseenter', function(_, data) {
|
|
const selection = d3.select(this);
|
|
// 移动到最上层
|
|
selection.raise();
|
|
selection.attr('stroke', 'var(--wire-active-color)');
|
|
});
|
|
|
|
lineSelections.on('mouseleave', function(_, data) {
|
|
const selection = d3.select(this);
|
|
selection.attr('stroke', 'var(--wire-color)');
|
|
});
|
|
|
|
|
|
if (globalSetting.renderAnimation) {
|
|
lineSelections = lineSelections
|
|
.transition()
|
|
.duration(1000);
|
|
}
|
|
|
|
lineSelections
|
|
.attr('stroke-width', LINE_WIDTH)
|
|
.each(function (data) {
|
|
const selection = d3.select(this);
|
|
// const manager = _this.createDataManager(selection, data);
|
|
});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {d3.Selection} selection
|
|
* @param {BasicD3DataItem} data
|
|
* @returns {BasicD3ManagmentItem}
|
|
*/
|
|
createDataManager(selection, data) {
|
|
const id2manager = this.id2manager;
|
|
// wire 不需要拖拽上下文
|
|
const managerItem = {
|
|
data,
|
|
selection,
|
|
type: 'wire'
|
|
};
|
|
|
|
if (!id2manager.has(data.id)) {
|
|
id2manager.set(data.id, []);
|
|
}
|
|
|
|
id2manager.get(data.id).push(managerItem);
|
|
return managerItem;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @description 计算贝塞尔插值,输入 0 - 1 的值 (delta),输出 [oldVal, newVal] 中间的插值
|
|
* @param {*} delta
|
|
* @param {*} oldVal
|
|
* @param {*} newVal
|
|
* @returns
|
|
*/
|
|
function cubicBezierAnimation(delta, oldVal, newVal) {
|
|
delta = 3 * (1 - delta) * (1 - delta) * delta + 3 * (1 - delta) * delta * delta + delta * delta * delta;
|
|
return (1 - delta) * oldVal + delta * newVal;
|
|
} |