旧版 http://huidong.xyz/?mode=2&id=439
/////////////////////////////////////
//
// 图像切换动画效果(像素点位移)
//
// Visual Studio 2022 | EasyX 20220610
//
// by huidong <mailhuid@163.com>
// 2022.7.28
//
#include "HiEasyX.h"
using std::vector;
int nWndWidth = 100;
int nWndHeight = 100;
#define SQR(x) ((x)*(x))
#define DISTANCE(x1, y1, x2, y2) sqrt(SQR(x2-x1)+SQR(y2-y1))
struct FloatPoint
{
double x = 0, y = 0;
};
struct MovePoint
{
FloatPoint pt = {}; // 原位置
double vx = 0, vy = 0; // 毫秒速度
bool isRedundant = false; // 该点是否多余(Src)
bool isUsed = false; // 是否已被作为目标点(Dst)
HWND hwnd = NULL;
};
IMAGE imgSrc, imgDst;
vector<MovePoint> vecSrc, vecDst;
int nAnimateDuration = 2; // 动画时长(秒)
int nFPS = 24; // 帧率
int nDelayMS = (int)((1.0 / nFPS) * 1000); // 每帧的延时(毫秒)
void Load(LPCTSTR src, LPCTSTR dst)
{
loadimage(&imgSrc, src);
loadimage(&imgDst, dst);
}
void Record()
{
IMAGE* pImg[] = { &imgSrc,&imgDst };
vector<MovePoint>* pVec[] = { &vecSrc,&vecDst };
for (int i = 0; i < 2; i++)
{
SetWorkingImage(pImg[i]);
for (int x = 0; x < getwidth(); x++)
{
for (int y = 0; y < getheight(); y++)
{
if (getpixel(x, y) == BLACK)
{
MovePoint pt = { (double)x,(double)y };
pVec[i]->push_back(pt);
}
}
}
}
}
void Calc()
{
// 平衡源点和目标点数量
size_t sizeSrc = vecSrc.size();
size_t sizeDst = vecDst.size();
if (sizeSrc < sizeDst)
{
for (size_t i = 0; i < sizeDst - sizeSrc; i++)
{
vecSrc.push_back(vecSrc[rand() % sizeSrc]);
}
}
else if (sizeSrc > sizeDst)
{
size_t sizeDiff = sizeSrc - sizeDst;
for (size_t i = 0; i < sizeDiff; i++)
{
vecSrc[sizeSrc / sizeDiff * i].isRedundant = true;
}
}
// 分配目标点,计算速度
size_t sizeSrcNew = vecSrc.size();
for (size_t i = 0; i < sizeSrcNew; i++)
{
FloatPoint ptBest = { -1,-1 };
double dDistance = -1;
int nBestIndex = -1;
for (size_t j = 0; j < sizeDst; j++)
{
// 多余的点选哪个做目标都可以,但是其他点只能选择没用过的
if (vecSrc[i].isRedundant || !vecDst[j].isUsed)
{
double dThis = DISTANCE(
vecSrc[i].pt.x, vecSrc[i].pt.y,
vecDst[j].pt.x, vecDst[j].pt.y);
if (dThis < dDistance || dDistance == -1)
{
ptBest = vecDst[j].pt;
dDistance = dThis;
nBestIndex = j;
}
}
}
if (nBestIndex == -1)
{
printf("\nError.\n");
exit(-1);
}
if (!vecSrc[i].isRedundant)
{
vecDst[nBestIndex].isUsed = true;
}
vecSrc[i].vx = (ptBest.x - vecSrc[i].pt.x) / (double)nAnimateDuration / 1000;
vecSrc[i].vy = (ptBest.y - vecSrc[i].pt.y) / (double)nAnimateDuration / 1000;
}
}
void CreateWindowPixel()
{
for (auto& pixel : vecSrc)
{
HiEasyX::PreSetWindowPos((int)pixel.pt.x * nWndWidth, (int)pixel.pt.y * nWndHeight);
pixel.hwnd = HiEasyX::initgraph_win32(nWndWidth, nWndHeight);
HiEasyX::SetWindowStyle(HiEasyX::GetWindowStyle() & ~WS_SIZEBOX);
BEGIN_TASK();
setbkcolor(WHITE);
cleardevice();
END_TASK();
FLUSH_DRAW();
Sleep (100);
}
}
void Animate(bool reverse = false)
{
int nDelayFrequency = nAnimateDuration * 1000 / nDelayMS;
vector<MovePoint> vecPt = vecSrc;
if (reverse)
{
for (size_t j = 0; j < vecSrc.size(); j++)
{
vecPt[j].pt.x += vecPt[j].vx * nAnimateDuration * 1000;
vecPt[j].pt.y += vecPt[j].vy * nAnimateDuration * 1000;
vecPt[j].vx = -vecPt[j].vx;
vecPt[j].vy = -vecPt[j].vy;
HiEasyX::MoveWindow(
(int)vecPt[j].pt.x * nWndWidth,
(int)vecPt[j].pt.y * nWndHeight,
vecPt[j].hwnd
);
}
}
for (int i = 0; i < nDelayFrequency; i++)
{
for (size_t j = 0; j < vecSrc.size(); j++)
{
vecPt[j].pt.x += vecPt[j].vx * nDelayMS;
vecPt[j].pt.y += vecPt[j].vy * nDelayMS;
putpixel((int)vecPt[j].pt.x, (int)vecPt[j].pt.y, BLACK);
HiEasyX::MoveWindow(
(int)vecPt[j].pt.x * nWndWidth,
(int)vecPt[j].pt.y * nWndHeight,
vecPt[j].hwnd
);
}
FlushBatchDraw();
HpSleep(nDelayMS);
}
}
int main()
{
/*Load(L"点赞.bmp", L"投币.bmp");
Record();
Calc();
CreateWindowPixel();
Animate();
Sleep (3000);
closegraph();*/
nWndWidth = 70;
nWndHeight = 70;
Load(L"投币.bmp", L"收藏.bmp");
Record();
Calc();
CreateWindowPixel();
Animate();
Sleep (3000);
closegraph();
return 0;
}