实现连接线的动画
This commit is contained in:
parent
f36fd94cbb
commit
3e2c7e3ae0
@ -16,9 +16,11 @@
|
|||||||
--instance-color: #CB81DA;
|
--instance-color: #CB81DA;
|
||||||
--instance-fill-color: rgba(203, 129, 218, 0.1);
|
--instance-fill-color: rgba(203, 129, 218, 0.1);
|
||||||
--wire-color: var(--foreground);
|
--wire-color: var(--foreground);
|
||||||
|
--wire-active-color: #CB81DA;
|
||||||
|
--wire-ball-color: #CB81DA;
|
||||||
--cross-dot-color: var(--foreground);
|
--cross-dot-color: var(--foreground);
|
||||||
--port-color: rgb(70, 70, 222);
|
--port-color: rgb(70, 70, 222);
|
||||||
--port-fill-color: rgba(70, 70, 222, 0.1);
|
--port-fill-color: rgba(70, 70, 222, 0.25);
|
||||||
|
|
||||||
/* css 动画属性 */
|
/* css 动画属性 */
|
||||||
--animation-7s: .7s cubic-bezier(0.23,1,0.32,1);
|
--animation-7s: .7s cubic-bezier(0.23,1,0.32,1);
|
||||||
|
@ -53,4 +53,9 @@ onMounted(async () => {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.connection-line {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--animation-5s);
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
@ -31,6 +31,12 @@ export const colorManager = reactive({
|
|||||||
type: 'cross-dot',
|
type: 'cross-dot',
|
||||||
label: t('cross-dot'),
|
label: t('cross-dot'),
|
||||||
color: 'white'
|
color: 'white'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 4,
|
||||||
|
type: 'wire-active',
|
||||||
|
label: t('common.selected-wire'),
|
||||||
|
color: 'white'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -37,7 +37,9 @@ export class NetlistRender {
|
|||||||
// 指定 layered 算法的方向为从左到右
|
// 指定 layered 算法的方向为从左到右
|
||||||
'elk.direction': 'RIGHT',
|
'elk.direction': 'RIGHT',
|
||||||
// 激活 node 分区功能,可以通过 partitioning.partition 赋予 id 的方式给 node 进行分组
|
// 激活 node 分区功能,可以通过 partitioning.partition 赋予 id 的方式给 node 进行分组
|
||||||
'elk.partitioning.activate': true
|
'elk.partitioning.activate': true,
|
||||||
|
// 边之间的距离
|
||||||
|
'elk.spacing.edgeEdge': 25
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @type {ElkGraph}
|
* @type {ElkGraph}
|
||||||
@ -217,10 +219,10 @@ export class NetlistRender {
|
|||||||
// 根据最大最小尺寸微调全局方位
|
// 根据最大最小尺寸微调全局方位
|
||||||
this.adjustLocation(g);
|
this.adjustLocation(g);
|
||||||
|
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// 生成各个 manager 的拖拽上下文
|
// // 生成各个 manager 的拖拽上下文
|
||||||
this.createManagerDragContext(computedLayout);
|
// this.createManagerDragContext(computedLayout);
|
||||||
}, 1000);
|
// }, 1000);
|
||||||
|
|
||||||
return svg;
|
return svg;
|
||||||
}
|
}
|
||||||
@ -276,7 +278,7 @@ export class NetlistRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 绘制连线
|
* @description 绘制连线和交叉点
|
||||||
* @param {d3.Selection} parentSelection
|
* @param {d3.Selection} parentSelection
|
||||||
* @param {ElkNode} computedLayout
|
* @param {ElkNode} computedLayout
|
||||||
*/
|
*/
|
||||||
|
@ -66,9 +66,6 @@ export class PortRender {
|
|||||||
.attr('dominant-baseline', 'middle') // 文本垂直居中
|
.attr('dominant-baseline', 'middle') // 文本垂直居中
|
||||||
.attr('text-anchor', 'middle') // 文本水平居中
|
.attr('text-anchor', 'middle') // 文本水平居中
|
||||||
.attr('fill', 'var(--foreground)') // 文本颜色
|
.attr('fill', 'var(--foreground)') // 文本颜色
|
||||||
.attr('font-size', '0')
|
|
||||||
.transition()
|
|
||||||
.duration(1000)
|
|
||||||
.attr('font-size', '12px')
|
.attr('font-size', '12px')
|
||||||
.attr('class', 'port-caption')
|
.attr('class', 'port-caption')
|
||||||
.text(data => data.text); // 设置文本内容
|
.text(data => data.text); // 设置文本内容
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
import { globalSetting } from '../global';
|
import { globalSetting } from '../global';
|
||||||
import { NetlistRender } from '.';
|
import { NetlistRender } from '.';
|
||||||
|
import { LINE_WIDTH } from './layout';
|
||||||
|
|
||||||
export class WireRender {
|
export class WireRender {
|
||||||
/**
|
/**
|
||||||
@ -30,16 +33,21 @@ export class WireRender {
|
|||||||
* @param {ElkEdge} edge
|
* @param {ElkEdge} edge
|
||||||
*/
|
*/
|
||||||
addAsD3DataItems(points, edge) {
|
addAsD3DataItems(points, edge) {
|
||||||
for (let i = 0; i < points.length - 1; ++ i) {
|
const linePaths = [];
|
||||||
this.data.push({
|
for (let i = 0; i < points.length; ++ i) {
|
||||||
id: edge.id,
|
// 根据点的信息创建 path
|
||||||
x1: points[i].x,
|
const command = i === 0 ? 'M': 'L';
|
||||||
y1: points[i].y,
|
const x = points[i].x;
|
||||||
x2: points[i + 1].x,
|
const y = points[i].y;
|
||||||
y2: points[i + 1].y,
|
linePaths.push(`${command}${x},${y}`);
|
||||||
strokeWidth: 2
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lineSvg = linePaths.join(' ');
|
||||||
|
|
||||||
|
this.data.push({
|
||||||
|
svg: lineSvg,
|
||||||
|
active: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -47,16 +55,102 @@ export class WireRender {
|
|||||||
const id2manager = this.id2manager;
|
const id2manager = this.id2manager;
|
||||||
const _this = this;
|
const _this = this;
|
||||||
|
|
||||||
let lineSelections = this.selection.selectAll('line')
|
|
||||||
|
// 一个 g.lines 管理一个线段
|
||||||
|
let lineSelections = this.selection.selectAll('path.lines')
|
||||||
.data(data)
|
.data(data)
|
||||||
.enter()
|
.enter()
|
||||||
.append('line')
|
.append('path')
|
||||||
.attr('x1', data => data.x1)
|
.attr('d', d => d.svg)
|
||||||
.attr('y1', data => data.y1)
|
.attr('class', 'connection-line')
|
||||||
.attr('x2', data => data.x2)
|
.attr("fill", "none")
|
||||||
.attr('y2', data => data.y2)
|
|
||||||
.attr('stroke', 'var(--wire-color)');
|
.attr('stroke', 'var(--wire-color)');
|
||||||
|
|
||||||
|
|
||||||
|
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', 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)');
|
||||||
|
});
|
||||||
|
|
||||||
|
// linesContainer.each(function(lines) {
|
||||||
|
// // 为每一个线段分别创建对应的对象
|
||||||
|
// const lineSelection = d3.select(this)
|
||||||
|
// .selectAll('.line')
|
||||||
|
// .data(lines)
|
||||||
|
// .enter()
|
||||||
|
// .append('line')
|
||||||
|
// .attr('x1', data => data.x1)
|
||||||
|
// .attr('y1', data => data.y1)
|
||||||
|
// .attr('x2', data => data.x2)
|
||||||
|
// .attr('y2', data => data.y2)
|
||||||
|
// .attr('stroke', 'var(--wire-color)');
|
||||||
|
|
||||||
|
// lineSelection.on('mouseenter', function(_, data) {
|
||||||
|
// console.log('enter enter');
|
||||||
|
// });
|
||||||
|
|
||||||
|
// lineSelection.on('mouseleave', function(_, data) {
|
||||||
|
// console.log('enter leave');
|
||||||
|
// });
|
||||||
|
|
||||||
|
// lineSelection.on('click', function(_, data) {
|
||||||
|
// console.log('click');
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
if (globalSetting.renderAnimation) {
|
if (globalSetting.renderAnimation) {
|
||||||
lineSelections = lineSelections
|
lineSelections = lineSelections
|
||||||
.transition()
|
.transition()
|
||||||
@ -64,7 +158,7 @@ export class WireRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lineSelections
|
lineSelections
|
||||||
.attr('stroke-width', data => data.strokeWidth)
|
.attr('stroke-width', LINE_WIDTH)
|
||||||
.each(function (data) {
|
.each(function (data) {
|
||||||
const selection = d3.select(this);
|
const selection = d3.select(this);
|
||||||
// const manager = _this.createDataManager(selection, data);
|
// const manager = _this.createDataManager(selection, data);
|
||||||
@ -94,3 +188,15 @@ export class WireRender {
|
|||||||
return 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;
|
||||||
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "المنفذ",
|
"common.port": "المنفذ",
|
||||||
"common.instance": "تجهيز الوحدة",
|
"common.instance": "تجهيز الوحدة",
|
||||||
"setting.general-color-setting": "إعدادات الألوان العامة",
|
"setting.general-color-setting": "إعدادات الألوان العامة",
|
||||||
"cross-dot": "تقاطع"
|
"cross-dot": "تقاطع",
|
||||||
|
"common.selected-wire": "الخط المحدد"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "Port",
|
"common.port": "Port",
|
||||||
"common.instance": "Modul instanziieren",
|
"common.instance": "Modul instanziieren",
|
||||||
"setting.general-color-setting": "Allgemeine Farbeinstellungen",
|
"setting.general-color-setting": "Allgemeine Farbeinstellungen",
|
||||||
"cross-dot": "Kreuzung"
|
"cross-dot": "Kreuzung",
|
||||||
|
"common.selected-wire": "Ausgewählte Linie"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "Port",
|
"common.port": "Port",
|
||||||
"common.instance": "Instantiate module",
|
"common.instance": "Instantiate module",
|
||||||
"setting.general-color-setting": "General Color Settings",
|
"setting.general-color-setting": "General Color Settings",
|
||||||
"cross-dot": "Intersection"
|
"cross-dot": "Intersection",
|
||||||
|
"common.selected-wire": "Selected line"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "Port",
|
"common.port": "Port",
|
||||||
"common.instance": "Instancier le module",
|
"common.instance": "Instancier le module",
|
||||||
"setting.general-color-setting": "Paramètres de couleur généraux",
|
"setting.general-color-setting": "Paramètres de couleur généraux",
|
||||||
"cross-dot": "Intersection"
|
"cross-dot": "Intersection",
|
||||||
|
"common.selected-wire": "Ligne sélectionnée"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "ポート",
|
"common.port": "ポート",
|
||||||
"common.instance": "モジュールのインスタンス化",
|
"common.instance": "モジュールのインスタンス化",
|
||||||
"setting.general-color-setting": "一般的な色設定",
|
"setting.general-color-setting": "一般的な色設定",
|
||||||
"cross-dot": "交差点"
|
"cross-dot": "交差点",
|
||||||
|
"common.selected-wire": "選択された線"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "포트",
|
"common.port": "포트",
|
||||||
"common.instance": "모듈 인스턴스화",
|
"common.instance": "모듈 인스턴스화",
|
||||||
"setting.general-color-setting": "일반 색상 설정",
|
"setting.general-color-setting": "일반 색상 설정",
|
||||||
"cross-dot": "교차로"
|
"cross-dot": "교차로",
|
||||||
|
"common.selected-wire": "선택된 선"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "Порт",
|
"common.port": "Порт",
|
||||||
"common.instance": "Создание экземпляра модуля",
|
"common.instance": "Создание экземпляра модуля",
|
||||||
"setting.general-color-setting": "Общие настройки цвета",
|
"setting.general-color-setting": "Общие настройки цвета",
|
||||||
"cross-dot": "Перекресток"
|
"cross-dot": "Перекресток",
|
||||||
|
"common.selected-wire": "Выбранная линия"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "端口",
|
"common.port": "端口",
|
||||||
"common.instance": "例化模块",
|
"common.instance": "例化模块",
|
||||||
"setting.general-color-setting": "通用颜色设置",
|
"setting.general-color-setting": "通用颜色设置",
|
||||||
"cross-dot": "交叉点"
|
"cross-dot": "交叉点",
|
||||||
|
"common.selected-wire": "被选中的线"
|
||||||
}
|
}
|
@ -24,5 +24,6 @@
|
|||||||
"common.port": "端口",
|
"common.port": "端口",
|
||||||
"common.instance": "實例化模組",
|
"common.instance": "實例化模組",
|
||||||
"setting.general-color-setting": "通用顏色設定",
|
"setting.general-color-setting": "通用顏色設定",
|
||||||
"cross-dot": "交叉點"
|
"cross-dot": "交叉點",
|
||||||
|
"common.selected-wire": "被選中的線"
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user