{ "packages": ["matplotlib"] }

固体压强可视化模型 · 英雄卡车

作者:雷霆一击就是我

1. 操作区

模拟在木块上逐个增加砝码:
▸ 砝码数量越多 → 总压力 F 越大;
▸ 木块底面积越小 → 压强 P 越大;
通过改变压力 F 和受力面积 S,观察:
● 左侧“压力条 F”的高度变化;
● 右侧“压强条 P”、地面红区以及卡车表情的变化。
砝码数量范围:1~10 个,总压力 F = 砝码个数 × 10 N。
可以理解为物体的重力的大小。
先将 F 固定,只改变 S,体验“面积变→压强变”。
木块底面积范围:4~100 cm²。面积越小,压强越大。
面积越小,同样的力越集中,卡车越“难受”。
再将 S 固定,只改变 F,看两根竖条同时变化。
练习:
● 找到“压力条 F 一样高,但压强条 P 不同高”的两种情况,说明原因。
● 找到“压强条 P 一样高,但 F 和 S 不相同”的两种情况。

2. 动画可视化模型

场景:英雄卡车上放一块木块,改变压力和受力面积,观察压强 P = F / S 的变化 固定压强
压力过大
3 个
30 N
木块底面积 S
😎
当前压强 P ≈ 150 Pa
压强状态:中等,英雄卡车有点吃力。
此时总压力 F = 30 N,底面积 S = 20 cm²。采用 P = F × 100 / S 计算压强,可得 P ≈ 150 Pa。
P = F × 100 / S(S 单位:cm²;例如:3×10×100/20 = 150 Pa)
实时压强变化曲线(Step vs P)
import matplotlib.pyplot as plt from pyscript import display, when from js import document, setTimeout from pyodide.ffi import create_proxy # ===== 参数汇总 ===== WEIGHT_MIN = 1 WEIGHT_MAX = 10 WEIGHT_FORCE = 10 # 单个砝码 10N AREA_MIN = 4 AREA_MAX = 100 PRESSURE_HIGH_THRESHOLD = 200 # ★ 压强过大阈值 WARNING_DURATION_MS = 3000 # ★ 提示显示时长(毫秒) COLOR_FORCE_LOW = "#22c55e" COLOR_FORCE_MED = "#f59e0b" COLOR_FORCE_HIGH = "#dc2626" COLOR_PRESSURE_LOW = "#22c55e" COLOR_PRESSURE_MED = "#f59e0b" COLOR_PRESSURE_HIGH = "#dc2626" # ===== matplotlib 压强曲线 ===== fig, ax = plt.subplots(figsize=(4, 2)) pressure_history = [] step_history = [] current_step = 0 display(fig, target="pressure-chart", append=False) def update_chart(P: float): """更新压强曲线""" global current_step current_step += 1 step_history.append(current_step) pressure_history.append(P) if len(step_history) > 50: del step_history[0] del pressure_history[0] ax.clear() ax.plot(step_history, pressure_history, marker="o") ax.set_xlabel("Step") ax.set_ylabel("P (Pa)") ax.grid(True, alpha=0.3) display(fig, target="pressure-chart", append=False) # ===== 压力过大提示 ===== def hide_warning(): document.getElementById("warning-popup").classList.remove("show") def show_warning(): popup = document.getElementById("warning-popup") popup.classList.add("show") cb = create_proxy(hide_warning) setTimeout(cb, WARNING_DURATION_MS) # ===== 主更新函数 ===== def update_state(event=None): mass_slider = document.getElementById("mass-count") area_slider = document.getElementById("block-area") mass_count = int(mass_slider.value) area_cm2 = int(area_slider.value) # 压力 F(N) F = mass_count * WEIGHT_FORCE # 按验收标准:P = F × 100 / S_cm2 P = F * 100.0 / area_cm2 # --- 顶部数值 --- force_top = document.getElementById("force-top-text") pressure_top = document.getElementById("pressure-top-text") force_top.textContent = f"F = {F} N" pressure_top.textContent = f"P ≈ {P:.0f} Pa" document.getElementById("mass-count-text").textContent = f"{mass_count} 个" document.getElementById("area-text").textContent = f"{area_cm2} cm²" document.getElementById("pressure-main-text").textContent = f"{P:.0f}" # --- 单砝码图标内部:显示个数 + 总力 --- weight_count = document.getElementById("weight-count") weight_force = document.getElementById("weight-force") weight_count.textContent = f"{mass_count} 个" weight_force.textContent = f"{F} N" # --- 颜色分档 --- if F < WEIGHT_FORCE * 3: force_top.style.color = COLOR_FORCE_LOW elif F < WEIGHT_FORCE * 7: force_top.style.color = COLOR_FORCE_MED else: force_top.style.color = COLOR_FORCE_HIGH if P < 0.5 * PRESSURE_HIGH_THRESHOLD: pressure_color = COLOR_PRESSURE_LOW state_text = "压强较小,英雄卡车轻松扛住。" emoji = "😎" cab_color = "linear-gradient(135deg,#2563eb,#1d4ed8)" elif P < PRESSURE_HIGH_THRESHOLD: pressure_color = COLOR_PRESSURE_MED state_text = "压强中等,英雄卡车有点吃力。" emoji = "😐" cab_color = "linear-gradient(135deg,#f97316,#ea580c)" else: pressure_color = COLOR_PRESSURE_HIGH state_text = "压强很大,英雄卡车“压力山大”。" emoji = "😫" cab_color = "linear-gradient(135deg,#dc2626,#b91c1c)" pressure_top.style.color = pressure_color document.getElementById("info-level-text").textContent = f"压强状态:{state_text}" # --- 条形高度 --- force_fraction = (F - WEIGHT_MIN*WEIGHT_FORCE) / ((WEIGHT_MAX*WEIGHT_FORCE) - WEIGHT_MIN*WEIGHT_FORCE) force_fraction = max(0.0, min(force_fraction, 1.0)) document.getElementById("force-bar-inner").style.height = f"{20 + force_fraction * 80:.1f}%" P_min, P_max = 0, 500 pressure_fraction = (P - P_min) / (P_max - P_min) if P_max > P_min else 0 pressure_fraction = max(0.0, min(pressure_fraction, 1.0)) document.getElementById("pressure-bar-inner").style.height = f"{20 + pressure_fraction * 80:.1f}%" # --- 木块宽度随 S 变化 --- cargo = document.getElementById("cargo") minArea, maxArea = AREA_MIN, AREA_MAX minW, maxW = 90, 180 k = (area_cm2 - minArea) / (maxArea - minArea) if maxArea > minArea else 0 k = max(0.0, min(k, 1.0)) cargo.style.width = f"{minW + k * (maxW - minW):.0f}px" document.getElementById("cargo-label").textContent = f"木块底面积 S = {area_cm2} cm²" # --- 地面高压区 + 卡车下沉 + 表情 --- hotspot = document.getElementById("ground-hotspot") hotspot_width = 90 - pressure_fraction * 30 hotspot.style.width = f"{hotspot_width:.0f}px" hotspot.style.opacity = f"{0.15 + pressure_fraction * 0.65:.2f}" truck_zone = document.getElementById("truck-zone") wheel_left = document.getElementById("wheel-left") wheel_right = document.getElementById("wheel-right") truck_cab = document.getElementById("truck-cab") truck_face = document.getElementById("truck-face") sink = pressure_fraction * 8 truck_zone.style.transform = f"translateX(-50%) translateY({sink}px)" wheel_left.style.transform = f"translateY({sink/3}px)" wheel_right.style.transform = f"translateY({sink/3}px)" truck_cab.style.background = cab_color truck_cab.style.transform = f"translateY({pressure_fraction*2}px)" truck_face.textContent = emoji # --- 文本描述 --- document.getElementById("info-detail-text").textContent = ( f"当前共有 {mass_count} 个砝码,总压力 F = {F} N,木块底面积 S = {area_cm2} cm²。" f" 采用 P = F × 100 / S 计算压强,可得 P ≈ {P:.0f} Pa。" ) # --- 压强过大提示 --- if P >= PRESSURE_HIGH_THRESHOLD: show_warning() # --- 更新压强曲线 --- update_chart(P) @when("input", "#mass-count") def _on_mass_change(event): update_state(event) @when("input", "#block-area") def _on_area_change(event): update_state(event) # 初始一次 update_state()