C#实现炫酷启动图-动态进度条效果

一、简述

最近接到一个新需求,让做一个动效进度条。

由于我们的产品比较大,在软件启动的时候会消耗比较长的时间,原生的进度条已经不能满足我们的需求,这里我们就需要一个会动的进度条,效果如下图所示。

光效进度条主要是做了一个进度动画,在已完成的部分上进行快速的迭代渲染,给用户一种直观感受,我们的软件一直努力加载,它还活着。

有了这个进度条之后,当我们的进度从40%到50%这个持续的过程中,界面再也不会出现假死的情况,是不是很完美呢。。。

下面我就来分析下这个动效进度条是怎么做的

二、动效进度条

如效果图所示,光效进度条不同于一般的进度条,他在基础的任务进度之上还添加了一层光效,主要是想告诉用户我们的软件一直在努力运行,请在耐心的等待一下下。

我自己在做功能的时候,往往喜欢先做一个测试demo,然后在把做好的功能集成在正式环境,这个功能也不列外,如第一节中展示的效果图,就是测试demo的样子,虽然很丑,但是基础功能是有的。

现在的很多软件,在进度展示上都有了比较绚丽的效果,比如压缩软件,解压文件的时候都会有动效进度条,用过的同学应该都知道长啥样,而我们的光效进度条跟这个效果差不多,除此之外我们还提供了另一种动效,延迟动效,他们两个在一定程度上都展示了更友好的进度效果。

在开始分析功能前,首先我们先来考虑下我们的需求:动效进度条,也就是说在原来的进度条基础上需要添加实时动画,让进度条看起来更炫酷一些,除了光效进度条以外,还有一种延迟到达进度条,也属于动态进度条。

延迟动效、说直白一点儿就是延迟到达,当我们设置了进度从10%到20%时,程序模拟了一个渐进的过程,使用一个时间段走完了这10%的进度。

下面我们分别来介绍这两种进度条的实现

实现炫酷的进度条我们可以从QWidget自定义开始写,也就是说从头开始写,但通常我们不这样干,因为这样可能会写出无穷无尽的bug,而且现有的轮子已经很稳定了,为什么还要造呢。

1、光效进度条

光效进度条我们使用了一个小技巧,采用一个简单的办法实现,我们的光效进度条控件继承自Qt原生进度条类QProgressBar,在新类中我们只需要在Qt绘制完原生进度条之后,补画动效即可。

a、paintEvent函数

paintEvent函数是Qt的绘制函数,当界面刷新的时候,这个接口函数就会被调用,因此我们需要重写这个接口,首先调用父窗口的绘制方法,然后我们在绘制我们自己的动效,代码如下所示

QProgressBar::paintEvent(event);
drawCache绘制动效

b、drawCache绘制动效

绘制动效的时候,我们需要知道动效的绘制区域,这个地方我们需要主动去解析qss的一些参数,Qt的style()->subElementRect这个接口刚好可以拿到我们需要的信息

下面简单描述下我们的实现流程

•首先我们获取进度条的几何大小和中间进度的几何大小,这样的话我们就可以计算出来各border的数值
•然后根据我们当前的value值就可以计算出进度条已经走过(就是值小于我们设定的区域)的几何大小
•我们的光效将是跑在第二步计算出来的区域上,一直循环迭代
•内存里我们维护一个cacheValue,这个值在每次界面刷新的时候递增,但是不能大于第二步的value值,cacheValue将是我们动效绘制的一个关键参数,他表示了动效绘制的长度
•构造一个渐变刷子,设置给QPainter
•绘制动效

上下大致描述了下绘制动效的一个流程,下面送上具体代码,由于篇幅原因,代码我进行了部分伪代码处理。

void GMPProgressBar::drawCache()
{
 QStyleOptionProgressBarV2 opt;
 QRect outerRect = style()->subElementRect(QStyle::SE_ProgressBarGroove, &opt, this);
 QRect innerRect = style()->subElementRect(QStyle::SE_ProgressBarContents, &opt, this);
 QMargins borders(构造一个QMargins);

QRectF rect(动效绘制区域);

 if (m_iCacheValue != 0)
 {
  QPainter painter(this);
  QLinearGradient gradient(构造绘图刷子);
  painter.setBrush(gradient);
  painter.drawRoundedRect(rect, 2, 2);
 }
}

c、定时刷新

由于我们的动效是需要主动去刷新的,因此我们需要声明一个定时器,然后定时去刷新,实现代码可能像下面这样

connect(m_pCacheTimer, &QTimer::timeout, this, [this]{
  if (TM_CACHE == m_mode)
  {
   ++m_iCacheValue;
   repaint();
  }else
  {
   m_pCacheTimer->stop();
  }
 });

定时器只需要在我们第一次设置进度条值的时候启动,或者当我们设置一个新的值,而定时器没有启动,我们就需要去激活定时器。

TM_CACHE模式即是我们的动效模式,TM_SMOOTH模式则是我们的延迟到达模式

connect(this, &QProgressBar::valueChanged, [this](int value){//TM_CACHE模式下 启动动画时机
  if (!m_pCacheTimer->isActive() && value != 0 && TM_CACHE == m_mode)
  {
   m_pCacheTimer->start(m_iRefreshleveling);
  }
 });

动效进度条效果如下图所示

2、延迟到达进度条

动效进度条可能更适用于启动界面,但是也有一些时候,我们可能需要更平缓的一个加载曲线,例如安装软件、卸载软件的时候。

a、setValue

延迟到达进度条和动效进度条的实现方式就有所差别了,对于实现延迟到达进度条,我们这里重写了setValue函数,当外部调用该接口设置value值时,我们并没有立即去设置当前值,而是使用了一个时间段去完成这个值得刷新。

•外部调用setValue时,我们首先计算出我们应该绘制的最大宽度PixelMax、当前已经绘制到的最大宽度cacheValue和我们的步长
•设置定时器刷新频率,并重启定时器
•定时器刷新时,cacheValue自增我们的步长
•调用父类的QProgressBar::setValue接口设置值

b、定时器

延迟达到功能的的定时器和之前我们什么的动效定时器可以混用一个,我们定时器刷新的时候,针对不同的动画模式,我们执行不同的的代码即可,实现代码可能像下面这样

connect(m_pCacheTimer, &QTimer::timeout, this, [this]{
 if (TM_CACHE == m_mode)
 {
  ++m_iCacheValue;
  repaint();
 }
 else if (TM_SMOOTH == m_mode)
 {
  changeSmooth();
 }
 else
 {
  m_pCacheTimer->stop();
 }
});

延迟到达进度条效果如下图所示

3、接口说明

光效进度条类对外只暴露了3个接口,分别是设置动画模式、动画时长和刷新频率

特别需要注意的是,我们这里重写了父类的setValue接口,这意味着我们不能使用多态来操作这个接口

void setTransitionMode(TransitionMode mode);//设置动画模式
void setSmoothDuration(int duration);//设置刷新总时长 模式为TM_SMOOTH时有效
void setRefreshleveling(int rate);//设置刷新频率 每次更改TransitionMode之后会变为默认值

a、修改动画模式

修改动画模式的时候,我们需要清空内存中的所有数据,并把value值设为0。

void GMPProgressBar::setTransitionMode(TransitionMode mode)
{
 if (m_mode == mode)
 {
  return;
 }
 m_mode = mode;
 clearData();
 QProgressBar::setValue(0);
}

b、其他接口

设置刷新时长和频率接口都比较简单,不做特别说明

特别注意:这个3个接口最好是在动画启动前设置,动画开始后尽量不要去调用

三、启动图

第二节我们主要是讲述了怎么做一个动效进度条,这一节我们来做一个启动图页面,把这个动效使用进去。

启动图不是我们主要分析的内容,这个我就简单说下这个类的实现方式和一些借口说明

1、实现思路

Qt已经给我们提供了一个QSplashScreen,但是使用起来还是特别有限,因此这里我把Qt的源码直接进行了二次开发

•首先Qt的原生实现方式基本都被移植了出来
•启动图使用了简单的上下布局,上面是一张我自绘制的图片,放在了一个QLabel上,下面是动效进度条
•自绘制的图片上包括了,产品名称、logo、背景图等等

2、背景图切换

当我们调用setPixmap设置背景图时,如果我们指定了多张图,我将会启动一个定时器,在指定时长后重新构造一张大的背景图,并添加到启动窗口上

这里主要说明下背景图是怎么构造出来的,代码如下所示

a、根据背景图构造启动图大小,并移动到屏幕中间

m_currentPixmap = m_lstPixmaps.at(m_iCurrentIndex);
QRect size(QPoint(), m_currentPixmap.size() / m_currentPixmap.devicePixelRatio());
size.setHeight(size.height() + StatusBarHeight);
setFixedSize(size.size());
m_pProgressBar->setFixedWidth(size.width() / 8 * 3);
move(QApplication::desktop()->screenGeometry().center() - size.center());

b、绘制程序logo

QPainter painter(&m_currentPixmap);
painter.drawPixmap(m_startPos, m_logo);

c、绘制标题栏

painter.save();
painter.setFont(m_titleFont);
QFontMetrics fontMetrics(m_titleFont);
int textWidth = fontMetrics.width(m_strWindowTitle);
int textHegith = m_logo.height();
QRect textTect = QRect(m_startPos + QPoint(13 + m_logo.width(), 0), QSize(textWidth, textHegith));
painter.drawText(textTect, m_strWindowTitle, QTextOption(Qt::AlignCenter));
painter.restore();

d、设置给QLabel背景图

m_pWindowBackground->setPixmap(m_currentPixmap);

启动图的效果这里就不在贴图了,第三节上的两个gif图都是最终的启动图效果

四、测试

最后就是测试代码了,主要是模拟了程序的一个加载过程

1、构造启动图

首先我们构造一个启动图对象,并设置程序logo和动画模式

GMPSplashScreen * screen = new GMPSplashScreen(QPixmap(":/splashScreen/start.png"));
screen->show();
screen->setLogo(QIcon("logo.ico").pixmap(48, 48));
screen->setTransitionMode(GMPProgressBar::TM_CACHE);

2、背景图

设置背景图,并设置背景图更换时间间隔

QList<QPixmap> lstPixmap;
lstPixmap.append(QPixmap(":/splashScreen/start.png"));
lstPixmap.append(QPixmap(":/splashScreen/start.jpg"));
screen->setPixmap(lstPixmap, 2000);

3、其他信息

设置程序的提示信息和标题栏

screen->showMessage("Established connections", 0);
screen->setTitle(QStringLiteral("广联达BIM土建计量GTJ2018"));

4、事件循环

这里写了一个死循环,主要是为了模拟程序的一个加载过程,每隔10ms处理下界面刷新事件

a.processEvents();
while (1)
{
 QTest::qSleep(10);
 a.processEvents();
}
splashScreen w;
w.show();
screen->finish(&w);

总结

以上所述是小编给大家介绍的C#实现炫酷启动图-动态进度条效果,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

时间: 2019-05-09

C# Winform下载文件并显示进度条的实现代码

方法一: 效果如下图所示: 代码如下: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace WinShowDown { public partial class F

c#进度条 progressBar 使用方法的小例子

复制代码 代码如下: using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using Mes.Core; namespace HCMDoImport{    public partial class ProcessBarForm : B

C#中常使用进度条的代码

复制代码 代码如下: using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Diagnostics; using System.Threading; namespace WindowsApplication2 { /// <summary> /// 

C#实现带进度条的ListView

推荐阅读:ListView 百分比进度条(delphi版) 对于已经有的组件,可以直接添加进来,添加后要先运行一下,然后会在工具箱内找到相应控件. 1.首先编写组件,然后将组件添加到工具箱内 编写代码如下: public partial class ListViewEx : System.Windows.Forms.ListView { public ListViewEx() { InitializeComponent(); } //C# listview进度条显示 private Color

c#根据文件大小显示文件复制进度条实例

初学者,照着书上的抄袭制作,但已经理解了里面的意思和应用,并且进行了稍微改善和异常捕捉.这里记录下,以防以后用到这方面的知识点. 窗体设计: code: 复制代码 代码如下: using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.

asp.net(c#)开发中的文件上传组件uploadify的使用方法(带进度条)

在Web开发中,有很多可以上传的组件模块,利用HTML的File控件的上传也是一种办法,不过这种方式,需要处理的细节比较多,而且只能支持单文件的操作.在目前Web开发中用的比较多的,可能uploadify(参考http://www.uploadify.com/)也算一个吧,不过这个版本一直在变化,他们的脚本调用也有很大的不同,甚至调用及参数都一直在变化,很早的时候,那个Flash的按钮文字还没法变化,本篇随笔主要根据项目实际,介绍一下3.1版本的uploadify的控件使用,这版本目前还是最新的

C#实现带百分比的进度条功能示例

本文实例讲述了C#实现带百分比的进度条功能.分享给大家供大家参考,具体如下: 功能需求: 如果程序中会执行一个耗时的计算过程,我想在用户点击按钮后,弹出一个进度条窗口,显示正在执行的进度(最好能带有百分比),执行完成后,进度条窗口关闭,回到主程序窗口. 在关闭子窗口之前父窗体不能点击操作. 实现方法: 先设计Form2进度条窗体,在Form2中央上放ProgressBar控件progressBar1和Label控件label1,代码: public partial class Form2 : F

C#自定义音乐播放器进度条

有些时候我们做的程序需要进度条,而vs提供的控件不是我们想要的.先看效果图: 进度条闪烁动画,当然背景可设为Transparent 之前想手绘进度条线条的,结果控件运行时会闪烁,所以直接用了panel控件 源码: [DefaultEvent("ProgressClick")] [ToolboxBitmap(typeof(TrackBar))] public partial class ProcessBar : UserControl { public ProcessBar() { //

android实现音乐播放器进度条效果

本文实例为大家分享了android实现音乐播放器进度条效果的具体代码,供大家参考,具体内容如下 效果图 依赖3个对象 MediaPlayer:实现音乐播放,暂停,缓冲. SeekBar:滑动的进度条. java.util.Timer:定时器,时时更新进度条. main.xml样式文件 <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android

vue2.0+SVG实现音乐播放圆形进度条组件

vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果 需求分析: 类似于大多数音乐播放器中等mini播放器控制按钮,显示播放进度,实时更新进度. progress-circle.vue源码: <template> <div class="progress-circle"> <svg :width="radius" :height="radius" viewBox="0 0 10

运用js教你轻松制作html音乐播放器

用HTML做了个音乐播放器,可以循环播放,选择歌曲,以及自动播放下一首,运用了js和json知识,下面是效果图和源码,有兴趣的可以试试哦 效果图: 源码:html <span style="color:#999999;"><!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>音乐播放器</title> <sc

Android版音乐播放器

音乐播放器是一个非常常见的应用,这篇博客就是介绍如何制作一个简单的音乐播放器,这款音乐播放器具有以下的功能:播放歌曲.暂停播放歌曲..显示歌曲的总时长.显示歌曲的当前播放时长.调节滑块可以将歌曲调节到任何时间播放.退出音乐播放器. 实现效果如下 实现方式: 第一步:使用Android Studio创建一个Android工程,并且修改activity_main.xml文件 <?xml version="1.0" encoding="utf-8"?> <

Android开发简易音乐播放器

这里介绍一个简易的音乐播放器,供大家参考,具体内容如下 效果图如下: 但是,由于这是一个简易版的音乐播放器,所播放的音乐只有一首,且被写死,但,操作却十分简单,方便理解! 这是代码的主要设计: 音乐主要存放在这一个文件中: 下面就来介绍各部分代码: activity-main.xml: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://

ios开发:一个音乐播放器的设计与实现案例

这个Demo,关于歌曲播放的主要功能都实现了的.下一曲.上一曲,暂停,根据歌曲的播放进度动态滚动歌词,将当前正在播放的歌词放大显示,拖动进度条,歌曲跟着变化,并且使用Time Profiler进行了优化,还使用XCTest对几个主要的类进行了单元测试. 已经经过真机调试,在真机上可以后台播放音乐,并且锁屏时,显示一些主要的歌曲信息. 根据歌曲的播放来显示对应歌词的.用UITableView来显示歌词,可以手动滚动界面查看后面或者前面的歌词. 并且,当拖动进度条,歌词也会随之变化,下一曲.上一曲依

基于C#的音乐播放器主Form实现代码

本文所述为一个由C#编写的音乐播放器的主Form代码,里面有一些小技巧还是不错的,现共享给大家参考一下.里面有播放器背景设置.线程定义.调用读取文件目录方法.播放时间计数器.设置LV背景.获取播放歌曲.播放按钮,切换播放or暂停.切换歌曲到下一首,调用切歌方法.显示播放列表.歌词局中.播放窗体最小化隐藏到托盘设置.进度条滚动模块.从歌曲列表中删除文件等等功能.且各个功能模板均备有较为详细的注释,便于大家阅读理解. 程序主要代码如下: using System; using System.Coll

实现音乐播放器的代码(html5+css3+jquery)

看下面的效果图很不错吧,是怎么实现的呢?下面小编给大家分享下我的一番宝物,Lisa唱的 在angel beats的插曲.用到html5.css.jquery实现此音乐播放器. 一番宝物,Lisa唱的   在angel beats的插曲 最后在简述这个东西怎么写之前,本人男,24岁,籍贯上海,诚招女友一枚,要求:性格温顺...(省略500字) <div class="Music"> <div class="MusicPlaySound"> &l

Angular2 Service实现简单音乐播放器服务

引言 如果说组件系统(Component)是ng2应用的躯体,那把服务(Service)认为是流通于组件之间并为其带来生机的血液再合适不过了.组件间通信的其中一种优等选择就是使用服务,在ng1里就有了广泛使用,而ng2保持了服务的全部特性,包括其全局单例与依赖注入.今天就来实践一下ng2的服务(Service)这一利器,来实现一个简单的音乐播放器,重点在于使用服务来进行音频的播放控制与全局范围的调用. 一.基本项目准备: 考虑到音频播放是个比较通用的服务,决定将其创建为一个单独的模块AudioM