翻译来源:https://www.codeproject.com/Articles/21006/Not-just-a-image-list-control-Neat-D-iTunes-style
作者:Ashok Jaiswal
下载AlbumViewerReleaseDemo.zip - 2,803.1 KB
下载AlbumViewerSrc.zip - 408.7 KB
介绍
灵感来自iTunes专辑列表控制。此图像列表控件允许您以新的3D方式显示图像。没有更无聊的图像一个接一个地显示,但附带一个很酷的动画。未选择项目的缩放级别可以是控件,也可以是阴影的高度,每个未选择项目的透明度,其位置和动画速度。
这对于在GUI丰富的应用程序中显示图像非常有用。控件是从CListCtrl类派生的,所以它可以被扩展,以支持传统的列表控件视图或者冷却动画3D视图。
背景
正如我提到的灵感是iTunes专辑控制。在这些变得越来越酷的周末之中,欣赏音乐,我想知道如何使用iTunes来平滑整洁的动画来显示图像,并采取了很好的控制,从而迎接挑战,发现它只需要大约10个代数方程,并且很好地利用了GDI +实现它。
此控件加载完整大小的图像,在演示中它使用的图像大到800x600,并通过高效使用GDI +,您可以动画,缩放和控制alpha,没有任何闪烁和非常顺利的方式。
这篇文章对于寻求有关使用GDI +绘画的更多信息的开发人员来说最有用。即使GDI +几乎需要所有的东西,但无法像iTunes一样绘制图像。它允许剪切图像,但不允许使图像的两侧的高度不同。我没有真正尝试使用矩阵变换,但我认为图像的动画和定位可以更好地使用它们。
使用代码
要在项目中使用控件,请添加AlubumCtrl.h和AlbumCtrl.cpp,在表单/对话框中添加列表控件,为类型为CAlbumCtrl的此列表控件创建一个成员变量,并在OnInitDialog或其他任何地方添加以下代码您要添加新项目到列表控件:m_ctlAlbum.AddItem(L"images\\Water lilies.jpg"); m_ctlAlbum.AddItem(L"images\\Sunset.jpg"); m_ctlAlbum.AddItem(L"images\\Winter.jpg"); m_ctlAlbum.AddItem(L"images\\Blue hills.jpg"); m_ctlAlbum.SetCurrentItem(2); m_sldAlpha.SetRange(0,100); m_sldAnim.SetRange(0,100); m_sldShadow.SetRange(0,100); m_sldZoom.SetRange(0,100); m_sldElevation.SetRange(-300,100); m_sldElevation.SetPos(m_ctlAlbum.GetItemElevation()); m_sldAlpha.SetPos(m_ctlAlbum.GetItemAlpha()); m_sldAnim.SetPos(m_ctlAlbum.GetAnimSpeed()); m_sldShadow.SetPos(m_ctlAlbum.GetItemShadow()); m_sldZoom.SetPos(m_ctlAlbum.GetItemZoom());
计算项目位置
以下功能是放置控件和令人惊讶的最小代码部分的大多数导入功能。那么在它来到这之前,它确实需要大约4次迭代代码和代码的微调
![](https://i-blog.csdnimg.cn/blog_migrate/0a9790198ad274bad3ea0c1649d06d71.png)
RectF CAlbumCtrl::CalcItemRect(const int n, const RectF rcBase, bool bLeft) { float r = m_fRatio; float p = pow(r,n); int h = rcBase.Height; int w = rcBase.Width; int y = 0; int x = 0; for(int j=1;j<=n;j++) y += (m_nItemY*h*pow(r,j))/100; if( bLeft ) { int x = 0; for(int j=2;j<=n+1;j++) x += w*pow(r,j); return RectF(rcBase.X-x, rcBase.Y-y, w*p, h*p); } x = rcBase.X + w; for(int j=1;j<n;j++) x += w*pow(r,j); for(int j=1;j<n+1;j++) x -= (1-r)*w*pow(r,j); return RectF(x, rcBase.Y-y, w*p, h*p); }
绘图项目
这是控制中第二重要的功能。这将拾取项目图像,计算阴影宽度,将alpha应用于图像,并将alpha减少到阴影,如果需要,将文本放置在所选图像上。
![](https://i-blog.csdnimg.cn/blog_migrate/0a9790198ad274bad3ea0c1649d06d71.png)
void CAlbumCtrl::DrawItem(const int nItem, RectF rc, Graphics &grf, float fAlpha, bool bDrawText) { // centre image // draw the items only if they are inside the visible area // to keep repainting fast if( nItem < 0 || nItem > m_vecItems.size()-1 ) return; Bitmap bitmap(m_vecItems[nItem].wszItem.c_str()); CRect r; GetClientRect(&r); RectF rcWnd(r.left, r.top, r.Width(), r.Height()); if(!rcWnd.IntersectsWith(rc)) return; Bitmap bmp(rc.Width, rc.Height, PixelFormat32bppARGB ); Graphics graphic(&bmp); ImageAttributes imgAttrb; RectF rcDraw(0, 0, rc.Width, rc.Height); graphic.DrawImage(&bitmap, rcDraw, 0, 0, bitmap.GetWidth(), bitmap.GetHeight(), UnitPixel, &imgAttrb); graphic.DrawRectangle(&Pen(Color(80,80,80), 2),rcDraw); ColorMatrix bmpAlpha = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, fAlpha, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; imgAttrb.SetColorMatrix(&bmpAlpha); grf.DrawImage(&bmp, rc, 0, 0, rc.Width, rc.Height, UnitPixel, &imgAttrb); int iHeight = bmp.GetHeight(); int iWidth = bmp.GetWidth(); // reflection percent if( !m_nItemShadow ) m_nItemShadow = 1; float nRef = 100.0/m_nItemShadow; RectF rcRef = RectF(rc.X, rc.Y+rc.Height, rc.Width, rc.Height/nRef); Bitmap bmpRef(rcDraw.Width, rcDraw.Height/nRef, PixelFormat32bppARGB ); Color color, colorTemp; for(UINT iRow = iHeight; iRow > iHeight-iHeight/nRef; iRow--) { for(UINT iColumn = 0; iColumn < iWidth; iColumn++) { // decrease the alpha by 1 value for each line in bottom margin double dAlpha = (iHeight/nRef-(iHeight-iRow))*255/(iHeight/nRef); bmp.GetPixel(iColumn, iRow, &color); colorTemp.SetValue(color.MakeARGB( //(BYTE)(5*(iHeight/nRef-(iHeight-iRow))), (BYTE)dAlpha, color.GetRed(), color.GetGreen(), color.GetBlue())); bmpRef.SetPixel(iColumn, iHeight-iRow, colorTemp); } } grf.DrawImage(&bmpRef, rcRef, 0, 0, rcRef.Width, rcRef.Height, UnitPixel, &imgAttrb); if( bDrawText ) { //wstring wszText = m_vecItems[nItem].wszItem; //Font myFont(L"Tahoma",12,FontStyleRegular,UnitPixel); // //RectF rcText; //grf.MeasureString(wszText.c_str(), wszText.length(), &myFont, // rcRef, &rcText); //grf.DrawString(wszText.c_str(),wszText.length(), &myFont, // PointF(rcRef.X+rcRef.Width-rcText.Width-14, // rcRef.Y+(rcRef.Height-rcText.Height)/2-rcText.Height), // &SolidBrush(Color(255,255,255))); //wchar_t temp[10]; //wszText = _itow(nItem+1, temp, 10); //grf.DrawString(wszText.c_str(),wszText.length(), &myFont, // PointF(rcRef.X+2, rcRef.Y+(rcRef.Height-rcText.Height)/2-rcText.Height), // &SolidBrush(Color(255,255,255))); //grf.DrawString(wszText.c_str(),wszText.length(), &myFont, // PointF(rcRef.X, rcRef.Y), &SolidBrush(Color(255,255,255))); } }
幕后涉及数学
控制最感兴趣的部分是涉及定位和动画项目的数学。因为在中心选择项目的左侧和右侧放置/动画物品时存在不同的代数。
将项目置于控件中
计算任何项目的宽度
W n = W ( r / 100 ) n计算任何物品的高度
H n = H ( r / 100 )n
计算任何项目的Y位置
Y n = Y - | Ñ Σ 我 = 1 | H / e ( r / 100 ) |
计算所选项目左侧项目的X位置
X n = X - | 的n + 1 Σ 我 = 2 | W ( r / 100 ) |
计算所选项目右侧项目的X位置
X n = X + W + | n-1个 Σ 我 = 1 | W ( r / 100 )i - | 的n + 1 Σ 我 = 1 | W ( 1-r / 100 )( r / 100 ) |
哪里
H =所选项目的高度
W =所选项目的宽度
X =所选项目的X位置
Y =所选项目的Y位置
r =当前缩放级别,以%
e = Y的当前高程
动画控件中的项目
动画向左的项目
动画左侧的项目
ΔX= c ( X n + 1 -X n ) / lX n = X n +ΔXΔY
= c ( Y n + 1 -Y n ) / l
Y n = Y n +
ΔYΔW= c ( W n + 1 -W n ) / 1
W n = W n +ΔWΔH
= c ( H n + 1 -H n ) / 1
H n = H n +ΔH
动画右侧的物品
ΔX= c ( X n + 1 -X n) / 1
X n-1 = X n +ΔXΔY
= c ( Y n + 1 -Y n) / 1
Y n-1 = Y n -ΔY
ΔW= c ( W n + 1 -W n) / l
W n = W n -ΔWΔH
= c ( H n + 1 -H n) / 1
H n = H n -ΔH
动画物品向右移动
动画左侧的项目
ΔX= c ( X n + 1 -X n) / l
X n = X n +ΔXΔY
= c ( Y n + 1 -Y n) / l
Y n = Y n +
ΔYΔW= c ( W n + 1 -W n) / 1
W n = W n +ΔWΔH
= c ( H n + 1 -H n) / 1
H n = H n +ΔH
动画右侧的物品
ΔX= c ( X n + 1 -X n) / I
X n = X n +ΔXΔY
= c ( Y n + 1 -Y n) / 1
Y n = Y n -
ΔYΔW= c ( W n-1 -W n) / 1
W n = W n -ΔWΔH
= c ( H n-1 -H n) / 1
H n = H n -ΔH
哪里
c =当前动画步骤
l =总动画步数计数
未来特点
计划添加视图切换按钮,以便用户可以切换到图标视图/列表视图/报告视图/相册视图