网站的建设进入哪个科目营销策划公司的经营范围
1.界面实现效果
以下是具体的项目需要用到的效果展示,用于验证字母。
2.简介
自定义CaptchaMovableLabel,继承自QLabel类:
中间的4个字母,就是CaptchaMovableLabel类来实例化的对象。
主要功能如下:
1.显示字母;
2.实现了鼠标移动事件,使字母可拖动;
3.存在定时器,不断改变字母颜色;
4.绘制字母时,可旋转一定角度;
#ifndef CAPTCHAMOVABLELABEL_H
#define CAPTCHAMOVABLELABEL_H#include <QLabel>
#include <QTime>
#include <QPropertyAnimation>
#include <QDebug>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QApplication>
#include <QGraphicsDropShadowEffect>
#include <cmath>
#include <QTimer>#define CAPTCHA_REFRESH_DURATION 300 // 刷新的动画时长
#define CAPTCHA_CHAR_ANGLE_MAX 20 // 最大旋转角:20°
#define CAPTCHA_SHADOW_BLUR_MAX 80 // 最大的阴影模糊半径class CaptchaMovableLabel : public QLabel
{Q_OBJECTQ_PROPERTY(int refreshProgress READ getRefreshProgress WRITE setRefreshProgress)Q_PROPERTY(int pressProgress READ getPressProgress WRITE setPressProgress)
public:CaptchaMovableLabel(QWidget* parent);void setAngle(int angle);void setColor(QColor color);void setText(QString ch);void startRefreshAnimation();void setMoveBorder(QRect rect);QString text();protected:void paintEvent(QPaintEvent *) override;void mousePressEvent(QMouseEvent *ev) override;void mouseMoveEvent(QMouseEvent *ev) override;void mouseReleaseEvent(QMouseEvent *ev) override;private:void startPressAnimation(int end);void setRefreshProgress(int g);int getRefreshProgress();inline bool isNoAni();void setPressProgress(int g);int getPressProgress();private slots://设置rgb颜色void slotMovePos();private:QPoint press_pos;bool dragging = false;bool moved = false;QGraphicsDropShadowEffect effect;QString ch;QColor color;int angle = 0;int refreshProgress = 100;QString prevCh;QColor prevColor;int prevAngle = 0;QString prevChar;int pressProgress = 0;bool inited = false;QTimer movingTimer;int moveR, moveG, moveB;
};#endif // CAPTCHAMOVABLELABEL_H#include "captchamovablelabel.h"CaptchaMovableLabel::CaptchaMovableLabel(QWidget *parent) : QLabel(parent)
{effect.setOffset(0, 0);
// effect.setBlurRadius(8);setGraphicsEffect(&effect);movingTimer.setInterval(30);movingTimer.setSingleShot(false);connect(&movingTimer, SIGNAL(timeout()), this, SLOT(slotMovePos()));
}void CaptchaMovableLabel::setAngle(int angle)
{this->prevAngle = this->angle;this->angle = angle;
}void CaptchaMovableLabel::setColor(QColor color)
{this->prevColor = this->color;this->color = color;moveR = qrand() % 5;moveG = qrand() % 5;moveB = qrand() % 5;movingTimer.start();
}void CaptchaMovableLabel::setText(QString text)
{this->prevCh = this->ch;this->ch = text;// 计算合适的高度QFontMetrics fm(this->font());double w = fm.horizontalAdvance(text)+2;double h = fm.height();const double PI = 3.141592;int xieHalf = sqrt(w*w/4+h*h/4); // 斜边的一半double a = atan(w/h) + CAPTCHA_CHAR_ANGLE_MAX * PI / 180; // 最大的倾斜角度int w2 = xieHalf * sin(a) * 2;a = atan(w/h) - CAPTCHA_CHAR_ANGLE_MAX * PI / 180;int h2 = xieHalf * cos(a) * 2;resize(w2, h2);
}void CaptchaMovableLabel::startRefreshAnimation()
{if (!inited) // 第一次,直接显示,取消动画{inited = true;return ;}QPropertyAnimation* ani = new QPropertyAnimation(this, "refreshProgress");ani->setStartValue(0);ani->setEndValue(100);ani->setDuration(qrand() % (CAPTCHA_REFRESH_DURATION / 3) + CAPTCHA_REFRESH_DURATION / 3);ani->start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));connect(ani, SIGNAL(finished()), &movingTimer, SLOT(start()));
}QString CaptchaMovableLabel::text()
{return ch;
}void CaptchaMovableLabel::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.setFont(this->font());painter.setRenderHint(QPainter::SmoothPixmapTransform);int w2 = width()/2, h2 = height()/2;painter.translate(w2, h2); // 平移到中心,绕中心点旋转if (isNoAni()) // 不在动画中,直接绘制{painter.setPen(color);painter.rotate(angle);painter.drawText(QRect(-w2, -h2, width(), height()), Qt::AlignCenter, ch);return ;}// 动画里面,前后渐变替换double newProp = refreshProgress / 100.0;double oldProp = 1.0 - newProp;double a = prevAngle * oldProp + angle * newProp + 0.5;painter.save();painter.rotate(a);QColor c = prevColor;c.setAlpha(c.alpha() * oldProp); // 旧文字渐渐消失painter.setPen(c);painter.drawText(QRect(-w2,-h2,width(),height()), Qt::AlignCenter, prevCh);c = this->color;c.setAlpha(c.alpha() * newProp); // 新文字渐渐显示painter.setPen(c);painter.drawText(QRect(-w2, -h2, width(), height()), Qt::AlignCenter, ch);painter.restore();
}void CaptchaMovableLabel::mousePressEvent(QMouseEvent *ev)
{if (ev->button() == Qt::LeftButton){// 开始拖拽press_pos = ev->pos();dragging = true;moved = false;this->raise();movingTimer.stop();startPressAnimation(200);return ev->accept();}QLabel::mousePressEvent(ev);
}void CaptchaMovableLabel::mouseMoveEvent(QMouseEvent *ev)
{if (dragging && ev->buttons() & Qt::LeftButton){if (!moved && (ev->pos() - press_pos).manhattanLength() < QApplication::startDragDistance()){return QLabel::mouseMoveEvent(ev); // 还没到这时候}moved = true;move(this->pos() + ev->pos() - press_pos);ev->accept();return ;}QLabel::mouseMoveEvent(ev);
}void CaptchaMovableLabel::mouseReleaseEvent(QMouseEvent *ev)
{if (dragging){// 结束拖拽dragging = false;movingTimer.start();startPressAnimation(0);}if (moved)return ev->accept();QLabel::mouseReleaseEvent(ev);
}void CaptchaMovableLabel::startPressAnimation(int end)
{QPropertyAnimation* ani = new QPropertyAnimation(this, "pressProgress");ani->setStartValue(pressProgress);ani->setEndValue(end);ani->setDuration(CAPTCHA_REFRESH_DURATION << 1);ani->start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));
}void CaptchaMovableLabel::setRefreshProgress(int g)
{this->refreshProgress = g;update();
}int CaptchaMovableLabel::getRefreshProgress()
{return refreshProgress;
}bool CaptchaMovableLabel::isNoAni()
{return refreshProgress == 100;
}void CaptchaMovableLabel::setPressProgress(int g)
{this->pressProgress = g;double off = g / 100;effect.setBlurRadius(g / 20.0);effect.setOffset(-off, off);
}int CaptchaMovableLabel::getPressProgress()
{return pressProgress;
}void CaptchaMovableLabel::slotMovePos()
{if (refreshProgress < 100)return ;int val = color.red() + moveR;if ( val > 255){val = 255;moveR = - qrand() % 5;}else if (val < 0){val = 0;moveR = - qrand() % 5;}color.setRed(val);val = color.green() + moveG;if ( val > 255){val = 255;moveG = - qrand() % 5;}else if (val < 0){val = 0;moveG = - qrand() % 5;}color.setGreen(val);val = color.blue() + moveB;if ( val > 255){val = 255;moveB = - qrand() % 5;}else if (val < 0){val = 0;moveB = - qrand() % 5;}color.setBlue(val);
}
自定义CaptchaLabel类,此类继承QWidget用于存在上面的4个字母。
主要功能如下:
1.画噪音点,背景上绘制无数个随机颜色的点;
2.画噪音线,这个线条是动态的,随时间更改起渐变颜色,线条位置;
3.鼠标点击,生成随机字母。
#ifndef CAPTCHALABEL_H
#define CAPTCHALABEL_H#include "captchamovablelabel.h"
#include <QTimer>#define CAPTCHAR_COUNT 4 // 验证码字符数量class CaptchaLabel : public QWidget
{Q_OBJECTQ_PROPERTY(int refreshProgress READ getRefreshProgress WRITE setRefreshProgress)
public:CaptchaLabel(QWidget* parent = nullptr);void refresh();bool match(QString input);private:void initView();void initData();void setRefreshProgress(int g);int getRefreshProgress();bool isNoAni();private slots:void moveNoiseLines();protected:void paintEvent(QPaintEvent* ) override;void mouseReleaseEvent(QMouseEvent *event) override;private:CaptchaMovableLabel* charLabels[CAPTCHAR_COUNT]; // Label控件QList<QPoint> noisePoints; // 噪音点QList<QColor> pointColors; // 点的颜色QList<QPointF> lineStarts; // 噪音线起始点QList<QPointF> lineEnds; // 噪音先结束点QList<QPointF> startsV; // 起始点的移动速度(带方向)QList<QPointF> endsV; // 结束点的速度(带方向)QList<QColor> lineColor1s; // 线的渐变色1QList<QColor> lineColor2s; // 线的渐变色2QList<int> lineWidths;QTimer movingTimer;int refreshProgress = 100;QList<QPoint> noisePoints2; // 新的位置int autoRefreshMax = 2; // match错误几次后就自动刷新int matchFailCount = 0; // match错误次数int matchFailAndRefreshCount = 0; // 失败且导致刷新的次数,强行刷新
};#endif // CAPTCHALABEL_H#include "captchalabel.h"CaptchaLabel::CaptchaLabel(QWidget *parent) : QWidget(parent)
{initView();// 这里延迟,等待布局结束QTimer::singleShot(0, [=]{initData();refresh();});
}void CaptchaLabel::initView()
{// 初始化控件for (int i = 0; i < CAPTCHAR_COUNT; i++){charLabels[i] = new CaptchaMovableLabel(this);charLabels[i]->move(0, 0);}// 初始化时钟movingTimer.setInterval(30);movingTimer.setSingleShot(false);movingTimer.start();connect(&movingTimer, SIGNAL(timeout()), this, SLOT(moveNoiseLines()));
}void CaptchaLabel::initData()
{// 初始化噪音线auto getRandomColor = [=]{return QColor(qrand() % 255, qrand() % 255, qrand() % 255);};int w = width(), h = height();int count = 20/*w * h / 400*/;int penW = qMin(w, h) / 15;for (int i = 0; i < count; i++){lineStarts.append(QPointF(qrand() % w, qrand() % h));lineEnds.append(QPointF(qrand() % w, qrand() % h));startsV.append(QPointF((qrand() % 30 - 15) / 10.0, (qrand() % 30 - 15) / 10.0));endsV.append(QPointF((qrand() % 30 - 15) / 10.0, (qrand() % 30 - 15) / 10.0));lineWidths.append(qrand() % penW + 1);lineColor1s.append(getRandomColor());lineColor2s.append(getRandomColor());}
}void CaptchaLabel::setRefreshProgress(int g)
{this->refreshProgress = g;update();
}int CaptchaLabel::getRefreshProgress()
{return refreshProgress;
}bool CaptchaLabel::isNoAni()
{return refreshProgress == 100;
}void CaptchaLabel::moveNoiseLines()
{int w = width(), h = height();double vBase = 100.0; // 大概最快要3秒钟走完for (int i = 0; i < lineStarts.size(); i++){QPointF& pos = lineStarts[i];pos += startsV.at(i);if (pos.x() < 0)startsV[i].setX(qrand() % w / vBase);else if (pos.x() > w)startsV[i].setX(- qrand() % w / vBase);if (pos.y() < 0)startsV[i].setY(qrand() % h / vBase);else if (pos.y() > h)startsV[i].setY(- qrand() % h / vBase);}for (int i = 0; i < lineEnds.size(); i++){QPointF& pos = lineEnds[i];pos += endsV.at(i);if (pos.x() < 0)endsV[i].setX(qrand() % w / vBase);else if (pos.x() > w)endsV[i].setX(- qrand() % w / vBase);if (pos.y() < 0)endsV[i].setY(qrand() % h / vBase);else if (pos.y() > h)endsV[i].setY(- qrand() % h / vBase);}update();
}
void CaptchaLabel::refresh()
{int width = this->width();int height = this->height();// 清空全部内容for (int i = 0; i < CAPTCHAR_COUNT; i++)charLabels[i]->hide();refreshProgress = -1;update();// 获取背景底色QPixmap rend(this->size());render(&rend);QColor bgColor = rend.toImage().pixelColor(width/2, height/2);int br = bgColor.red(), bg = bgColor.green(), bb = bgColor.blue();// 开始随机生成const int border = 10;int leftest = width / border;int topest = height / border;int wid = width - leftest * 2;int hei = height - topest * 2;for (int i = 0; i < CAPTCHAR_COUNT; i++){auto label = charLabels[i];// 随机大小QFont font;font.setPointSize( qrand() % 8 + 22 );label->setFont(font);// 随机旋转label->setAngle( qrand() % (CAPTCHA_CHAR_ANGLE_MAX*2) - CAPTCHA_CHAR_ANGLE_MAX);// 生成随机字符const QString pool = "QWERTYUIOPASDFGHJKLZXCVBNM";QChar rc = pool.at(qrand() % pool.size());// 此时会调整大小,setText必须在setFont之后label->setText(rc);// 生成随机位置(排除边缘)int left = leftest + wid * i / CAPTCHAR_COUNT;int right = leftest + wid * (i+1) / CAPTCHAR_COUNT - label->width();int x = qrand() % qMax(right-left, 1) + left;int y = qrand() % qMax(hei - label->height(), 1) + topest;label->show(); // 之前是hide状态QPropertyAnimation * ani = new QPropertyAnimation(label, "pos");ani->setStartValue(label->pos());ani->setEndValue(QPoint(x, y));ani->setDuration(qrand() % (CAPTCHA_REFRESH_DURATION/2) + CAPTCHA_REFRESH_DURATION/2);ani->setEasingCurve(QEasingCurve::OutQuart);ani->start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));// 生成随机颜色,且必须和背景颜色有区分度QColor color;while (true){int r = qrand() % 255;int g = qrand() % 255;int b = qrand() % 255;if (abs(r-br) + abs(g-bg) + abs(b-bb) > 383){color = QColor(r, g, b);break;}}label->setColor(color);label->startRefreshAnimation();}// 生成噪音点int count = wid * hei / border; // 点的数量if (noisePoints.size() == 0) // 第一次{for (int i = 0; i < count; i++){int x = qrand() % width;int y = qrand() % height;noisePoints.append(QPoint(x, y / 2));noisePoints2.append(QPoint(x, y));pointColors.append(QColor(qrand() % 255, qrand() % 255, qrand() % 255));}}else{noisePoints = noisePoints2;count = noisePoints.size();noisePoints2.clear();for (int i = 0; i < count; i++){noisePoints2.append(QPoint(qrand() % width, qrand() % height));}}// 生成噪音线QPropertyAnimation* ani = new QPropertyAnimation(this, "refreshProgress");ani->setStartValue(0);ani->setEndValue(100);ani->setDuration(qrand() % (CAPTCHA_REFRESH_DURATION) + CAPTCHA_REFRESH_DURATION);ani->start();connect(ani, SIGNAL(finished()), ani, SLOT(deleteLater()));
}/*** 判断能否匹配*/
bool CaptchaLabel::match(QString input)
{// 根据label的位置排序std::sort(charLabels, charLabels+CAPTCHAR_COUNT, [=](QLabel* a, QLabel* b){if (a->pos().x() == b->pos().x())return a->pos().y() < b->pos().y();return a->pos().x() < b->pos().x();});// 按顺序组合成新的字符串QString captcha;for (int i = 0; i < CAPTCHAR_COUNT; i++)captcha += charLabels[i]->text();// 进行比较if (input.toUpper() == captcha)return true;// 记录失败matchFailCount++;if (matchFailCount >= autoRefreshMax // 达到刷新的次数|| matchFailAndRefreshCount > 2) // 多次错误导致刷新{refresh();matchFailAndRefreshCount++;matchFailCount = 0;}return false;
}void CaptchaLabel::paintEvent(QPaintEvent *)
{QPainter painter(this);if (refreshProgress == -1) // 不画,可能需要获取背景颜色return ;// 画噪音点if (isNoAni()){// 显示随机的点for (int i = 0; i < noisePoints2.size(); i++){painter.setPen(pointColors.at(i));painter.drawPoint(noisePoints2.at(i));}}else{// 动画过程中的点的移动double newProp = refreshProgress / 100.0;double oldProp = 1.0 - newProp;int count = qMin(noisePoints.size(), noisePoints2.size());for (int i = 0; i < count; i++){QPoint pt1 = noisePoints.at(i);QPoint pt2 = noisePoints2.at(i);QPoint pt( pt1.x() * oldProp + pt2.x() * newProp,pt1.y() * oldProp + pt2.y() * newProp );painter.setPen(pointColors.at(i));painter.drawPoint(pt);}}// 画噪音线painter.setRenderHint(QPainter::Antialiasing);for (int i = 0; i < lineStarts.size(); i++){QLinearGradient grad(lineStarts.at(i), lineEnds.at(i));grad.setColorAt(0, lineColor1s.at(i));grad.setColorAt(1, lineColor2s.at(i));painter.setPen(QPen(grad, lineWidths.at(i)));painter.drawLine(lineStarts.at(i), lineEnds.at(i));}
}void CaptchaLabel::mouseReleaseEvent(QMouseEvent *event)
{if (QRect(0,0,width(),height()).contains(event->pos()))refresh();QWidget::mouseReleaseEvent(event);
}
3.使用
新建MainWindow,拖动一个QWidget,提升为CaptchaLabel即可。