前言
临近七夕,看到抖音上的excel表格做的七夕抽奖,突发灵感,想着用代码也整一个出来。想着做个小程序效果最好,但自己没做过小程序,时间紧迫很悬;做个pc应用,那时最拿手的,很快,但要在电脑上打开才能看,局限性太大;最后决定做个安卓应用,用Qt就能做,装好后还能留存在手机中。
成品效果
抽奖原理
概率抽奖的本质其实就是生成随机数。
比如生成10以内的随机数,会生成0-9,每个数生成的概率是0.1,当0-9每个数都对应一个奖品,每个奖品的中奖概率是相同的,即0.1。
如果要每个奖品的概率不同,则我们可以选定区间对应一个奖品,比如 [0,2)对应一个奖品,那对应的概率就是0.3,[2,3)对应一个奖品,则概率是0.1,以此类推,如下图所示
抽奖代码实现
1.自定义表示区间的结构体
struct RangValue {
RangValue(qreal startVal, qreal endVal):
m_startValue(startVal), m_endValue(endVal)
{
}
bool contain(qreal val) {
if(val >= m_startValue && val < m_endValue)
return true;
else
return false;
}
qreal m_startValue;
qreal m_endValue;
};
2.给奖品设置概率分布,这里有12个奖品,可对照上面效果图,具体概率能够很清楚看出来,为了方便与界面交互,我这里放在一个map里
m_optionMap.insert(0, RangValue(0, 0.05));
m_optionMap.insert(1, RangValue(0.05, 0.08));
m_optionMap.insert(2, RangValue(0.08, 0.13));
m_optionMap.insert(3, RangValue(0.13, 0.135));
m_optionMap.insert(4, RangValue(0.135, 0.235));
m_optionMap.insert(5, RangValue(0.235, 0.335));
m_optionMap.insert(6, RangValue(0.335, 0.535));
m_optionMap.insert(7, RangValue(0.535, 0.835));
m_optionMap.insert(8, RangValue(0.835, 0.885));
m_optionMap.insert(9, RangValue(0.885, 0.89));
m_optionMap.insert(10, RangValue(0.89, 0.94));
m_optionMap.insert(11, RangValue(0.94, 1));
3.生成随机数,根据生成的随机数找出对应的产品
int a = qrand()%m_srandBase;
m_prizeIndex = getOption(a);
int getOption(int rand)
{
int index = 0;
qreal percentVal = (qreal)rand/(qreal)m_srandBase;
QMapIterator<int, RangValue> iter(m_optionMap);
while (iter.hasNext()) {
iter.next();
RangValue rangVal = iter.value();
if(rangVal.contain(percentVal)) {
index = iter.key();
break;
}
}
return index;
}
其中m_srandBase的值为1000,生成0-999的随机数,根据所在的区间就能找到对应的索引。
界面实现
界面使用Qt进行实现,自己对QML不熟,为了快点做出来,选择使用QWidget。
Qt环境为Qt12.3 + armeabi-v7a,安卓配置jdk1.8.0_261 + SDK 26.1.1,+ NDK 19.2.5345600,具体安卓环境的搭建这里不介绍了
界面效果主要有三部分组成,转盘 + 中间点击(即开始抽奖部分)+ 抽奖结果
1.转盘
使用paintEvent进行绘制
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(QColor(255, 211, 212));
//画背景
painter.drawRect(this->rect());
//画标题栏
painter.setBrush(QColor(255, 68, 72));
painter.drawRect(QRect(0, 0, this->width(), this->height()/10));
painter.setPen(Qt::white);
m_font.setPixelSize(50);
painter.setFont(m_font);
painter.drawText(QRect(0, 0, this->width(), this->height()/10), Qt::AlignCenter, u8"七夕送女朋友的礼物");
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::white);
painter.drawRect(m_outFirstRect);
painter.setBrush(QColor(255, 117, 88));
painter.drawRect(m_outSecondRect);
for(int i = 0; i < 4; ++i) {
for(int j = 0; j < 4; ++j) {
//排除中间四个
if((i == 1 && (j == 1 || j == 2)) || (i == 2 && (j == 1 || j == 2)))
continue;
QRect sigleRect = QRect(m_startRect.x() + i*m_singleWidth,
m_startRect.y() + j*m_singleWidth,
m_startRect.width(), m_startRect.height());
// qDebug() << __FUNCTION__ << sigleRect << startRect;
painter.setBrush(m_colorMap.value(i + j*4));
painter.drawRect(sigleRect);
painter.setPen(QPen(Qt::white, 6*m_rScaleFactor));
m_font.setPixelSize(40);
painter.setFont(m_font);
painter.drawText(sigleRect, Qt::AlignCenter, m_prizeMap.value(i + j*4));
painter.setPen(Qt::NoPen);
//画选中的边框
if(m_indexList.at(m_index) == i + j*4) {
painter.setBrush(Qt::NoBrush);
painter.setPen(QPen(Qt::white, 16*m_rScaleFactor));
painter.drawRect(sigleRect.adjusted(8*m_rScaleFactor, 8*m_rScaleFactor,
-8*m_rScaleFactor, -8*m_rScaleFactor));
painter.setPen(Qt::NoPen);
}
}
}
}
定时器控制绘制每个选项的边框
m_pTimer = new QTimer(this);
connect(m_pTimer, &QTimer::timeout, this, &MainWindow::onStartLottery);
void MainWindow::onStartLottery()
{
m_index++;
if(m_index >= 12) {
m_index = 0;
m_circle++;
}
//第二圈开始减速
if(m_circle >= 2) {
m_pTimer->start((m_circle)*120);
//第5圈停
if(m_circle >= 4 && m_index == m_prizeIndex)
{
update();
m_pTimer->stop();
//延时一下,不然show的时候会卡
QTimer::singleShot(10, this, [=]{
m_pMaskWidget->setPrizeText(m_prizeMap.value(m_indexList.at(m_index)));
m_pMaskWidget->show();
});
m_pMiddleWidget->stop();
}
}
update();
}
2.开始抽奖点击
中间部分有一圈原点,抽奖的时候一直能红白闪动,为了与转盘的绘制不冲突,这个动画效果放在另外一个widget实现,如果放在一起绘制,两个定时器两个update会产生冲突
void MiddleWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
int middleStartX = 15*m_rScaleFactor;
int middleStartY = 15*m_rScaleFactor;
QRect middleStartRect(middleStartX, middleStartY, 15*m_rScaleFactor, 15*m_rScaleFactor);
painter.setPen(Qt::NoPen);
painter.setBrush(m_bMiddleFlag ? Qt::red : Qt::white);
// painter.setBrush(Qt::white);
int xCount = this->width()/23.*m_rScaleFactor;
int yCount = this->height()/23.*m_rScaleFactor;
for(int i = 0; i < xCount; ++i) {
for(int j = 0; j < yCount; ++j) {
if(i == 0 || i == (xCount - 1) || j == 0 || j == (yCount - 1)) {
QRect ellipseRect = QRect(middleStartRect.x() + i*23*m_rScaleFactor, middleStartRect.y() + j*23*m_rScaleFactor,
middleStartRect.width(), middleStartRect.height());
painter.drawEllipse(ellipseRect);
}
}
}
m_font.setPixelSize(100);
painter.setFont(m_font);
painter.setPen(Qt::white);
painter.drawText(this->rect(), Qt::AlignCenter, u8"开始\n抽奖");
QWidget::paintEvent(event);
}
也是定时器控制闪动的效果
//定时器闪动
m_pMiddleTimer = new QTimer(this);
m_pMiddleTimer->setInterval(300);
connect(m_pMiddleTimer, &QTimer::timeout, this, [=]{
m_bMiddleFlag = !m_bMiddleFlag;
update();
});
3.抽奖结果
抽奖结果就是一个遮罩效果,在遮罩上画个爱心,毕竟七夕还是要有点浪漫的元素
画爱心:
//绘制爱心的path
m_heartPath.setFillRule(Qt::WindingFill);
//调整爱心大小的系数
double k = 18;
float x = 16 * k * sin(0.0)*sin(0.0)*sin(0.0);
float y = 13 * k * cos(0.0) - 5 * k*cos(0.0) - 2 * k*cos(0.0) - cos(0.0);
m_heartPath.moveTo(x, -y);
for (double t = 0.01; t < 100; t += 0.05)
{
x = 16 * k * sin(k*t)*sin(k*t)*sin(k*t);
y = 13 * k * cos(k*t) - 5 * k * cos(2 * k * t) - 2 * k * cos(3 * k * t) - cos(4 * k * t);
m_heartPath.lineTo(x, -y);
}
在paintEvent绘制出来:
void MaskWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setBrush(QColor(0, 0, 0, 150));
painter.setPen(Qt::NoPen);
painter.drawRect(this->rect());
painter.translate(this->width()/2, this->height()/2);
// painter.setBrush(Qt::NoBrush);
painter.setBrush(QColor(253, 189, 210));
// painter.setPen(QColor(253, 189, 210));
painter.drawPath(m_heartPath);
painter.translate(-this->width()/2, -this->height()/2);
painter.setPen(QColor(248, 10, 87));
painter.setFont(m_font);
painter.drawText(this->rect(), Qt::AlignCenter, "恭喜亲爱的抽中了\n" + m_prizeText);
QWidget::paintEvent(event);
}
结语
主体代码就这些,内容不多,把Qt For Android的环境搭起来就很好办了。里面的界面是按比例显示的,为了适配安卓的显示,如果在pc上运行是不适配的,只能在pc上运行看看效果。真正开发安卓的应用还是建议使用QML还做界面。
整个工程的代码在github, 地址为