当前位置: 首页 > news >正文

arkui 动画曲线

参考文档

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-curve#curvesinterpolatingspring10

可视化工具网站

https://easingwizard.com/
https://www.desmos.com/calculator/k01p40v0ct?lang=zh-CN

基本介绍

import { curves } from '@kit.ArkUI'
curves.interpolatingSpring(10, 1, 228, 30) // 创建一个时长由弹簧参数决定的弹簧插值曲线
/**
代码核心功能:
该代码片段用于创建一个弹簧插值曲线,曲线的时长由弹簧参数决定。这在动画或界面过渡效果中非常有用,可以根据物理模拟的弹簧运动来平滑过渡。代码逻辑走读:
1. 导入模块:代码首先从`@kit.ArkUI`模块中导入`curves`对象,这个对象包含了各种用于动画和过渡效果的函数和方法。
2. 创建弹簧插值曲线:调用`curves.interpolatingSpring`方法,传入四个参数:`10`(初始位置)、`1`(初始速度)、`228`(弹簧常数)和`30`(摩擦常数)。这个方法根据这些参数创建一个弹簧插值曲线,曲线的时长和形状由这些参数定义。
3. 曲线应用:生成的曲线可以用于界面元素的动画效果,使其在移动或变化时遵循弹簧运动的物理规律,从而实现平滑、自然的过渡。
本次解答由人工智能生成,仅供参考
*/
  • damping(阻尼):控制弹簧震荡的衰减速度。
    阻尼值越小,弹簧震荡次数越多,衰减越慢(如软弹簧,弹性强);
    阻尼值越大,震荡越快停止,甚至可能无明显回弹(如硬弹簧,接近刚性)。
  • stiffness(刚度 / 劲度系数):控制弹簧的 “硬度”。
    刚度越大,弹簧越 “硬”,运动速度快、回弹幅度小(如金属弹簧);
    刚度越小,弹簧越 “软”,运动更平缓、回弹幅度大(如橡胶弹簧)。
  • mass(质量):模拟被弹簧拉动的物体质量(部分实现中默认固定值)。
    质量越大,动画启动和停止的惯性越强,运动更迟缓;质量越小,响应越灵敏。
  • initialVelocity(初始速度):动画开始时的初始运动速度,影响初始震荡的幅度(如快速滑动后的惯性回弹)。
    from(起始值) 与 to(目标值):定义动画的起始状态和最终稳定的目标状态(如位置、大小、透明度等属性值)。

代码

弹簧曲线动画

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.interpolate import make_interp_spline
import matplotlib.widgets as widgets# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "Heiti TC", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False  # 正确显示负号class ParametricSpringAnimation:def __init__(self):# 物理参数(默认值)self.initial_velocity = -2.0   # 初始速度(负值表示向左运动)self.mass = 1.0                # 质量self.stiffness = 5.0           # 刚度(劲度系数)self.damping = 0.5             # 阻尼系数# 弹簧基本参数self.start_point = np.array([2, 5])  # 弹簧固定端self.equilibrium_pos = 8            # 平衡位置X坐标self.spring_coils = 12              # 弹簧圈数self.coil_height = 0.6              # 线圈高度# 状态变量self.current_pos = self.equilibrium_pos  # 当前位置self.current_vel = self.initial_velocity  # 当前速度self.time = 0.0                           # 时间# 动画参数self.total_frames = 300                  # 总帧数self.fps = 60                            # 帧率self.dt = 1.0 / self.fps                 # 时间步长# 创建图形和轴self.fig = plt.figure(figsize=(12, 8))self.ax = self.fig.add_axes([0.1, 0.3, 0.8, 0.6])  # 主绘图区self.fig.suptitle('参数可控的插值弹簧曲线动画', fontsize=16)# 设置主坐标轴范围self.ax.set_xlim(0, 12)self.ax.set_ylim(2, 12)self.ax.set_xlabel('X轴')self.ax.set_ylabel('Y轴')self.ax.grid(True, alpha=0.3)self.ax.set_aspect('equal', adjustable='box')# 初始化绘图元素self.spring_line, = self.ax.plot([], [], 'b-', linewidth=3)  # 弹簧曲线self.fixed_point, = self.ax.plot([], [], 'ro', markersize=10)  # 固定端点self.mass_point, = self.ax.plot([], [], 'go', markersize=15)  # 重物self.trace_line, = self.ax.plot([], [], 'r-', linewidth=1, alpha=0.3)  # 轨迹线# 轨迹记录self.trace_points = []# 信息文本self.info_text = self.ax.text(0.02, 0.98, '', transform=self.ax.transAxes, verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))# 添加参数控制面板self.add_parameter_controls()def add_parameter_controls(self):"""添加参数控制滑块"""# 初始速度滑块ax_vel = self.fig.add_axes([0.2, 0.2, 0.65, 0.03])self.vel_slider = widgets.Slider(ax=ax_vel,label='初始速度',valmin=-5.0,valmax=5.0,valinit=self.initial_velocity,valstep=0.1)# 质量滑块ax_mass = self.fig.add_axes([0.2, 0.15, 0.65, 0.03])self.mass_slider = widgets.Slider(ax=ax_mass,label='质量',valmin=0.1,valmax=5.0,valinit=self.mass,valstep=0.1)# 刚度滑块ax_stiff = self.fig.add_axes([0.2, 0.1, 0.65, 0.03])self.stiff_slider = widgets.Slider(ax=ax_stiff,label='刚度',valmin=1.0,valmax=20.0,valinit=self.stiffness,valstep=0.5)# 阻尼滑块ax_damp = self.fig.add_axes([0.2, 0.05, 0.65, 0.03])self.damp_slider = widgets.Slider(ax=ax_damp,label='阻尼',valmin=0.1,valmax=2.0,valinit=self.damping,valstep=0.1)# 重置按钮ax_reset = self.fig.add_axes([0.85, 0.05, 0.1, 0.04])self.reset_btn = widgets.Button(ax_reset, '重置')# 绑定事件处理函数self.vel_slider.on_changed(self.update_parameters)self.mass_slider.on_changed(self.update_parameters)self.stiff_slider.on_changed(self.update_parameters)self.damp_slider.on_changed(self.update_parameters)self.reset_btn.on_clicked(self.reset_animation)def init_animation(self):"""初始化动画元素"""self.spring_line.set_data([], [])self.fixed_point.set_data([], [])self.mass_point.set_data([], [])self.trace_line.set_data([], [])self.info_text.set_text('')return self.spring_line, self.fixed_point, self.mass_point, self.trace_line, self.info_textdef update_parameters(self, val):"""更新物理参数"""self.initial_velocity = self.vel_slider.valself.mass = self.mass_slider.valself.stiffness = self.stiff_slider.valself.damping = self.damp_slider.valself.reset_animation(None)  # 参数改变后重置动画def reset_animation(self, event):"""重置动画状态"""self.current_pos = self.equilibrium_posself.current_vel = self.initial_velocityself.time = 0.0self.trace_points = []def generate_spring_points(self, end_x):"""生成弹簧上的点并进行插值平滑"""end_point = np.array([end_x, self.start_point[1]])# 计算弹簧总长度和方向length = np.linalg.norm(end_point - self.start_point)# 生成弹簧的控制点t = np.linspace(0, 1, self.spring_coils * 2 + 1)x = self.start_point[0] + t * (end_point[0] - self.start_point[0])# 生成弹簧的波动形状y = self.start_point[1] + np.sin(t * self.spring_coils * 2 * np.pi) * self.coil_height# 使用三次样条插值使曲线更平滑spl = make_interp_spline(t, np.column_stack((x, y)), k=3)t_smooth = np.linspace(0, 1, 200)  # 更密集的点smooth_points = spl(t_smooth)return smooth_pointsdef calculate_physics(self):"""根据物理规律计算下一帧状态"""# 胡克定律:F = -k(x - x0) - c*vdisplacement = self.current_pos - self.equilibrium_posforce = -self.stiffness * displacement - self.damping * self.current_vel# 牛顿第二定律:a = F/macceleration = force / self.mass# 更新速度和位置self.current_vel += acceleration * self.dtself.current_pos += self.current_vel * self.dt# 限制位置范围,防止弹簧过度拉伸if self.current_pos < self.start_point[0] + 1.0:self.current_pos = self.start_point[0] + 1.0self.current_vel = 0.0if self.current_pos > 11.0:self.current_pos = 11.0self.current_vel = 0.0self.time += self.dtdef update_animation(self, frame):"""更新动画帧"""# 计算物理状态self.calculate_physics()# 生成弹簧曲线点spring_points = self.generate_spring_points(self.current_pos)# 更新弹簧曲线self.spring_line.set_data(spring_points[:, 0], spring_points[:, 1])# 更新固定端点self.fixed_point.set_data(self.start_point[0], self.start_point[1])# 更新重物位置self.mass_point.set_data(self.current_pos, self.start_point[1])# 更新轨迹self.trace_points.append([self.time, self.current_pos - self.equilibrium_pos])if len(self.trace_points) > 1000:  # 限制轨迹点数量self.trace_points.pop(0)# 绘制轨迹if len(self.trace_points) > 1:trace_array = np.array(self.trace_points)self.trace_line.set_data(trace_array[:, 0], trace_array[:, 1] + self.equilibrium_pos)# 更新信息文本displacement = self.current_pos - self.equilibrium_posself.info_text.set_text(f'时间: {self.time:.1f}s\n'f'位移: {displacement:.2f}\n'f'速度: {self.current_vel:.2f}')return self.spring_line, self.fixed_point, self.mass_point, self.trace_line, self.info_textdef create_animation(self, save_path=None):"""创建并显示动画"""anim = FuncAnimation(self.fig,self.update_animation,frames=self.total_frames,init_func=self.init_animation,interval=1000/self.fps,  # 每帧间隔毫秒blit=True,repeat=True  # 动画循环播放)# 修复matplotlib版本兼容性问题anim._resize_id = None# 如果提供了保存路径,则保存动画if save_path:try:anim.save(save_path, writer='ffmpeg', fps=self.fps)print(f"动画已保存至: {save_path}")except Exception as e:print(f"保存动画失败: {e}")print("请确保已安装ffmpeg")plt.show()if __name__ == "__main__":# 创建并显示弹簧动画spring_anim = ParametricSpringAnimation()# 如需保存动画,取消下面一行的注释并指定路径spring_anim.create_animation('parametric_spring_animation.mp4')# 显示动画spring_anim.create_animation()

贝塞尔曲线动画

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False  # 正确显示负号class BezierAnimation:def __init__(self):# 初始化控制点 - 可以修改这些点来获得不同的曲线self.controls = np.array([[0, 0],    # 起点[2, 0],    # 控制点1[2, 5],    # 控制点2[5, 5]     # 终点])self.num_points = 100  # 曲线上的点数量self.t = np.linspace(0, 1, self.num_points)  # 参数t从0到1# 创建图形和轴self.fig, self.ax = plt.subplots(figsize=(8, 6))self.fig.suptitle('贝塞尔曲线动画演示', fontsize=15)# 设置坐标轴范围self.ax.set_xlim(-1, 6)self.ax.set_ylim(-1, 5)self.ax.set_xlabel('X轴')self.ax.set_ylabel('Y轴')self.ax.grid(True)# 初始化绘图元素self.control_line, = self.ax.plot([], [], 'r--', alpha=0.6)  # 控制点连接线self.control_points, = self.ax.plot([], [], 'ro', markersize=8)  # 控制点self.bezier_curve, = self.ax.plot([], [], 'b-', linewidth=2)  # 贝塞尔曲线self.animated_point, = self.ax.plot([], [], 'go', markersize=10)  # 曲线上的动画点# 添加控制点标签self.control_labels = [self.ax.text(0, 0, '', fontsize=12) for _ in range(len(self.controls))]# 动画帧数量self.animation_frames = 100def bezier_curve_calc(self, t):"""计算贝塞尔曲线上的点"""n = len(self.controls) - 1  # 曲线阶数 = 控制点数量 - 1result = np.zeros(2)for i in range(n + 1):# 计算二项式系数binom = np.math.comb(n, i)# 计算贝塞尔基函数basis = binom * (t ** i) * ((1 - t) ** (n - i))# 累加计算曲线上的点result += basis * self.controls[i]return resultdef init_animation(self):"""初始化动画"""self.control_line.set_data([], [])self.control_points.set_data([], [])self.bezier_curve.set_data([], [])self.animated_point.set_data([], [])for label in self.control_labels:label.set_text('')return (self.control_line, self.control_points, self.bezier_curve, self.animated_point, *self.control_labels)def update_animation(self, frame):"""更新动画帧"""# 计算当前帧对应的t值current_t = frame / self.animation_frames# 更新控制点显示self.control_points.set_data(self.controls[:, 0], self.controls[:, 1])self.control_line.set_data(self.controls[:, 0], self.controls[:, 1])# 更新控制点标签for i, label in enumerate(self.control_labels):label.set_position((self.controls[i, 0] + 0.1, self.controls[i, 1] + 0.1))label.set_text(f'P{i}')# 计算当前t值范围内的贝塞尔曲线curve_points = np.array([self.bezier_curve_calc(t) for t in self.t if t <= current_t])if len(curve_points) > 0:self.bezier_curve.set_data(curve_points[:, 0], curve_points[:, 1])# 更新动画点(当前t对应的点)current_point = self.bezier_curve_calc(current_t)self.animated_point.set_data(current_point[0], current_point[1])return (self.control_line, self.control_points, self.bezier_curve, self.animated_point, *self.control_labels)def create_animation(self, save_path=None):"""创建并显示动画"""anim = FuncAnimation(self.fig, self.update_animation, frames=self.animation_frames + 1,init_func=self.init_animation, interval=50,  # 每帧间隔毫秒blit=True)# 如果提供了保存路径,则保存动画if save_path:# 需要安装ffmpeg才能保存为mp4anim.save(save_path, writer='ffmpeg', fps=20)plt.tight_layout()plt.show()if __name__ == "__main__":# 创建并显示贝塞尔曲线动画bezier_anim = BezierAnimation()# 如需保存动画,取消下面一行的注释并指定路径# 保存并显示动画bezier_anim.create_animation('bezier_animation.mp4')# 显示动画bezier_anim.create_animation()

弹簧动画模拟器

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>弹簧曲线模拟器</title><script src="https://cdn.tailwindcss.com"></script><link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"><script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script><script>tailwind.config = {theme: {extend: {colors: {primary: '#3B82F6',secondary: '#10B981',accent: '#8B5CF6',dark: '#1E293B',light: '#F8FAFC'},fontFamily: {sans: ['Inter', 'system-ui', 'sans-serif'],},}}}</script><style type="text/tailwindcss">@layer utilities {.card-shadow {box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.02);}.slider-thumb {@apply appearance-none w-5 h-5 rounded-full bg-primary cursor-pointer;}.fixed-dimension {width: 400px;height: 200px;flex-shrink: 0;overflow: hidden;}}input[type="range"]::-webkit-slider-thumb {@apply slider-thumb;}input[type="range"]::-moz-range-thumb {@apply slider-thumb;}</style>
</head>
<body class="bg-gray-50 font-sans text-dark"><div class="container mx-auto px-4 py-8 max-w-5xl"><header class="text-center mb-10"><h1 class="text-[clamp(1.8rem,4vw,2.5rem)] font-bold text-dark mb-2">弹簧曲线模拟器</h1><p class="text-gray-600 max-w-2xl mx-auto">调整参数以模拟不同弹簧特性,实时查看位移-时间曲线</p></header><div class="grid grid-cols-1 lg:grid-cols-3 gap-8"><!-- 控制面板 --><div class="lg:col-span-1"><div class="bg-white rounded-xl-6 card-shadowshadow"><h2 class="text-xl font-semibold mb-6 flex items-center"><i class="fa fa-sliders text-primary mr-2"></i>参数控制</h2><div class="space-y-6"><!-- 初始速度 --><div><div class="flex justify-between mb-1"><label for="velocity" class="text-sm font-medium text-gray-700">初始速度</label><span id="velocity-value" class="text-sm font-medium text-primary">-10</span></div><input type="range" id="velocity" min="-50" max="50" value="-10" class="w-full h-2 bg-gray-200 rounded-lg"><div class="flex justify-between text-xs text-gray-500 mt-1"><span>-50</span><span>0</span><span>50</span></div></div><!-- 质量 --><div><div class="flex justify-between mb-1"><label for="mass" class="text-sm font-medium text-gray-700">质量</label><span id="mass-value" class="text-sm font-medium text-primary">1.0</span></div><input type="range" id="mass" min="0.1" max="5" step="0.1" value="1.0" class="w-full h-2 bg-gray-200 rounded-lg"><div class="flex justify-between text-xs text-gray-500 mt-1"><span>0.1</span><span>2.5</span><span>5.0</span></div></div><!-- 刚度 --><div><div class="flex justify-between mb-1"><label for="stiffness" class="text-sm font-medium text-gray-700">刚度</label><span id="stiffness-value" class="text-sm font-medium text-primary">100</span></div><input type="range" id="stiffness" min="10" max="500" value="100" class="w-full h-2 bg-gray-200 rounded-lg"><div class="flex justify-between text-xs text-gray-500 mt-1"><span>10</span><span>250</span><span>500</span></div></div><!-- 阻尼 --><div><div class="flex justify-between mb-1"><label for="damping" class="text-sm font-medium text-gray-700">阻尼</label><span id="damping-value" class="text-sm font-medium text-primary">10</span></div><input type="range" id="damping" min="0" max="50" value="10" class="w-full h-2 bg-gray-200 rounded-lg"><div class="flex justify-between text-xs text-gray-500 mt-1"><span>0</span><span>25</span><span>50</span></div></div><!-- 初始位置 --><div><div class="flex justify-between mb-1"><label for="position" class="text-sm font-medium text-gray-700">初始位置</label><span id="position-value" class="text-sm font-medium text-primary">100</span></div><input type="range" id="position" min="-200" max="200" value="100" class="w-full h-2 bg-gray-200 rounded-lg"><div class="flex justify-between text-xs text-gray-500 mt-1"><span>-200</span><span>0</span><span>200</span></div></div><!-- 动画速度 --><div><div class="flex justify-between mb-1"><label for="speed" class="text-sm font-medium text-gray-700">动画速度</label><span id="speed-value" class="text-sm font-medium text-primary">1.0x</span></div><input type="range" id="speed" min="0.1" max="3" step="0.1" value="1.0" class="w-full h-2 bg-gray-200 rounded-lg"><div class="flex justify-between text-xs text-gray-500 mt-1"><span>慢速</span><span>正常</span><span>快速</span></div></div><div class="pt-4"><button id="simulate-btn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-2 px-4 rounded-lg transition-allduration-300 flex items-center justify-center"><i class="fa fa-play mr-2"></i> 开始模拟</button></div></div></div><div class="bg-white rounded-xl p-6 mt-6 card-shadow"><h2 class="text-xl font-semibold mb-4 flex items-center"><i class="fa fa-info-circle text-accent mr-2"></i>参数说明</h2><ul class="text-sm text-gray-600 space-y-2"><li class="flex items-start"><i class="fa fa-arrow-right text-primary mt-1 mr-2"></i><span><strong>初始速度</strong>:物体开始运动的速度,正值向右,负值向左</span></li><li class="flex items-start"><i class="fa fa-arrow-right text-primary mt-1 mr-2"></i><span><strong>质量</strong>:物体的质量,越大惯性越大</span></li><li class="flex items-start"><i class="fa fa-arrow-right text-primary mt-1 mr-2"></i><span><strong>刚度</strong>:弹簧的硬度,越大弹簧越硬</span></li><li class="flex items-start"><i class="fa fa fa-arrow-right text-primary mt-1 mr-2"></i><span><strong>阻尼</strong>:阻力大小,越大震荡衰减越快</span></li><li class="flex items-start"><i class="fa fa fa fa-arrow-right text-primary mt-1 mr-2"></i><span><strong>初始位置</strong>:物体的起始位置,偏离平衡位置的距离</span></li><li class="flex items-start"><i class="fa fa-arrow-right text-primary mt-1 mr-2"></i><span><strong>动画速度</strong>:控制动画播放速度,1.0x为正常速度</span></li></ul></div></div><!-- 可视化区域 --><div class="lg:col-span-2"><div class="bg-white rounded-xl p-6 card-shadow"><!-- 弹簧动画动画演示(固定大小) --><div class="mb-6"><h2 class="text-lg font-semibold mb-2 flex items-center"><i class="fa fa-film text-secondary mr-2"></i>弹簧动画</h2><div class="fixed-dimension border borderborderborderborderborder-gray-200 rounded-lg bg-gray-50 relative"><div id="spring-container" class="absolute inset-0 flex items-center px-4"><!-- 墙面 --><div class="w-3 h-12 bg-gray-400 rounded-sm"></div><!-- 弹簧 --><div id="spring" class="flex-1 h-3 flex justifyjustify-between items-centeritems-center mx-1"><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div><div class="h-full w-1 bg-primary"></div></div><!-- 物体 --><div id="mass-object" class="w-12 h-12 bg-accent rounded-full-full-fullflexitemsitemsitemsitems-centertext-white font-bold text-sm">m</div></div></div></div><!-- 曲线图 --><div><h2 class="text-lg font-semibold mb-2 flex items-center"><i class="fa fa-line-chart text-primary mr-2"></i>位移-时间曲线</h2><div class="fixed-dimension border border-gray-200 rounded-lg"><canvas id="spring-chart"></canvas></div></div></div></div></div><footer class="mt-12 text-center text-gray-500 text-sm"><p>弹簧曲线曲线模拟器基于胡克定律模拟:F = -kx - cv</p></footer></div><script>// 获取DOM元素const velocitySlider = document.getElementById('velocity');const velocityValue = document.getElementById('velocity-value');const massSlider = document.getElementById('mass');const massValue = document.getElementById('mass-value');const stiffnessSlider = document.getElementById('stiffness');const stiffnessValue = document.getElementById('stiffness-value');const dampingSlider = document.getElementById('damping');const dampingValue = document.getElementById('damping-value');const positionSlider = document.getElementById('position');const positionValue = document.getElementById('position-value');const speedSlider = document.getElementById('speed');const speedValue = document.getElementById('speed-value');const simulateBtn = document.getElementById('simulate-btn');const massObject = document.getElementById('mass-object');const springContainer = document.getElementById('spring-container');// 更新显示的参数值velocitySlider.addEventListener('input', () => {velocityValue.textContent = velocitySlider.value;});massSlider.addEventListener('input', () => {massValue.textContent = parseFloat(massSlider.value).toFixed(1);});stiffnessSlider.addEventListener('input', () => {stiffnessValue.textContent = stiffnessSlider.value;});dampingSlider.addEventListener('input', () => {dampingValue.textContent = dampingSlider.value;});positionSlider.addEventListener('input', () => {positionValue.textContent = positionSlider.value;});speedSlider.addEventListener('input', () => {speedValue.textContent = parseFloat(speedSlider.value).toFixed(1) + 'x';// 如果模拟正在运行,实时时更新速度if (simulation && simulation.isRunning) {simulation.speedFactor = parseFloat(speedSlider.value);}});// 初始化图表const ctx = document.getElementById('spring-chart').getContext('2d');let springChart = new Chart(ctx, {type: 'line',data: {labels: [],datasets: [{label: '位移',data: [],borderColor: '#3B82F6',backgroundColor: 'rgba(59, 130, 246, 0.1)',borderWidth: 2,fill: true,tension: 0.1,pointRadius: 0}]},options: {responsive: true,maintainAspectRatio: false,scales: {x: {title: {display: true,text: '时间 (ms)',font: {size: 10}},ticks: {font: {size: 8}}},y: {title: {display: true,text: '位移',font: {size: 10}},min: -250,max: 250,ticks: {font: {size: 8}}}},animation: false,interaction: {intersect: false,mode: 'index'},plugins: {legend: {labels: {font: {size: 10}}}}}});// 弹簧模拟类class SpringSimulation {constructor(params) {// 物理参数this.stiffness = params.stiffness;  // 刚度this.damping = params.damping;      // 阻尼this.mass = params.mass;            // 质量this.initialPosition = params.position; // 初始位置this.initialVelocity = params.velocity; // 初始速度this.speedFactor = params.speed || 1.0; // 动画速度因子// 状态变量this.position = params.position;    // 当前位置this.velocity = params.velocity;    // 当前速度this.time = 0;                      // 时间this.history = [];                  // 历史数据this.isRunning = false;             // 模拟是否运行this.animationFrameId = null;       // 动画帧IDthis.lastTime = 0;                  // 上一帧时间// 固定容器宽度(400px减去内边距和元素宽度)this.containerWidth = 400 - 30 - 48; // 固定计算,不受窗口影响this.centerX = this.containerWidth / 2; // 平衡位置}// 更新模拟状态update(currentTime) {if (!this.lastTime) this.lastTime = currentTime;// 应用速度因子调整时间增量const deltaTime = ((currentTime - this.lastTime) / 1000) * this.speedFactor;this.lastTime = currentTime;// 计算加速度: F = -kx - cv, a = F/mconst acceleration = (-this.stiffness * this.position - this.damping * this.velocity) / this.mass;// 更新速度和位置this.velocity += acceleration * deltaTime;this.position += this.velocity * deltaTime;// 记录时间和位置this.time += (currentTime - (this.lastTime - (currentTime - this.lastTime))) / 1000 * 1000;this.history.push({time: this.time,position: this.position});// 更新物体位置const objectX = this.centerX + this.position;massObject.style.transform = `translateX(${objectX}px)`;// 检查是否应该停止模拟if (Math.abs(this.velocity) < 0.1 && Math.abs(this.position) < 0.5) {this.stop();return false;}return true;}// 开始模拟start() {this.isRunning = true;this.lastTime = 0;this.history = [];this.time = 0;// 重置图表springChart.data.labels = [];springChart.data.datasets[0].data = [];springChart.update();// 初始位置const initialX = this.centerX + this.initialPosition;massObject.style.transform = `translateX(${initialX}px)`;// 动画循环const animate = (timestamp) => {if (!this.isRunning) return;const shouldContinue = this.update(timestamp);// 更新图表if (this.history.length % 2 === 0) {springChart.data.labels.push(Math.round(this.time));springChart.data.datasets[0].data.push(this.position);// 限制图表数据点数量if (springChart.data.labels.length > 100) {springChart.data.labels.shift();springChart.data.datasets[0].data.shift();}springChart.update();}if (shouldContinue) {this.animationFrameId = requestAnimationFrame(animate);}};this.animationFrameId = requestAnimationFrame(animate);}// 停止模拟stop() {this.isRunning = false;if (this.animationFrameId) {cancelAnimationFrame(this.animationFrameId);}}}// 模拟控制let simulation = null;simulateBtn.addEventListener('click', () => {// 如果已有模拟在运行,先停止if (simulation && simulation.isRunning) {simulation.stop();}// 获取参数const params = {velocity: parseFloat(velocitySlider.value),mass: parseFloat(massSlider.value),stiffness: parseFloat(stiffnessSlider.value),damping: parseFloat(dampingSlider.value),position: parseFloat(positionSlider.value),speed: parseFloat(speedSlider.value)};// 创建并启动新模拟simulation = new SpringSimulation(params);simulation.start();// 更新按钮文本simulateBtn.innerHTML = '<i class="fa fa-refresh mr-2"></i> 重新模拟';});// 初始位置设置window.addEventListener('load', () => {const containerWidth = 400 - 30 - 48; // 固定值const centerX = containerWidth / 2;const initialX = centerX + parseFloat(positionSlider.value);massObject.style.transform = `translateX(${initialX}px)`;});// 移除窗口大小变化的影响window.removeEventListener('resize', () => {});</script>
</body>
</html>

弹簧曲线curves.interpolatingSpring(10, 1, 228, 30)

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches# 设置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False  # 正确显示负号def calculate_spring_curve(mass, stiffness, damping, initial_velocity, duration=1.0, fps=100):"""计算弹簧曲线的数值点"""dt = 1.0 / fpstotal_frames = int(duration * fps)time_points = []position_points = []velocity_points = []position = 0.0  # 初始位置在平衡位置velocity = initial_velocityfor _ in range(total_frames):time = len(time_points) * dt# 计算力和加速度 (F = -kx - cv, a = F/m)force = -stiffness * position - damping * velocityacceleration = force / mass# 更新速度和位置velocity += acceleration * dtposition += velocity * dttime_points.append(time)position_points.append(position)velocity_points.append(velocity)return np.array(time_points), np.array(position_points), np.array(velocity_points)# 计算ArkUI interpolatingSpring(10, 1, 228, 30)的曲线数据
mass = 10.0
stiffness = 1.0
damping = 228.0
initial_velocity = 16.0time, position, velocity = calculate_spring_curve(mass, stiffness, damping, initial_velocity, duration=1.0
)# 创建图形
fig, ax = plt.subplots(figsize=(10, 6))
fig.suptitle('curves.interpolatingSpring(10, 1, 228, 30) 数值曲线', fontsize=16)# 绘制位移曲线
ax.plot(time, position, 'b-', linewidth=2, label='位移')
ax.set_xlabel('时间 (秒)')
ax.set_ylabel('位移')
ax.grid(True, alpha=0.3)
ax.set_xlim(0, max(time))
ax.set_ylim(min(position)*1.1, max(position)*1.1)# 标记关键 points
peak1_idx = np.argmax(position)
peak1_time = time[peak1_idx]
peak1_pos = position[peak1_idx]trough1_idx = np.argmin(position)
trough1_time = time[trough1_idx]
trough1_pos = position[trough1_idx]# 添加关键点标注
ax.plot(peak1_time, peak1_pos, 'ro', markersize=8)
ax.annotate(f'峰值: ({peak1_time:.2f}s, {peak1_pos:.2f})',xy=(peak1_time, peak1_pos),xytext=(peak1_time+0.05, peak1_pos+0.2),arrowprops=dict(arrowstyle='->', color='red'))ax.plot(trough1_time, trough1_pos, 'go', markersize=8)
ax.annotate(f'谷值: ({trough1_time:.2f}s, {trough1_pos:.2f})',xy=(trough1_time, trough1_pos),xytext=(trough1_time+0.05, trough1_pos-0.3),arrowprops=dict(arrowstyle='->', color='green'))# 添加参数说明
param_text = (f'参数: mass={mass}, stiffness={stiffness}\n'f'damping={damping}, initialVelocity={initial_velocity}')
plt.figtext(0.15, 0.01, param_text, fontsize=10, bbox=dict(facecolor='white', alpha=0.8, boxstyle='round,pad=0.5'))# 添加图例
ax.legend()plt.tight_layout(rect=[0, 0.05, 1, 0.95])
plt.show()
http://www.lryc.cn/news/604431.html

相关文章:

  • 【python 获取邮箱验证码】模拟登录并获取163邮箱验证码,仅供学习!仅供测试!仅供交流!
  • 【go】实现BMI计算小程序与GUI/WEB端实现
  • python案例分析:基于抖音评论的文本分析,使用svm算法进行情感分析以及LDA主题分析,准确率接近90%
  • 相亲小程序聊天与互动系统模块搭建
  • 鹏哥C语言_82_指针_指针数组
  • 构建智能体(Agent)时如何有效管理其上下文
  • 大语言模型(LLM)技术架构与工程实践:从原理到部署
  • 基于 Hadoop 生态圈的数据仓库实践 —— OLAP 与数据可视化(二)
  • 【Lua】元表常用属性
  • PCB学习笔记(一)
  • 【Python系列】如何安装无 GIL 的 Python 3.13
  • dify 添加 ollama 模型报错
  • AP-0316 全功能语音处理模组:技术解析与应用指南
  • MySQL的单行函数:
  • 【C++】适配器模式手搓STL的stack和queue
  • 字节跳动GR-3:可泛化、支持长序列复杂操作任务的机器人操作大模型(技术报告解读)
  • 探索 Linux 权限的奥秘:守护系统安全的关键
  • C++11 std::function 详解:通用多态函数包装器
  • Thales靶机攻略
  • 二叉树算法之【二叉树的层序遍历】
  • 关于mysql时间类型和java model的日期类型映射
  • “古法编程”到“vibe coding”的第一步:Zread助力一键生成项目说明书
  • 本地 docker 部署 HAR包分析工具 harviewer
  • 云原生环境里的显示变革:Docker虚拟浏览器与cpolar穿透技术实战
  • Web前端实战:Vue工程化+ElementPlus
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——8. AI赋能(下):在Qt中部署YOLOv8模型
  • 【CF】Day115——杂题 (构造 | 区间DP | 思维 + 贪心 | 图论 + 博弈论 | 构造 + 位运算 | 贪心 + 构造 | 计数DP)
  • 从0到1学PHP(七):PHP 与 HTML 表单:实现数据交互
  • useRouteLeaveConfirm 路由离开确认弹窗 Hook
  • ECCV | 2024 | LocalMamba:具有窗口选择性扫描的视觉状态空间模型