风火轮越来越有广告范,之前实现的素材导入功能已能解决50%的用户需求,即可以拖入现成的动画、视频及图片素材,效果见QQ空间之前的某篇日志。
从进化发展角度来看,现在的很多产品,纯硬件的竞争已是薄利见血,软件功能提升才是王道。
所以,软件实现得加强。电子黑板如此,风火轮也如此。
准备实现动画效果。
闭门造车是白手起家的最脑残做法,所以先放眼成熟产品,看哪些功能与UI可以借鉴。
做素材,一般会选取FLASH、Photoshop,而动画效果,PPT是大家耳熟能详的。
仔细研究一下,决定采用PPT的界面,再参考PhotoShop的gif功能。
首先做PPT的动画界面,这个用Ribbon风格控件,就是花点时间的事,没太大的技术含量。
最花时间的是找图标,不过现在别的不好找,代码、图标却是大把。
花了半天时间,虽谈不上是神似,也能算得上是形似的了。具体功能将逐个实现。
再看下PPT中的动画属性,其界面最终所围绕的也就是这两个
分解一下,设计几个数据结构:
typedefstruct tagEnhanceInfo { // 增强选项
UnicodeStringSoundFileName; // 声音文件
TEffect_Enhance_AfterPlayAfterPlayMode; // 动画播放后处理方式
TColorAfterAnimationColor; // 变换为某种颜色,FAfterPlayMode为teapToColor有效
UnicodeStringAnimationText; // 动画文本
intDelayPercent; // 字母之间延迟百分比
}ENHANCE_INFO;
typedefstruct tagTimerInfo { // 计时选项
TEffect_Time_StartStartMode; // 开始选项
intDelay; // 延迟秒数
TEffect_Time_PeriodPeriodMode; // 期间选项
TEffect_Time_RepeatRepeatMode; // 重复选项
boolQuickBackAfterPlay; // 播完后快退
}TIMER_INFO;
enum TEffect_Enhance_AfterPlay { // 增强选项-动画后播放选项
teapToColor,// 变换为某种颜色
teapNoDarking,// 不变暗
teapHide,// 隐藏
teapHideAfterClick// 下次单击后隐藏
};
enum TEffect_Time_Start { // 计时选项-开始选项
ttsClick,// 单击时
ttsSameTimeAsPrevOne,// 与上一动画同时
ttsAfterPrevOne// 上一动画完成之后
};
enum TEffect_Time_Period { // 计时选项-期间
ttpVerySlow,// 非常慢
ttpSlow,// 慢速
ttpNormal,// 中速
ttpFast,// 快速
ttpVeryFast// 非常快
};
enum TEffect_Time_Repeat { // 计时选项-重复
ttrNone,// 无
ttr2,// 2
ttr3,// 3
ttr4,// 4
ttr5,// 5
ttr10,// 10
ttrTillNextClick,// 直到下一次单击
ttrTillEnd// 直到结束
};
enum CbwEffectType { // 效果类型枚举量
cetBase= 0, // TCbwAnimationEffect
cetAppear= 1, // TCbwAnimationEffect_Appear
cetFadeOut= 2, // TCbwAnimationEffect_FadeOut
cetFlyIn= 3, // TCbwAnimationEffect_FlyIn
cetEnd
};
enum CbwEffectDirection { // 动画方向
cedFromBottom= 0, // 自底部
cedFromLeftBottom= 1, // 自左下部
cedFromLeft= 2, // 自左侧
cedFromLeftTop= 3, // 自左上部
cedFromTop= 4, // 自顶部
cedFromRightTop= 5, // 自右上部
cedFromRight= 6, // 自右侧
cedFromRightBottom= 7 // 自右下部
};
为了更好地模块化,把对象相关的动画属性提取出来
typedefstruct tagObjectMat {
TPointLeftTopPosition;
TCbwObject* Object;
cv::MatMat;
cv::MatMask;
void__fastcall BuildMask(int height, int width);
}OBJECTMAT;
这样,可以设计动画基类为:
/**
*@class TCbwAnimationEffect
*@brief 动画基类
*
* 处理动画基本内容
*@author 陈斌文
*@version 1.0
*@date 2015-03-03
*@QQ: 282397369
*/
class TCbwAnimationEffect {
bool__fastcall IsAtEnd();
CbwObjectsFAnimationObjects; // 相关对象
cv::Mat__fastcall Object2Mat(TCbwObject * object, double ratio = 1);
int__fastcall GetRepeateTime();
protected:
vector<OBJECTMAT>FDestMats;
intFCurrentIndex; // 当前帧索引
intFPeriodLength; // 周期长度,指动画一个周期内的帧数
intFPosition; // 当前位置,指动画累加索引位置
intFWidth, FHeight; // 长宽尺寸,指显示限制
public:
CbwEffectTypeEffectType;
ENHANCE_INFOEnhanceOption; // 增强选项
TIMER_INFOTimerOption; // 计时选项
__fastcallTCbwAnimationEffect();
staticTCbwAnimationEffect * Build();
void__fastcall Assign(TCbwAnimationEffect * other);
void__fastcall AddToXmlNode(CbwXmlNode * node);
void__fastcall GetFromXmlNode(CbwXmlNode * node);
void__fastcall First();
void__fastcall Next();
void__fastcall SetRelativeObject(CbwObjects relativeObjects,
TPaintBox* pb, TScrollBox * scrollBox);
void__fastcall SetBounds(int width, int height);
void__fastcall Draw(HWND wnd, BYTE * backData, int width, int height);
cv::MatCurrentMat;
__propertybool Eof = {read = IsAtEnd};
protected:
virtualTRect __fastcall BuildDisplayRect(OBJECTMAT * m);
virtualvoid __fastcall BuildDisplayMat(cv::Mat& destMat, cv::Mat& srcMat);
virtualvoid __fastcall BuildMaskMat(cv::Mat& destMat, cv::Mat& srcMat);
};
核心实现代码为:
__fastcallTCbwAnimationEffect::TCbwAnimationEffect() {
TimerOption.StartMode= ttsClick;
TimerOption.Delay= 0;
TimerOption.PeriodMode= ttpNormal;
TimerOption.RepeatMode= ttrNone;
TimerOption.QuickBackAfterPlay= false;
EffectType= cetBase;
}
TCbwAnimationEffect *TCbwAnimationEffect::Build() {
returnnew TCbwAnimationEffect;
}
TRect __fastcallTCbwAnimationEffect::BuildDisplayRect(OBJECTMAT * m) {
TRectresult(m->LeftTopPosition.x, m->LeftTopPosition.y,
m->LeftTopPosition.x+ m->Mat.cols, m->LeftTopPosition.y + m->Mat.rows);
returnresult;
}
void __fastcall TCbwAnimationEffect::BuildDisplayMat(cv::Mat&destMat,
cv::Mat&srcMat) {
destMat= srcMat.clone();
}
void __fastcallTCbwAnimationEffect::BuildMaskMat(cv::Mat& destMat,
cv::Mat&srcMat) {
destMat= srcMat.clone();
}
void __fastcallTCbwAnimationEffect::Draw(HWND wnd, BYTE * backData, int width,
intheight) {
cv::MatbackGndMat(height, width, CV_8UC3); // 背景
GlobalOpenCVObject->CopyRGBDatasToMat(backGndMat,backData, width,
height,true);
CBW_ITERATOR(vector<OBJECTMAT>,FDestMats) {
OBJECTMAT* animationObject = &(*it);
//以下取得待显示的区域、内容
TRectanimationDisplayRect = BuildDisplayRect(animationObject); // 目标区域
cv::MatanimationDisplayMat =
cv::Mat::zeros(animationDisplayRect.Height(),
animationDisplayRect.Width(),animationObject->Mat.type());
BuildDisplayMat(animationDisplayMat,animationObject->Mat); // 待显示内容
cv::MatmaskMat = cv::Mat::zeros(animationObject->Mask.rows,
animationObject->Mask.cols,animationObject->Mask.type());
BuildMaskMat(maskMat,animationObject->Mask); // Mask内容
TRectsuitableDisplayRect; // 真正的目标显示区域
suitableDisplayRect.left= max(0, int(animationDisplayRect.left));
suitableDisplayRect.top= max(0, int(animationDisplayRect.top));
suitableDisplayRect.right= min(int(animationDisplayRect.right), width);
suitableDisplayRect.bottom= min(int(animationDisplayRect.bottom),
height);
TRectsuitableRectInMat(0, 0, suitableDisplayRect.Width(),
suitableDisplayRect.Height());
intdeltaL =
max(0,int(suitableDisplayRect.left - animationDisplayRect.left));
if(suitableDisplayRect.left != animationDisplayRect.left) {
suitableRectInMat.left+= deltaL;
suitableRectInMat.right+= deltaL;
}
intdeltaT =
max(0,int(suitableDisplayRect.top - animationDisplayRect.top));
if(suitableDisplayRect.top != animationDisplayRect.top) {
suitableRectInMat.top+= deltaT;
suitableRectInMat.bottom+= deltaT;
}
cv::MatdestPartMat =
animationDisplayMat(cv::Rect(suitableRectInMat.left,
suitableRectInMat.top,suitableRectInMat.Width(),
suitableRectInMat.Height()));
cv::MatbackgndPartMat = // 目标背景区域相应矩阵
backGndMat(cv::Rect(suitableDisplayRect.left,
suitableDisplayRect.top,suitableDisplayRect.Width(),
suitableDisplayRect.Height()));
cv::MatmaskPartMat =
maskMat(cv::Rect(deltaL,deltaT, suitableDisplayRect.Width(),
suitableDisplayRect.Height()));
destPartMat.copyTo(backgndPartMat,maskPartMat);
}
GlobalOpenCVObject->PreviewMat(wnd,backGndMat, width, height);
}
void __fastcallTCbwAnimationEffect::Assign(TCbwAnimationEffect * other) {
TimerOption.StartMode= other->TimerOption.StartMode;
TimerOption.Delay= other->TimerOption.Delay;
TimerOption.PeriodMode= other->TimerOption.PeriodMode;
TimerOption.RepeatMode= other->TimerOption.RepeatMode;
TimerOption.QuickBackAfterPlay= other->TimerOption.QuickBackAfterPlay;
}
bool __fastcallTCbwAnimationEffect::IsAtEnd() {
boolresult = (FPosition >= FPeriodLength * GetRepeateTime());
returnresult;
}
void __fastcall TCbwAnimationEffect::First(){
FPosition= 0;
FCurrentIndex= 0;
}
void __fastcall TCbwAnimationEffect::Next(){
++FPosition;
FCurrentIndex= FPosition % FPeriodLength;
}
cv::Mat __fastcallTCbwAnimationEffect::Object2Mat(TCbwObject * object,
doubleratio) {
TCanvas* oldCanvas = object->Canvas;
Graphics::TBitmap* bitmap = new Graphics::TBitmap;
bitmap->PixelFormat= pf24bit;
bitmap->Width= object->Width;
bitmap->Height= object->Height;
TCanvas* canvas = bitmap->Canvas;
boolallowDraw = object->AllowDraw;
boololdDrawBorderFlag = object->DrawBorderFlag;
object->AllowDraw= false; {
TRestoreleft(object, "Left", 0);
TRestoretop(object, "Top", 0);
object->Canvas= canvas;
object->AllowDraw= allowDraw;
object->DrawBorderFlag= false;
object->Draw();
object->AllowDraw= false;
}object->Canvas = oldCanvas;
object->AllowDraw= allowDraw;
object->DrawBorderFlag= oldDrawBorderFlag;
BYTE* backData = THelper::Graphics::GetBitmapData(bitmap);
cv::Matresult;
GlobalOpenCVObject->CopyRGBDatasToMat(result,backData, bitmap->Width,
bitmap->Height,true);
deletebitmap;
deletebackData;
returnresult;
}
void __fastcallTCbwAnimationEffect::OBJECTMAT::BuildMask(int height, int width)
{
Mask= cv::Mat(height, width, CV_8UC1);
BYTE* pSrc = Mat.data;
BYTE* pDst = Mask.data;
for(int i = height * width - 1; i >= 0; --i) {
BYTEB = *pSrc++;
BYTEG = *pSrc++;
BYTER = *pSrc++;
*pDst++= (B == 0xFF && G == 0xFF && R == 0xFF) ? 0 : 255;
}
}
void __fastcallTCbwAnimationEffect::SetRelativeObject
(CbwObjectsrelativeObjects, TPaintBox * pb, TScrollBox * scrollBox) {
CBW_ITERATOR(CbwObjects,relativeObjects) {
cv::Matmat = Object2Mat(*it);
TPointlt = TPoint((*it)->Left, (*it)->Top);
lt= pb->ClientToScreen(lt);
lt= scrollBox->ScreenToClient(lt);
OBJECTMATm;
m.Mat= mat;
m.LeftTopPosition= lt;
m.Object= *it;
m.BuildMask((*it)->Height,(*it)->Width);
FDestMats.push_back(m);
}
FPeriodLength= 50;
FCurrentIndex= 0;
FPosition= 0;
}
void __fastcallTCbwAnimationEffect::SetBounds(int width, int height) {
FWidth= width;
FHeight= height;
}
为方便调用处逻辑简单,可采用:
typedef TCbwAnimationEffect *(*BuildEffectObject)(); // 智能构造函数
typedef std::map<int,BuildEffectObject>EffectObjectMap; // 构造各图元对象映射
#define CREATEEFFECTOBJECTif(!CbwEffectObjectMap) CbwEffectObjectMap = new EffectObjectMap; (*CbwEffectObjectMap)
…
CREATEEFFECTOBJECT[cetBase]= TCbwAnimationEffect::Build;
CREATEEFFECTOBJECT[cetAppear]= TCbwAnimationEffect_Appear::Build;
CREATEEFFECTOBJECT[cetFadeOut]= TCbwAnimationEffect_FadeOut::Build;
CREATEEFFECTOBJECT[cetFlyIn]= TCbwAnimationEffect_FlyIn::Build;
这样,调用时,简单处理即可:
if(!(*CbwEffectObjectMap)[effectType]) {
THelper::Util::MessageBox(L"本动画效果尚未实现,请稍候!", false);
return;
}
TCbwAnimationEffect* effectItem = (*CbwEffectObjectMap)[effectType](); // 根据类型创建动画对象
调用,先处理用户选中某个对象后的动画类型逻辑
void __fastcall TForm::AddEffect(TdxRibbonGalleryGroupItem*AItem) {
if(cSelectedObjects->MetaNumber== 0) return;
inteffectType = AItem->ImageIndex + 1;
if(!(*CbwEffectObjectMap)[effectType]) {
THelper::Util::MessageBox(L"本动画效果尚未实现,请稍候!", false);
return;
}
TCbwAnimationEffect* effectItem = (*CbwEffectObjectMap)[effectType](); // 根据类型创建动画对象
effectItem->SetRelativeObject(cSelectedObjects->SubObjects,PaintBox, ScrollBox);
effectItem->SetBounds(ScrollBox->Width,ScrollBox->Height);
Graphics::TBitmap* bitmap = new Graphics::TBitmap;
bitmap->PixelFormat= pf24bit;
bitmap->Width= ScrollBox->Width;
bitmap->Height= ScrollBox->Height;
RECTdisplayRect = Rect(ScrollBox->HorzScrollBar->Position,
ScrollBox->VertScrollBar->Position,ScrollBox->HorzScrollBar->Position +
ScrollBox->Width,ScrollBox->VertScrollBar->Position +
ScrollBox->Height);
Graphics::TBitmap* FPreviewBitmap = new Graphics::TBitmap;
FPreviewBitmap->PixelFormat= pf24bit;
FPreviewBitmap->Width= PaintBox->Width;
FPreviewBitmap->Height= PaintBox->Height;
TCanvas* canvas = FPreviewBitmap->Canvas;
canvas->Rectangle(0,0, 10000, 10000);
CBW_ITERATOR(CbwObjects,Objects)(*it)->Canvas = canvas;
CBW_ITERATOR(CbwObjects,Objects) {
TCbwObject* object = *it;
if(!CanObjectBeVisible(object) || !object->CanContinueWithRect
(displayRect,CBW_CONTINUE_DRAW) || object->Selected)
continue;
object->Draw();
}
PostPaint(canvas);
bitmap->Canvas->CopyRect(Rect(0,0, bitmap->Width, bitmap->Height), canvas,
displayRect);
CBW_ITERATOR(CbwObjects,Objects)(*it)->Canvas = PaintBox->Canvas;
TRestoreApplicationCurrentStatus(TGraphApp::CurrentStatus, cfsAnimation);
BYTE* backData = THelper::Graphics::GetBitmapData(bitmap);
effectItem->First();
while(!effectItem->Eof){
effectItem->Draw(ScrollBox->Handle,backData, bitmap->Width, bitmap->Height);
effectItem->Next();
Sleep(10);
}
deletebackData;
deleteFPreviewBitmap;
deletebitmap;
deleteeffectItem;
}
剩下的事情就是针对各动画类型进行细化。今天先实现一种:淡出动画效果
参考PPT效果,淡出是慢慢出现对象,这用OpenCV控制Mat的各点即可简单实现
在基类的基础上,再实现各种动画效果,应该算是比较轻松的了,对于淡出效果而言,无需变动位置,只控制显示像素,直接重载BuildDisplayMat,根据当前进度处理各像素亮度。
/**
*@class TCbwAnimationEffect_ FadeOut
*@brief 淡出动画类
*
* 淡出动画
*@author 陈斌文
*@version 1.0
*@date 2015-03-03
*@QQ: 282397369
*/
class TCbwAnimationEffect_FadeOut : publicTCbwAnimationEffect { // 淡出
typedefTCbwAnimationEffect inherited;
virtualvoid __fastcall BuildDisplayMat(cv::Mat& destMat, cv::Mat& srcMat);
public:
__fastcallTCbwAnimationEffect_FadeOut();
staticTCbwAnimationEffect * Build();
};
// ***************************** 淡出效果**************************************
__fastcallTCbwAnimationEffect_FadeOut::TCbwAnimationEffect_FadeOut()
:TCbwAnimationEffect() {
EffectType= cetFadeOut;
}
TCbwAnimationEffect *TCbwAnimationEffect_FadeOut::Build() {
returnnew TCbwAnimationEffect_FadeOut;
}
void __fastcallTCbwAnimationEffect_FadeOut::BuildDisplayMat(cv::Mat& destMat,
cv::Mat&srcMat) {
for(int y = 0; y < destMat.rows; y++) {
for(int x = 0; x < destMat.cols; x++) {
for(int c = 0; c < 3; c++) {
BYTEvalue = srcMat.at<cv::Vec3b>(y, x)[c];
value= 255 - FCurrentIndex / double(FPeriodLength) *
(255- value);
destMat.at<cv::Vec3b>(y,x)[c] =
cv::saturate_cast<uchar>(value);
}
}
}
}
// ***************************** 淡出效果**************************************
原文地址:http://blog.csdn.net/arwen/article/details/44039999