跳到主要内容

动画与过渡

HMI 动画全部基于 CSS transition / animation / keyframes, 不需要 JS 动画库。配合 darra.bind 回调切换 class / style, 即可实现工业设备的开/关/旋转/流动/闪烁效果。

三种动画技术

技术适合性能
transition属性渐变 (width/color/transform)极高
animation + @keyframes循环/复杂序列 (旋转/闪烁/流动)
JS 动画 (requestAnimationFrame)极端自定义 (轨迹曲线)中, 占 CPU

90% 场景优先用 CSS, CSS 动画由 GPU 处理, 流畅且省电。

1. transition (状态切换)

.valve {
transition: background 0.3s ease, transform 0.3s ease;
}
.valve.open { background: green; transform: rotate(0deg); }
.valve.closed { background: red; transform: rotate(90deg); }
<div class="valve" :class="{ open: isOpen, closed: !isOpen }"
x-data="{ isOpen: false }"
x-init="darra.bind('M11.0', v => isOpen = v)">
</div>

常用过渡属性:

属性说明
transition: all 0.3s ease一次性声明
transition: color .2s, background .5s ease-out多属性不同时长
transition-delay: .1s延迟

缓动函数: linear / ease / ease-in / ease-out / ease-in-out / cubic-bezier(.2,.8,.2,1)

2. keyframes (循环动画)

阀门旋转开闭

@keyframes valve-open {
0% { transform: rotate(90deg); background: red; }
50% { transform: rotate(45deg); background: orange; }
100% { transform: rotate(0deg); background: green; }
}

.valve.opening {
animation: valve-open 1s ease-out forwards;
}

电机旋转

@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.motor.running {
animation: spin 1s linear infinite;
}
.motor.running.slow { animation-duration: 3s; }
.motor.running.fast { animation-duration: 0.3s; }
<div class="motor" :class="{
'running': speed > 0,
'slow': speed > 0 && speed < 500,
'fast': speed >= 2000
}"
x-data="{ speed: 0 }"
x-init="darra.bind('DB3.MotorSpeed', v => speed = v)">
<img src="/static/img/motor.svg" />
</div>

液位上升 (tank)

.tank {
position: relative;
width: 120px; height: 200px;
border: 3px solid #333;
overflow: hidden;
}
.tank .liquid {
position: absolute;
left: 0; right: 0; bottom: 0;
background: linear-gradient(180deg, #4FC3F7, #1E88E5);
height: 0;
transition: height 0.5s ease;
}
.tank .liquid::before {
content: '';
position: absolute;
left: 0; right: 0; top: -10px;
height: 10px;
background: inherit;
border-radius: 50%;
animation: wave 2s ease-in-out infinite;
}
@keyframes wave {
0%,100% { transform: translateX(0) scaleY(1); }
50% { transform: translateX(-5px) scaleY(0.8); }
}
<div class="tank"
x-data="{ level: 0 }"
x-init="darra.bind('DB1.Level', v => level = v)">
<div class="liquid" :style="`height: ${level}%`"></div>
</div>

管道流体动画

.pipe {
height: 20px;
background: linear-gradient(90deg, #666, #aaa, #666);
}
.pipe.flowing {
background-image: repeating-linear-gradient(
-45deg,
#4FC3F7 0px, #4FC3F7 10px,
#81D4FA 10px, #81D4FA 20px
);
background-size: 28.28px 28.28px;
animation: flow 1s linear infinite;
}
@keyframes flow {
from { background-position: 0 0; }
to { background-position: 28.28px 0; }
}
.pipe.flowing.reverse { animation-direction: reverse; }
.pipe.flowing.slow { animation-duration: 3s; }
<div class="pipe" :class="{
'flowing': flow > 0,
'reverse': flow < 0,
'slow': Math.abs(flow) < 5
}"
x-data="{ flow: 0 }"
x-init="darra.bind('DB1.Flow', v => flow = v)">
</div>

报警闪烁

@keyframes alarm-blink {
0%, 49% { background: var(--darra-danger); }
50%,100% { background: #7f1d1d; }
}
.alarm-active {
animation: alarm-blink 0.5s step-end infinite;
color: #fff;
}

/* 伴随抖动 */
@keyframes alarm-shake {
0%,100% { transform: translateX(0); }
25% { transform: translateX(-3px); }
75% { transform: translateX(3px); }
}
.alarm-active.critical {
animation: alarm-blink 0.3s step-end infinite, alarm-shake .2s infinite;
}

LED 呼吸灯

@keyframes pulse {
0%,100% { opacity: 1; box-shadow: 0 0 8px currentColor; }
50% { opacity: .5; box-shadow: 0 0 20px currentColor; }
}
.led.on { animation: pulse 2s ease-in-out infinite; }

3. SVG 动画 (机器人/管线图)

<svg viewBox="0 0 200 100">
<!-- 机械臂示意 -->
<g :transform="`rotate(${j1Angle} 50 50)`">
<line x1="50" y1="50" x2="100" y2="50" stroke="#666" stroke-width="6" />
<circle cx="50" cy="50" r="8" fill="#333" />
</g>
</svg>

<script>
const el = document.querySelector('svg g')
let j1Angle = 0
darra.bind('DB50.Axis0.Pos', (v) => {
j1Angle = v
el.setAttribute('transform', `rotate(${v} 50 50)`)
})
</script>

4. Chart.js 动画

const chart = new Chart(ctx, {
type: 'line',
data: { /* ... */ },
options: {
animation: { duration: 400, easing: 'easeOutQuart' },
// 或关闭 (高速刷新)
// animation: false
}
})

// 强制重置动画
chart.reset()

趋势图高频更新时关闭动画:

darra.bind('DB1.T1', (v) => {
chart.data.datasets[0].data.push({ x: Date.now(), y: v })
chart.update('none') // 'none' = 跳过动画
})

5. 页面切换动画

body { animation: page-in .4s ease; }
@keyframes page-in {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}

6. Alpine x-transition (显隐动画)

<div x-data="{ show: false }">
<button @click="show = !show">切换</button>

<div x-show="show"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform scale-90"
x-transition:enter-end="opacity-100 transform scale-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0">
内容
</div>
</div>

简化写法:

<div x-show="show" x-transition>内容</div>

7. 性能 & 最佳实践

只动 transform / opacity

这两个属性走 GPU 合成层, 不触发 layout/paint:

/* 好 */
.el { transform: translateX(100px); opacity: 0.5; }

/* 差 (触发 layout) */
.el { left: 100px; width: 200px; }

will-change 提示浏览器

.chart-canvas {
will-change: transform;
}

不要滥用, 只对即将动画的元素设。

降频处理

// 60 FPS 太高, 节流到 20 FPS 显示动画
let lastUpdate = 0
darra.bind('DB1.Temp', (v) => {
const now = performance.now()
if (now - lastUpdate < 50) return // 20 FPS
lastUpdate = now
tempDisplay.textContent = v.toFixed(2)
})

动画降级 (老设备)

@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001s !important;
transition-duration: 0.001s !important;
}
}

8. 陷阱

陷阱解法
动画卡顿只动 transform/opacity, 避免 top/left/width/height
animation: spin 1s infinite CPU 高隐藏元素动画继续? 加 visibility: hidden 或移除 class
阀门在打开和关闭间切换时跳动forwards 保留最终帧
液位变化过于"丝滑"看不清transition: height .5s cubic-bezier(...) 或数值显示
Chart.js 动画叠加卡顿chart.update('none') 跳过

9. 工业动画参考清单

设备动画思路
阀门旋转 + 色变
电机持续旋转, 速度由动画时长驱动
液位高度渐变 + 上表面波浪
管道背景条纹平移
加热丝橙→红色渐变 + 轻微闪烁
传送带纹理平移
报警灯颜色闪烁 + 抖动
搅拌机叶片旋转
指示灯呼吸灯 pulse
机械臂SVG group rotate

相关文档