/* * @LastEditors: lisushuang * @Description: 启停等控制面板 * @FilePath: /graph/src/viewer/ControlTools.ts * @Date: 2023-07-31 16:03:16 * @LastEditTime: 2023-09-26 15:40:22 * @Author: lisushuang */ import { Graph, INode } from "../core"; import { IFunction } from "../interfaces"; import { NativeButton, NativeDiv } from "../shared"; import { GraphAction, GraphEventTypes } from "../types"; import { GraphEvents } from "./GraphEvents"; interface controlButton{ name:string, callback:IFunction, backgroundColor?:string, color?:string } class ControlTools extends NativeDiv { graph: Graph logPanel: ConsolePanel | null = null events: GraphEvents constructor(graph: Graph, events: GraphEvents) { super(); this.graph = graph; this.events = events this.initBox(); this.initButtons(); this.events.add(GraphEventTypes.AddRunLog, (log: { node: INode, msg: string }) => { this.logPanel?.log(log) }) } initBox() { this.setStyle({ position: "absolute", top: "10px", right: "10px", display: "flex", flexDirection: "row", background: "black", opacity: "0.8", height: "30px", padding: "8px", borderRadius: "10px", cursor: "pointer", justifyContent: "center", alignItems: "center" }) this.DOM.addEventListener("dblclick", (e:MouseEvent) => { e.stopPropagation(); }) } initButtons() { let start = new StartButton(); start.setMarginRight("10px") start.onClick(() => { if (!this.logPanel) { this.logPanel = new ConsolePanel(); this.add(this.logPanel); } this.graph.start(); }) this.add(start) this.events.add(GraphAction.StartRun,() => { start.DOM.click(); }) let end = new EndButton(); end.onClick(() => { this.logPanel?.remove(); this.logPanel = null; start.stopAnimation(); this.graph.stop(); setTimeout(() => { this.graph.stop(); }, this.graph.stepTime); }) this.events.add(GraphAction.StopRun,() => { end.DOM.click(); }) this.add(end); } /** * @description: 添加一个控制按钮 * @param {controlButton} button */ addButton(button:controlButton){ if(!button.name || !button.callback){ console.error("无效的按钮") return ; } let bt = new NativeButton(button.name) bt.onClick(() => { button.callback() }) bt.setMarginLeft("10px") if(button.backgroundColor){ bt.setBackgroundColor(button.backgroundColor) } if(button.color){ bt.setColor(button.color) } this.add(bt) } } class StartButton extends NativeDiv { isAnimating: boolean = false; animationFrameId: number = 0; scale: number = 1; scaleFactor: number = 0.01; maxScale: number = 1.2; minScale: number = 0.8; constructor() { super(); this.DOM.innerHTML = '\ \ \ ' this.setHeight(24); this.onClick(() => { this.startAnimation(); }) } // 开始动画 startAnimation() { if (this.isAnimating) { return; } this.isAnimating = true; this.animateScale(); } // 停止动画 stopAnimation() { if (!this.isAnimating) { return; } this.isAnimating = false; cancelAnimationFrame(this.animationFrameId); this.scale = 1; this.updateScale(); } // 缓慢缩放动画 animateScale() { if (!this.isAnimating) { return; } this.scale += this.scaleFactor; if (this.scale >= this.maxScale || this.scale <= this.minScale) { this.scaleFactor = -this.scaleFactor; // 反转缩放方向 } this.updateScale(); // 使用 requestAnimationFrame 递归调用动画函数,实现连续的缩放动画效果 this.animationFrameId = requestAnimationFrame(() => { this.animateScale(); }); } // 更新元素的缩放比例 updateScale() { this.DOM.style.transform = `scale(${this.scale})`; } } class EndButton extends NativeDiv { constructor() { super() this.setHeight(24) this.DOM.innerHTML = '\ \ \ ' } } // 运行日志面板 class ConsolePanel extends NativeDiv { logs: Array<{ node: INode, msg: string }> = []; constructor() { super(); this.initBox(); this.render(); } initBox() { this.setStyle({ position: "absolute", left: "-20px", background: "black", height: "20px", width: "20px", top: "0", borderRadius: "6px", padding: "6px", overflowY: "auto", opacity: ".8", transition: "all 0.5s ease" }) setTimeout(() => { this.setWidth("200px") .setLeft("-220px") .setHeight("200px") }, 0); this.onWheel((e: WheelEvent) => { e.stopPropagation() }) } log(log: { node: INode, msg: string }) { this.logs.unshift(log); this.render(); } private render() { this.clear(); this.logs.forEach(item => { this.createLogItem(item); }) } override remove(): void { // this.clear(); this.setLeft("0px") .setTop(5) .setWidth("24px") .setHeight("24px") setTimeout(() => { this.clear(); }, 300); setTimeout(() => { super.remove(); }, 500); } createLogItem(item: { node: INode, msg: string }) { let content = new NativeDiv(); content.DOM.innerHTML = `节点${item.node.id} [${item.node.label}]:${item.msg}` content.setStyle({ color: "white", fontSize: "12px", padding: "2px", borderBottom: "1px solid white", borderRadius: "4px", cursor: "pointer", userSelect:"none", webkitUserSelect:"none" }) content.onMouseover(() => { content.setStyle({ background: "white", color: "black" }) }) content.DOM.addEventListener("dblclick", (e: MouseEvent) => { e.stopPropagation(); }) content.onMouseout(() => { content.setStyle({ background: "black", color: "white" }) }) content.onClick(() => { item.node.render?.events.dispatch(GraphAction.FocusOnNode,item.node.id) item.node.render?.shake() }) this.add(content); } } export default ControlTools;