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