系统学习iOS动画 —— 动画组, 时间控制, 图层弹簧动画

2023-12-07 03:59

本文主要是介绍系统学习iOS动画 —— 动画组, 时间控制, 图层弹簧动画,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 动画组和时间控制

动画组可以对动画进行分组,可以向组中添加多个动画并同时调整持续时间,代理和timingFunction等属性。 对动画进行分组会产生简化的代码,并确保所有动画将作为一个实体单元同步。

这里创建一个CAAnimationGroup

    let formGroup = CAAnimationGroup()formGroup.duration = 0.5formGroup.fillMode = .backwards

然后创建一个向右移动的动画和变化透明度的动画

  let flyRight = CABasicAnimation(keyPath: "position.x")flyRight.fromValue = -view.bounds.size.width/2flyRight.toValue = view.bounds.size.width/2let fadeFieldIn = CABasicAnimation(keyPath: "opacity")fadeFieldIn.fromValue = 0.25fadeFieldIn.toValue = 1.0

为formGroup的animations赋值

    formGroup.animations = [flyRight, fadeFieldIn]

然后为控件添加上这个动画

heading.layer.add(formGroup, forKey: nil)formGroup.delegate = selfformGroup.setValue("form", forKey: "name")formGroup.setValue(username.layer, forKey: "layer")formGroup.beginTime = CACurrentMediaTime() + 0.3username.layer.add(formGroup, forKey: nil)formGroup.setValue(password.layer, forKey: "layer")formGroup.beginTime = CACurrentMediaTime() + 0.4password.layer.add(formGroup, forKey: nil)

接下来为登陆按钮添加动画组

 let groupAnimation = CAAnimationGroup()groupAnimation.beginTime = CACurrentMediaTime() + 0.5groupAnimation.duration = 0.5groupAnimation.fillMode = .backwardslet scaleDown = CABasicAnimation(keyPath: "transform.scale")scaleDown.fromValue = 3.5scaleDown.toValue = 1.0let rotate = CABasicAnimation(keyPath: "transform.rotation")rotate.fromValue = .pi / 4.0rotate.toValue = 0.0let fade = CABasicAnimation(keyPath: "opacity")fade.fromValue = 0.0fade.toValue = 1.0groupAnimation.animations = [scaleDown, rotate, fade]groupAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)loginButton.layer.add(groupAnimation, forKey: nil)

2. 图层弹簧动画

下面解释来自于Andy_Ron

2.1 阻尼谐振子

阻尼谐振子,Damped harmonic oscillators(直译就是,逐渐衰弱的振荡器),可以理解为逐渐衰减的振动。
UIKit API简化了弹簧动画的制作,不需要了解它们的原理就可以很方便的使用。 但是,由于您现在是核心动画专家,因此您需要深入研究细节。
钟摆,理想状况下钟摆是不停的摆动,像下面的一样:
在这里插入图片描述
对应的运动轨迹图就像:
在这里插入图片描述

但现实中由于能量的损耗,钟摆的摇摆的幅度会逐渐减小:

在这里插入图片描述
对应的运动轨迹:

在这里插入图片描述
这就是一个阻尼谐振子 。
钟摆停下来所需的时间长度,以及最终振荡器图形的方式取决于振荡系统的以下参数:

  • 阻尼(damping):由于空气摩擦、机械摩擦和其他作用在系统上的外部减速力。

  • 质量(mass):摆锤越重,摆动的时间越长。

  • 刚度(stiffness):振荡器的“弹簧”越硬(钟摆的“弹簧”是指地球的引力),钟摆摆动越困难,系统停下来也越快。想象一下,如果在月球或木星上使用这个钟摆;在低重力和高重力情况下的运动将是完全不同的。

  • 初始速度(initial velocity):推一下钟摆。

2.2 视图弹簧动画 vs 图层弹簧动画

UIKit以动态方式调整所有其他变量,使系统在给定的持续时间内稳定下来。 这就是为什么UIKit弹簧动画有时有点被迫 停下来的感觉。 如果仔细观察会发现UIKit动画有点不太自然。
幸运的是,核心允许通过CASpringAnimation类为图层属性创建合适的弹簧动画。 CASpringAnimation在幕后为UIKit创建弹簧动画,但是当我们直接调用它时,可以设置系统的各种变量,让动画自己稳定下来。 这种方法的缺点是不能设置固定的持续时间(duration);持续时间取决于提供的其它变量,然后系统计算所得。

CASpringAnimation的一些属性(对应之前振荡系统的参数):

  • damping 阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快
  • mass 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
  • stiffness 刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
  • initialVelocity 初始速率,动画视图的初始速度大小。速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反

2.3 图层弹簧动画应用

将之前在animationDidStop的动画

  let pulse = CABasicAnimation(keyPath: "transform.scale")pulse.fromValue = 1.25pulse.toValue = 1.0pulse.duration = 0.25layer?.add(pulse, forKey: nil)

修改为

     let pulse = CASpringAnimation(keyPath: "transform.scale")pulse.damping = 7.5pulse.fromValue = 1.25pulse.toValue = 1.0pulse.duration = pulse.settlingDurationlayer?.add(pulse, forKey: nil)

运行后发现动画明显自然很多
继续将其他的动画也修改好:
将tintBackgroundColor里面的动画改为:

func tintBackgroundColor(layer: CALayer, toColor: UIColor) {let tint = CASpringAnimation(keyPath: "backgroundColor")tint.damping = 5.0tint.initialVelocity = -10.0tint.fromValue = layer.backgroundColortint.toValue = toColor.cgColortint.duration = tint.settlingDurationlayer.add(tint, forKey: nil)layer.backgroundColor = toColor.cgColor
}

将roundCorners里面的动画改为:

func roundCorners(layer: CALayer, toRadius: CGFloat) {let round = CASpringAnimation(keyPath: "cornerRadius")round.damping = 5.0round.fromValue = layer.cornerRadiusround.toValue = toRadiusround.duration = round.settlingDurationlayer.add(round, forKey: nil)layer.cornerRadius = toRadius
}

在textFieldDidEndEditing里面添加判断,如果输入小于5,那么就添加动画。

 func textFieldDidEndEditing(_ textField: UITextField) {guard let text = textField.text else { return }if text.count < 5 {// add animations herelet jump = CASpringAnimation(keyPath: "position.y")jump.initialVelocity = 100.0jump.mass = 10.0jump.stiffness = 1500.0jump.damping = 50.0jump.fromValue = textField.layer.position.y + 1.0jump.toValue = textField.layer.position.yjump.duration = jump.settlingDurationtextField.layer.add(jump, forKey: nil)textField.layer.borderWidth = 3.0textField.layer.borderColor = UIColor.clear.cgColorlet flash = CASpringAnimation(keyPath: "borderColor")flash.damping = 7.0flash.stiffness = 200.0flash.fromValue = UIColor(red: 1.0, green: 0.27, blue: 0.0, alpha: 1.0).cgColorflash.toValue = UIColor.white.cgColorflash.duration = flash.settlingDurationtextField.layer.add(flash, forKey: nil)textField.layer.cornerRadius = 5}}

2.4 弹簧动画属性

CASpringAnimation预定义的弹簧动画属性的默认值分别是:

damping: 10.0
mass: 1.0
stiffness: 100.0
initialVelocity: 0.0
  • initialVelocity: 附着在弹簧上的物体的初始速度。默认为零,表示一个不动的对象。负值表示对象远离弹簧附着点,正值表示对象向弹簧附着点移动。
  • mass:附着在弹簧末端的物体的质量。必须大于0。默认为1。数值越大,对象跳跃的更高,并且稳定下来的时间更久了。
  • stiffness : 弹簧刚度系数。必须大于0。默认为100。让跳跃高度降低。
  • damping: 阻尼系数。必须大于或等于0,默认为10。增加阻尼系数可以让动画更快地稳定下来。

完整代码:

//
//  ViewController.swift
//  AirLoginAnimation
//
//  Created by aibus on 2021/10/27.
//import UIKitclass ViewController: UIViewController {let screenWidth = UIScreen.main.bounds.size.widthlet screenHeight = UIScreen.main.bounds.size.heightlet titleLabel = UILabel()let backgroundImage = UIImageView()let usernameTextField = TextField()let passwordTextField = TextField()let loginButton = UIButton()let cloud1 = UIImageView()let cloud2 = UIImageView()let cloud3 = UIImageView()let cloud4 = UIImageView()let spinner = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.large)let status = UIImageView(image: UIImage(named: "banner"))let label = UILabel()let info = UILabel()let messages = ["Connecting ...", "Authorizing ...", "Sending credentials ...", "Failed"]var statusPosition = CGPoint.zerooverride func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view.view.addSubview(backgroundImage)view.addSubview(titleLabel)view.addSubview(usernameTextField)view.addSubview(passwordTextField)view.addSubview(loginButton)view.addSubview(cloud1)view.addSubview(cloud2)view.addSubview(cloud3)view.addSubview(cloud4)loginButton.addSubview(spinner)let textFieldWidth = screenWidth - 60let buttonWidth = 260backgroundImage.image = UIImage(named: "bg-sunny")backgroundImage.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)titleLabel.text = "Bahama Login"titleLabel.textColor = .whitetitleLabel.font = UIFont.systemFont(ofSize: 28)let titleWidth = titleLabel.intrinsicContentSize.widthtitleLabel.frame = CGRect(x: (screenWidth - titleWidth) / 2 , y: 120, width: titleWidth, height: titleLabel.intrinsicContentSize.height)usernameTextField.backgroundColor = .whiteusernameTextField.layer.cornerRadius = 5usernameTextField.placeholder = "  Username"usernameTextField.delegate = selfusernameTextField.frame = CGRect(x: 30, y: 202, width: textFieldWidth, height: 40)passwordTextField.backgroundColor = .whitepasswordTextField.layer.cornerRadius = 5passwordTextField.placeholder = "  Password"passwordTextField.delegate = selfpasswordTextField.frame = CGRect(x: 30, y: 263, width: textFieldWidth, height: 40)loginButton.frame = CGRect(x: (Int(screenWidth) - buttonWidth) / 2, y: 343, width: buttonWidth, height: 50)loginButton.setTitle("Login", for: .normal)loginButton.setTitleColor(.red, for: .normal)loginButton.layer.cornerRadius = 5loginButton.backgroundColor = UIColor(red: 0.63, green: 0.84, blue: 0.35, alpha: 1.0)loginButton.addTarget(self, action: #selector(handleLogin), for: .touchUpInside)spinner.frame = CGRect(x: -20.0, y: 6.0, width: 20.0, height: 20.0)spinner.startAnimating()spinner.alpha = 0.0cloud1.frame = CGRect(x: -120, y: 79, width: 160, height: 50)cloud1.image = UIImage(named: "bg-sunny-cloud-1")cloud2.frame = CGRect(x: 256, y: 213, width: 160, height: 50)cloud2.image = UIImage(named: "bg-sunny-cloud-2")cloud3.frame = CGRect(x: 284, y: 503, width: 74, height: 35)cloud3.image = UIImage(named: "bg-sunny-cloud-3")cloud4.frame = CGRect(x:22 , y: 545, width: 115, height: 50)cloud4.image = UIImage(named: "bg-sunny-cloud-4")status.isHidden = truestatus.center = loginButton.centerview.addSubview(status)label.frame = CGRect(x: 0.0, y: 0.0, width: status.frame.size.width, height: status.frame.size.height)label.font = UIFont(name: "HelveticaNeue", size: 18.0)label.textColor = UIColor(red: 0.89, green: 0.38, blue: 0.0, alpha: 1.0)label.textAlignment = .centerstatus.addSubview(label)statusPosition = status.centerinfo.frame = CGRect(x: 0.0, y: loginButton.center.y + 60.0,  width: view.frame.size.width, height: 30)info.backgroundColor = UIColor.clearinfo.font = UIFont(name: "HelveticaNeue", size: 12.0)info.textAlignment = .centerinfo.textColor = UIColor.whiteinfo.text = "Tap on a field and enter username and password"view.insertSubview(info, belowSubview: loginButton)}override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)let formGroup = CAAnimationGroup()formGroup.duration = 0.5formGroup.fillMode = .backwardslet flyRight = CABasicAnimation(keyPath: "position.x")flyRight.fromValue = -view.bounds.size.width/2flyRight.toValue = view.bounds.size.width/2let fadeFieldIn = CABasicAnimation(keyPath: "opacity")fadeFieldIn.fromValue = 0.25fadeFieldIn.toValue = 1.0formGroup.animations = [flyRight, fadeFieldIn]titleLabel.layer.add(formGroup, forKey: nil)formGroup.delegate = selfformGroup.setValue("form", forKey: "name")formGroup.setValue(usernameTextField.layer, forKey: "layer")formGroup.beginTime = CACurrentMediaTime() + 0.3usernameTextField.layer.add(formGroup, forKey: nil)formGroup.setValue(passwordTextField.layer, forKey: "layer")formGroup.beginTime = CACurrentMediaTime() + 0.4passwordTextField.layer.add(formGroup, forKey: nil)}override func viewDidAppear(_ animated: Bool) {super.viewDidAppear(animated)let fadeIn = CABasicAnimation(keyPath: "opacity")fadeIn.fromValue = 0.0fadeIn.toValue = 1.0fadeIn.duration = 0.5fadeIn.fillMode = .backwardsfadeIn.beginTime = CACurrentMediaTime() + 0.5cloud1.layer.add(fadeIn, forKey: nil)fadeIn.beginTime = CACurrentMediaTime() + 0.7cloud2.layer.add(fadeIn, forKey: nil)fadeIn.beginTime = CACurrentMediaTime() + 0.9cloud3.layer.add(fadeIn, forKey: nil)fadeIn.beginTime = CACurrentMediaTime() + 1.1cloud4.layer.add(fadeIn, forKey: nil)let groupAnimation = CAAnimationGroup()groupAnimation.beginTime = CACurrentMediaTime() + 0.5groupAnimation.duration = 0.5groupAnimation.fillMode = .backwardslet scaleDown = CABasicAnimation(keyPath: "transform.scale")scaleDown.fromValue = 3.5scaleDown.toValue = 1.0let rotate = CABasicAnimation(keyPath: "transform.rotation")rotate.fromValue = .pi / 4.0rotate.toValue = 0.0let fade = CABasicAnimation(keyPath: "opacity")fade.fromValue = 0.0fade.toValue = 1.0groupAnimation.animations = [scaleDown, rotate, fade]groupAnimation.timingFunction = CAMediaTimingFunction(name: .easeIn)loginButton.layer.add(groupAnimation, forKey: nil)animateCloud(layer: cloud1.layer)animateCloud(layer: cloud2.layer)animateCloud(layer: cloud3.layer)animateCloud(layer: cloud4.layer)let flyLeft = CABasicAnimation(keyPath: "position.x")flyLeft.fromValue = info.layer.position.x + view.frame.size.widthflyLeft.toValue = info.layer.position.xflyLeft.duration = 5.0info.layer.add(flyLeft, forKey: "infoappear")let fadeLabelIn = CABasicAnimation(keyPath: "opacity")fadeLabelIn.fromValue = 0.2fadeLabelIn.toValue = 1.0fadeLabelIn.duration = 4.5info.layer.add(fadeLabelIn, forKey: "fadein")}@objc func handleLogin() {view.endEditing(true)UIView.animate(withDuration: 1.5, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: [], animations: {self.loginButton.bounds.size.width += 80.0}, completion: nil)UIView.animate(withDuration: 0.33, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.0, options: [], animations: {self.loginButton.center.y += 60.0self.spinner.center = CGPoint(x: 40.0,y: self.loginButton.frame.size.height/2)self.spinner.alpha = 1.0}, completion: { _ inself.showMessage(index:0)})let tintColor = UIColor(red: 0.85, green: 0.83, blue: 0.45, alpha: 1.0)tintBackgroundColor(layer: loginButton.layer, toColor: tintColor)roundCorners(layer: loginButton.layer, toRadius: 25.0)}func showMessage(index: Int) {label.text = messages[index]UIView.transition(with: status, duration: 0.33, options: [.curveEaseOut, .transitionFlipFromTop], animations: {self.status.isHidden = false}, completion: { _ in//transition completiondelay(2.0) {if index < self.messages.count-1 {self.removeMessage(index: index)} else {self.resetForm()}}})}func removeMessage(index: Int) {UIView.animate(withDuration: 0.33, delay: 0.0, options: [], animations: {self.status.center.x += self.view.frame.size.width}, completion: { _ inself.status.isHidden = trueself.status.center = self.statusPositionself.showMessage(index: index+1)})}func resetForm() {UIView.transition(with: status, duration: 0.2, options: .transitionFlipFromTop, animations: {self.status.isHidden = trueself.status.center = self.statusPosition}, completion: { _ inlet tintColor = UIColor(red: 0.63, green: 0.84, blue: 0.35, alpha: 1.0)tintBackgroundColor(layer: self.loginButton.layer, toColor: tintColor)roundCorners(layer: self.loginButton.layer, toRadius: 10.0)})UIView.animate(withDuration: 0.2, delay: 0.0, options: [], animations: {self.spinner.center = CGPoint(x: -20.0, y: 16.0)self.spinner.alpha = 0.0self.loginButton.bounds.size.width -= 80.0self.loginButton.center.y -= 60.0}, completion: nil)}func animateCloud(layer: CALayer) {//1let cloudSpeed = 60.0 / Double(view.layer.frame.size.width)let duration: TimeInterval = Double(view.layer.frame.size.width - layer.frame.origin.x) * cloudSpeed//2let cloudMove = CABasicAnimation(keyPath: "position.x")cloudMove.duration = durationcloudMove.toValue = self.view.bounds.size.width + layer.bounds.width/2cloudMove.delegate = selfcloudMove.fillMode = .forwardscloudMove.setValue("cloud", forKey: "name")cloudMove.setValue(layer, forKey: "layer")layer.add(cloudMove, forKey: nil)}}func delay(_ seconds: Double, completion: @escaping ()->Void) {DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: completion)
}func tintBackgroundColor(layer: CALayer, toColor: UIColor) {let tint = CASpringAnimation(keyPath: "backgroundColor")tint.damping = 5.0tint.initialVelocity = -10.0tint.fromValue = layer.backgroundColortint.toValue = toColor.cgColortint.duration = tint.settlingDurationlayer.add(tint, forKey: nil)layer.backgroundColor = toColor.cgColor
}func roundCorners(layer: CALayer, toRadius: CGFloat) {let round = CASpringAnimation(keyPath: "cornerRadius")round.damping = 5.0round.fromValue = layer.cornerRadiusround.toValue = toRadiusround.duration = round.settlingDurationlayer.add(round, forKey: nil)layer.cornerRadius = toRadius
}class TextField: UITextField {let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)override open func textRect(forBounds bounds: CGRect) -> CGRect {return bounds.inset(by: padding)}override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {return bounds.inset(by: padding)}override open func editingRect(forBounds bounds: CGRect) -> CGRect {return bounds.inset(by: padding)}
}extension ViewController: CAAnimationDelegate {func animationDidStop(_ anim: CAAnimation,finished flag: Bool) {print("animation did finish")guard let name = anim.value(forKey: "name") as? String else {return}if name == "form" {//form field foundlet layer = anim.value(forKey: "layer") as? CALayeranim.setValue(nil, forKey: "layer")let pulse = CASpringAnimation(keyPath: "transform.scale")pulse.damping = 7.5pulse.fromValue = 1.25pulse.toValue = 1.0pulse.duration = pulse.settlingDurationlayer?.add(pulse, forKey: nil)}if name == "cloud" {if let layer = anim.value(forKey: "layer") as? CALayer {anim.setValue(nil, forKey: "layer")layer.position.x = -layer.bounds.width/2delay(0.5) {self.animateCloud(layer: layer)}}}}
}
extension ViewController: UITextFieldDelegate {func textFieldDidBeginEditing(_ textField: UITextField) {guard let runningAnimations = info.layer.animationKeys() else {return}print(runningAnimations)info.layer.removeAnimation(forKey: "infoappear")info.layer.removeAnimation(forKey: "fadein")}func textFieldDidEndEditing(_ textField: UITextField) {guard let text = textField.text else { return }if text.count < 5 {// add animations herelet jump = CASpringAnimation(keyPath: "position.y")jump.initialVelocity = 100.0jump.mass = 10.0jump.stiffness = 1500.0jump.damping = 50.0jump.fromValue = textField.layer.position.y + 1.0jump.toValue = textField.layer.position.yjump.duration = jump.settlingDurationtextField.layer.add(jump, forKey: nil)textField.layer.borderWidth = 3.0textField.layer.borderColor = UIColor.clear.cgColorlet flash = CASpringAnimation(keyPath: "borderColor")flash.damping = 7.0flash.stiffness = 200.0flash.fromValue = UIColor(red: 1.0, green: 0.27, blue: 0.0, alpha: 1.0).cgColorflash.toValue = UIColor.white.cgColorflash.duration = flash.settlingDurationtextField.layer.add(flash, forKey: nil)textField.layer.cornerRadius = 5}}
}

这篇关于系统学习iOS动画 —— 动画组, 时间控制, 图层弹簧动画的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/464408

相关文章

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

2.1/5.1和7.1声道系统有什么区别? 音频声道的专业知识科普

《2.1/5.1和7.1声道系统有什么区别?音频声道的专业知识科普》当设置环绕声系统时,会遇到2.1、5.1、7.1、7.1.2、9.1等数字,当一遍又一遍地看到它们时,可能想知道它们是什... 想要把智能电视自带的音响升级成专业级的家庭影院系统吗?那么你将面临一个重要的选择——使用 2.1、5.1 还是

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Ubuntu系统怎么安装Warp? 新一代AI 终端神器安装使用方法

《Ubuntu系统怎么安装Warp?新一代AI终端神器安装使用方法》Warp是一款使用Rust开发的现代化AI终端工具,该怎么再Ubuntu系统中安装使用呢?下面我们就来看看详细教程... Warp Terminal 是一款使用 Rust 开发的现代化「AI 终端」工具。最初它只支持 MACOS,但在 20

windows系统下shutdown重启关机命令超详细教程

《windows系统下shutdown重启关机命令超详细教程》shutdown命令是一个强大的工具,允许你通过命令行快速完成关机、重启或注销操作,本文将为你详细解析shutdown命令的使用方法,并提... 目录一、shutdown 命令简介二、shutdown 命令的基本用法三、远程关机与重启四、实际应用

Debian如何查看系统版本? 7种轻松查看Debian版本信息的实用方法

《Debian如何查看系统版本?7种轻松查看Debian版本信息的实用方法》Debian是一个广泛使用的Linux发行版,用户有时需要查看其版本信息以进行系统管理、故障排除或兼容性检查,在Debia... 作为最受欢迎的 linux 发行版之一,Debian 的版本信息在日常使用和系统维护中起着至关重要的作

Python 标准库time时间的访问和转换问题小结

《Python标准库time时间的访问和转换问题小结》time模块为Python提供了处理时间和日期的多种功能,适用于多种与时间相关的场景,包括获取当前时间、格式化时间、暂停程序执行、计算程序运行时... 目录模块介绍使用场景主要类主要函数 - time()- sleep()- localtime()- g

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主