js+css实现基于浏览器的交互式数字逻辑电路模拟器(可视化编辑器)代码

代码语言:html

所属分类:其他

代码描述:js+css实现基于浏览器的交互式数字逻辑电路模拟器(可视化编辑器)代码,主要用于搭建、测试和验证组合逻辑电路。 用户可通过左侧工具箱拖拽添加输入开关、输出指示灯及五种基本逻辑门(与、或、异或、与非、非),并用贝塞尔曲线连线构建电路。系统会实时计算信号传递,高电平以霓虹发光和流动动画直观反馈。右下角真值表面板自动穷举所有输入组合并高亮当前状态,便于验证逻辑正确性。 操作方面,支持画布无限平移缩放、节点多选拖拽、右键切换门类型、撤销/重做及键盘快捷键。还可通过保存/加载按钮将电路序列化为json存入剪贴板,

代码标签: js css 基于 浏览器 交互式 数字 逻辑 电路 模拟器 可视化编辑器 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="zh-CN">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>逻辑验证引擎 v3</title>
	<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
	<style>
		*,
		*::before,
		*::after {
			margin: 0;
			padding: 0;
			box-sizing: border-box
		}

		html,
		body {
			height: 100%;
			overflow: hidden
		}

		:root {
			--bg-primary: #0a0a12;
			--bg-secondary: #12121e;
			--bg-glass: rgba(18, 18, 30, 0.75);
			--border-glass: rgba(0, 240, 255, 0.08);
			--color-true: #00f0ff;
			--color-false: #1a1a2e;
			--color-true-glow: 0 0 20px rgba(0, 240, 255, 0.4), 0 0 60px rgba(0, 240, 255, 0.1);
			--color-accent: #7b61ff;
			--color-accent-glow: 0 0 15px rgba(123, 97, 255, 0.3);
			--color-danger: #ff4466;
			--color-danger-glow: 0 0 12px rgba(255, 68, 102, 0.3);
			--color-text: #e0e0e8;
			--color-text-dim: #5a5a72;
			--color-wire-true: #00f0ff;
			--color-wire-false: #1e1e32;
			--color-gate-bg: #16162a;
			--color-gate-border: #2a2a44;
			--font-mono: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
			--font-sans: 'Inter', 'SF Pro', system-ui, sans-serif;
			--ease-expo: cubic-bezier(0.16, 1, 0.3, 1);
			--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
			--ease-signal: cubic-bezier(0.4, 0, 0.2, 1);
		}

		body {
			font-family: var(--font-mono);
			background: var(--bg-primary);
			color: var(--color-text);
			user-select: none
		}

		.top-bar {
			position: fixed;
			top: 0;
			left: 0;
			right: 0;
			height: 48px;
			display: flex;
			align-items: center;
			justify-content: space-between;
			padding: 0 1.5rem;
			z-index: 200;
			background: linear-gradient(180deg, var(--bg-primary) 60%, transparent);
			pointer-events: none
		}

		.top-bar>* {
			pointer-events: auto
		}

		.app-title {
			font-family: var(--font-mono);
			font-size: .7rem;
			font-weight: 600;
			letter-spacing: .2em;
			text-transform: uppercase;
			opacity: .4;
			color: var(--color-text)
		}

		.top-actions {
			display: flex;
			gap: .5rem;
			align-items: center
		}

		.action-btn {
			font-family: var(--font-mono);
			font-size: .6rem;
			font-weight: 600;
			letter-spacing: .08em;
			text-transform: uppercase;
			color: var(--color-text-dim);
			background: var(--bg-glass);
			backdrop-filter: blur(20px) saturate(1.2);
			border: 1px solid var(--border-glass);
			border-radius: 8px;
			padding: .45rem .9rem;
			cursor: pointer;
			transition: all .3s var(--ease-expo);
			white-space: nowrap
		}

		.action-btn:hover {
			color: var(--color-true);
			border-color: rgba(0, 240, 255, 0.2);
			box-shadow: var(--color-true-glow)
		}

		.action-btn:focus-visible {
			outline: 2px solid var(--color-accent);
			outline-offset: 2px
		}

		.action-btn.danger:hover {
			color: var(--color-danger);
			border-color: rgba(255, 68, 102, 0.2);
			box-shadow: var(--color-danger-glow)
		}

		.action-btn:disabled {
			opacity: .3;
			pointer-events: none
		}

		.canvas-viewport {
			width: 100%;
			height: 100vh;
			position: relative;
			overflow: hidden;
			cursor: grab
		}

		.canvas-viewport.panning {
			cursor: grabbing
		}

		.canvas-world {
			position: absolute;
			width: 1px;
			height: 1px;
			top: 0;
			left: 0;
			transform-origin: 0 0;
			will-change: transform;
			overflow: visible
		}

		.canvas-world::before {
			content: '';
			position: absolute;
			left: -50000px;
			top: -50000px;
			width: 100000px;
			height: 100000px;
			background-image: radial-gradient(circle at 1px 1px, rgba(0, 240, 255, 0.025) 1px, transparent 1px);
			background-size: 40px 40px;
			pointer-events: none
		}

		.wire-canvas {
			position: absolute;
			top: 0;
			left: 0;
			pointer-events: none;
			z-index: 5
		}

		.wire-hit-layer {
			position: absolute;
			top: 0;
			left: 0;
			width: 1px;
			height: 1px;
			overflow: visible;
			pointer-events: none;
			z-index: 6
		}

		.wire-hit {
			fill: none;
			stroke: transparent;
			stroke-width: 16;
			stroke-linecap: round;
			pointer-events: visibleStroke;
			cursor: pointer
		}

		.wire-temp-layer {
			position: absolute;
			top: 0;
			left: 0;
			width: 1px;
			height: 1px;
			overflow: visible;
			pointer-events: none;
			z-index: 7
		}

		.wire-temp {
			fill: none;
			stroke: var(--color-accent);
			stroke-width: 2;
			stroke-dasharray: 6 6;
			opacity: .7;
			pointer-events: none
		}

		.wire-temp.wire-invalid {
			stroke: var(--color-danger);
			opacity: .5
		}

		.circuit-node {
			position: absolute;
			cursor: grab;
			z-index: 10;
			transition: box-shadow .3s var(--ease-expo)
		}

		.circuit-node:active {
			cursor: grabbing
		}

		.circuit-node:focus-visible {
			outline: 2px solid var(--color-accent);
			outline-offset: 3px;
			z-index: 15
		}

		.circuit-node.selected {
			z-index: 15
		}

		.circuit-node.selected::after {
			content: '';
			position: absolute;
			inset: -4px;
			border: 1.5px solid var(--color-accent);
			border-radius: inherit;
			pointer-events: none;
			opacity: .6
		}

		@keyframes node-appear {
			0% {
				opacity: 0;
				transform: scale(.5)
			}

			100% {
				opacity: 1;
				transform: scale(1)
			}
		}

		@keyframes wire-reject {

			0%,
			100% {
				transform: translateX(0)
			}

			25% {
				transform: translateX(-4px)
			}

			75% {
				transform: translateX(4px)
			}
		}

		.circuit-node.wire-rejected {
			animation: wire-reject .3s var(--ease-spring)
		}

		.node-input {
			width: 90px;
			height: 72px;
			background: var(--color-gate-bg);
			border: 1.5px solid var(--color-gate-border);
			border-radius: 12px;
			display: flex;
			flex-direction: column;
			align-items: center;
			justify-content: center;
			gap: 4px;
			transition: border-color .3s var(--ease-expo), box-shadow .3s var(--ease-expo)
		}

		.node-input.state-true {
			border-color: rgba(0, 240, 255, 0.25);
			box-shadow: inset 0 0 20px rgba(0, 240, 255, 0.06), var(--color-true-glow)
		}

		.node-input .node-label {
			font-size: .7rem;
			font-weight: 600;
			color: var(--color-text-dim);
			transition: color .3s var(--ease-expo)
		}

		.node-input.state-true .node-label {
			color: var(--color-true)
		}

		.node-toggle {
			width: 32px;
			height: 18px;
			background: var(--bg-secondary);
			border: 1.5px solid var(--color-gate-border);
			border-radius: 9px;
			position: relative;
			cursor: pointer;
			transition: all .3s var(--ease-spring)
		}

		.node-toggle:focus-visible {
			outline: 2px solid var(--color-accent);
			outline-offset: 2px
		}

		.node-toggle::after {
			content: '';
			position: absolute;
			width: 12px;
			height: 12px;
			border-radius: 50%;
			background: var(--color-text-dim);
			top: 1.5px;
			left: 2px;
			transition: all .3s var(--ease-spring)
		}

		.node-input.state-true .node-toggle {
			background: rgba(0, 240, 255, 0.12);
			border-color: var(--color-true)
		}

		.node-input.state-true .node-toggle::after {
			left: 16px;
			background: var(--color-true);
			box-shadow: 0 0 8px rgba(0, 240, 255, 0.5)
		}

		.node-input .input-val-label {
			font-size: .5rem;
			font-weight: 600;
			color: var(--color-text-dim);
			transition: color .3s var(--ease-expo)
		}

		.node-input.state-true .input-val-label {
			color: var(--color-true)
		}

		.state-icon {
			display: inline-block;
			width: 6px;
			height: 6px;
			border-radius: 50%;
			background: var(--color-text-dim);
			margin-right: 2px;
			vertical-align: middle;
			transition: background .3s var(--ease-expo), box-shadow .3s var(--ease-expo)
		}

		.state-icon.active {
			background: var(--color-true);
			box-shadow: 0 0 6px rgba(0, 240, 255, 0.5)
		}

		.node-gate {
			width: 120px;
			height: 80px;
			background: var(--color-gate-bg);
			border: 1.5px solid var(--color-gate-border);
			border-radius: 12px;
			display: flex;
			flex-direction: column;
			align-items: center;
			justify-content: center;
			gap: 2px;
			transition: border-color .3s var(--ease-expo), box-shadow .3s var(--ease-expo)
		}

		.node-gate.output-true {
			border-color: rgba(0, 240, 255, 0.2);
			box-shadow: inset 0 0 20px rgba(0, 240, 255, 0.05), 0 0 15px rgba(0, 240, 255, 0.08)
		}

		.node-gate .gate-symbol {
			width: 38px;
			height: 26px;
			display: flex;
			align-items: center;
			justify-content: center;
			color: var(--color-text-dim);
			transition: transform .3s var(--ease-spring), color .3s var(--ease-expo)
		}

		.node-gate.output-true .gate-symbol {
			color: var(--color-true)
		}

		.node-gate .gate-symbol svg {
			width: 100%;
			height: 100%
		}

		.node-gate .gate-type-label {
			font-size: .55rem;
			font-weight: 600;
			color: var(--color-text-dim);
			letter-spacing: .1em;
			transition: color .3s var(--ease-expo)
		}

		.node-gate.output-true .gate-type-label {
			color: rgba(0, 240, 255, 0.5)
		}

		.gate-tooltip {
			position: absolute;
			bottom: calc(100% + 10px);
			left: 50%;
			transform: translateX(-50%) translateY(6px);
			background: var(--bg-glass);
			backdrop-filter: blur(20px) saturate(1.2);
			border: 1px solid var(--border-glass);
			border-radius: 10px;
			padding: .5rem .7rem;
			font-size: .5rem;
			color: var(--color-text-dim);
			white-space: nowrap;
			opacity: 0;
			pointer-events: none;
			transition: all .25s var(--ease-expo);
			z-index: 50
		}

		.gate-tooltip::after {
			content: '';
			position: absolute;
			top: 100%;
			left: 50%;
			transform: translateX(-50%);
			border: 5px solid transparent;
			border-top-color: rgba(0, 240, 255, 0.08)
		}

		.node-gate:hover .gate-tooltip,
		.node-gate:focus-within .gate-tooltip {
			opacity: 1;
			transform: translateX(-50%) translateY(0)
		}

		.gate-tooltip table {
			border-collapse: collapse
		}

		.gate-tooltip th,
		.gate-tooltip td {
			padding: 1px 5px;
			text-align: center
		}

		.gate-tooltip th {
			color: var(--color-true);
			border-bottom: 1px solid var(--border-glass)
		}

		.gate-tooltip td.v1 {
			color: var(--color-true)
		}

		.node-output {
			width: 72px;
			height: 72px;
			background: var(--color-gate-bg);
			border: 1.5px solid var(--color-gate-border);
			border-radius: 50%;
			display: flex;
			flex-direction: column;
			align-items: center;
			justify-content: center;
			gap: 2px;
			transition: border-color .3s var(--ease-expo), box-shadow .4s var(--ease-expo), background .3s var(--ease-expo)
		}

		.node-output.state-true {
			border-color: var(--color-true);
			background: rgba(0, 240, 255, 0.06);
			box-shadow: var(--color-true-glow);
			animation: led-breathe 2.2s ease-in-out infinite
		}

		@keyframes led-breathe {

			0%,
			100% {
				box-shadow: 0 0 20px rgba(0, 240, 255, 0.35), 0 0 50px rgba(0, 240, 255, 0.08)
			}

			50% {
				box-shadow: 0 0 30px rgba(0, 240, 255, 0.5), 0 0 70px rgba(0, 240, 255, 0.12)
			}
		}

		.node-output .node-label {
			font-size: .7rem;
			font-weight: 600;
			color: var(--color-text-dim);
			transition: color .3s var(--ease-expo)
		}

		.node-output.state-true .node-label {
			color: var(--color-true)
	.........完整代码请登录后点击上方下载按钮下载查看

网友评论0