Qt贝塞尔曲线

目录

  • 引言
  • 核心代码
    • 基本表达
    • 绘制曲线
    • 使用QEasingCurve
  • 完整代码

引言

贝塞尔曲线客户端开发中常见的过渡效果,如界面的淡入淡出、数值变化、颜色变化等等。为了能够更深的了解地理解贝塞尔曲线,本文通过Demo将贝塞尔曲线绘制出来,如下所示:

在这里插入图片描述

核心代码

基本表达

一般来说贝塞尔曲线由起止点以及c1、c2点构成,如上图中,黄色为c1点,绿色为c2点,通过调整c1、c2点去调整整个曲线的变化快慢。

cubic-bezier(.42,0,.58,1)

而这两个点这么怎么去表达呢,如上所示,可以拆分为c1坐标(0.42,0)和c2坐标(0.58,1),而这个坐标则是一个相对坐标,范围是从(0,0)到(1,1),如下图所示:
在这里插入图片描述

绘制曲线

void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
Adds a cubic Bezier curve between the current position and the given endPoint using the control points specified by c1, and c2.
After the curve is added, the current position is updated to be at the end point of the curve.

绘制可以通过QPainterPath::cubicTo完成,需要注意的是其中使用的是这个绘制界面的绝对坐标,而不是相对坐标,因此需要增加两个转换函数方便后续的编码。

首先是百分比坐标转换为实际坐标:

QPoint CubicBezierWidget::PercentToPosition(const QPointF &percent)
{return valid_rect_.bottomLeft() + QPoint(valid_rect_.width() * percent.x(), -valid_rect_.height() * percent.y());
}

再者就是将实际坐标转换为百分比坐标:

QPointF CubicBezierWidget::PositionToPercent(const QPoint &position)
{double x_percent = position.x() - valid_rect_.bottomLeft().x();x_percent = x_percent / valid_rect_.width();double y_percent = valid_rect_.bottomLeft().y() - position.y();y_percent = y_percent / valid_rect_.height();return QPointF(x_percent, y_percent);
}

最后则是起止点以及c1、c2组装起来

    // 关键数据QPointF start_point = valid_rect_.bottomLeft();QPointF end_point = valid_rect_.topRight();QPoint c1_point = PercentToPosition(c1_);QPoint c2_point = PercentToPosition(c2_);QPainterPath path;path.moveTo(start_point);path.cubicTo(c1_point, c2_point, end_point);

使用QEasingCurve

QEasingCurve是Qt核心库的曲线函数,可以使用其作为动画函数的变化曲线,这也是贝塞尔曲线最多的应用场景,通过QAbstractAnimation::setEasingCurve设置。此处为了展示,从曲线中采样10个点,通过函数QEasingCurve::valueForProgress获取对应的y值进行绘制,如下所示:

    QEasingCurve easing_curve(QEasingCurve::BezierSpline);easing_curve.addCubicBezierSegment(QPointF(0.42, 0.0), QPointF(0.58, 1.0), QPointF(1.0, 1.0));QPainterPath path_bezier;int count = 10;for(int i=0; i <= count; i++){double progress = (double)i / count;QPointF target_point(PercentToPosition(QPointF(progress, easing_curve.valueForProgress(progress))));if(i){path_bezier.lineTo(target_point);}else{path_bezier.moveTo(target_point);}}pen.setColor(QColor(241, 148, 138));painter.save();painter.setPen(pen);painter.setBrush(Qt::NoBrush);painter.drawPath(path_bezier);painter.restore();

完整代码

class CubicBezierWidget : public QWidget
{Q_OBJECTQ_PROPERTY(QPointF c1 READ c1 WRITE setC1 NOTIFY c1Changed FINAL)Q_PROPERTY(QPointF c2 READ c2 WRITE setC2 NOTIFY c2Changed FINAL)public:explicit CubicBezierWidget(QWidget *parent = nullptr);enum MouseState {MouseNormal = 0,MouseActivatedC1,MouseActivatedC2,};public:QPointF c1() const;void setC1(QPointF c1);QPointF c2() const;void setC2(QPointF c2);signals:void c1Changed();void c2Changed();protected:void paintEvent(QPaintEvent *event) override;void resizeEvent(QResizeEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void mouseReleaseEvent(QMouseEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;private:QPoint PercentToPosition(const QPointF &percent);QPointF PositionToPercent(const QPoint &position);MouseState StateByPosition(const QPoint &position);private slots:void toUpdate();private:int space_ = 20;int radius_ = 12;int inner_radius_ = 6;// 百分比QPointF c1_;QPointF c2_;QRect valid_rect_;// 鼠标标志MouseState mouse_type_ = MouseNormal;
};
#include <QDebug>
#include <QPainter>
#include <QPainterPath>
#include <QPaintEvent>
#include <QEasingCurve>CubicBezierWidget::CubicBezierWidget(QWidget *parent): QWidget{parent}
{connect(this, &CubicBezierWidget::c1Changed, this, &CubicBezierWidget::toUpdate);connect(this, &CubicBezierWidget::c2Changed, this, &CubicBezierWidget::toUpdate);
}void CubicBezierWidget::paintEvent(QPaintEvent *event)
{// 关键数据QPointF start_point = valid_rect_.bottomLeft();QPointF end_point = valid_rect_.topRight();QPoint c1_point = PercentToPosition(c1_);QPoint c2_point = PercentToPosition(c2_);QPainterPath path;path.moveTo(start_point);path.cubicTo(c1_point, c2_point, end_point);// 初始化画笔QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);painter.setPen(Qt::NoPen);painter.setBrush(Qt::NoBrush);// 背景painter.save();painter.setBrush(QColor(32, 32, 32));painter.drawRect(event->rect());painter.setBrush(QColor(42, 42, 42));painter.drawRect(valid_rect_);painter.restore();QPen pen;pen.setCapStyle(Qt::RoundCap);pen.setWidth(4);pen.setColor(QColor(100, 100, 100));// 连接线painter.save();painter.setPen(pen);painter.drawLine(start_point, c1_point);painter.drawLine(end_point, c2_point);painter.restore();pen.setWidth(6);pen.setColor("white");// 曲线painter.save();painter.setPen(pen);painter.drawPath(path);painter.restore();// 操作圆c1painter.save();painter.setBrush(QColor(247, 220, 111));painter.drawEllipse(c1_point, radius_, radius_);painter.setBrush(Qt::white);painter.drawEllipse(c1_point, inner_radius_, inner_radius_);painter.restore();// 操作圆c2painter.save();painter.setBrush(QColor(72, 201, 176));painter.drawEllipse(c2_point, radius_, radius_);painter.setBrush(Qt::white);painter.drawEllipse(c2_point, inner_radius_, inner_radius_);painter.restore();
}void CubicBezierWidget::resizeEvent(QResizeEvent *event)
{valid_rect_ = rect().adjusted(space_, space_, -space_, -space_);// 还原setC1(QPointF(0, 0));setC2(QPointF(1, 1));
}void CubicBezierWidget::mousePressEvent(QMouseEvent *event)
{mouse_type_ = StateByPosition(event->pos());
}void CubicBezierWidget::mouseReleaseEvent(QMouseEvent *event)
{mouse_type_ = MouseNormal;
}void CubicBezierWidget::mouseMoveEvent(QMouseEvent *event)
{if(mouse_type_ == MouseActivatedC1){QPointF percent = PositionToPercent(event->pos());setC1(percent);}else if(mouse_type_ == MouseActivatedC2){QPointF percent = PositionToPercent(event->pos());setC2(percent);}
}QPoint CubicBezierWidget::PercentToPosition(const QPointF &percent)
{return valid_rect_.bottomLeft() + QPoint(valid_rect_.width() * percent.x(), -valid_rect_.height() * percent.y());
}QPointF CubicBezierWidget::PositionToPercent(const QPoint &position)
{double x_percent = position.x() - valid_rect_.bottomLeft().x();x_percent = x_percent / valid_rect_.width();double y_percent = valid_rect_.bottomLeft().y() - position.y();y_percent = y_percent / valid_rect_.height();return QPointF(x_percent, y_percent);
}CubicBezierWidget::MouseState CubicBezierWidget::StateByPosition(const QPoint &position)
{QPoint c2_position = PercentToPosition(c2_);QRect c2_rect(c2_position.x() - radius_, c2_position.y() - radius_, 2 * radius_, 2* radius_);if(c2_rect.contains(position)){return MouseActivatedC2;}QPoint c1_position = PercentToPosition(c1_);QRect c1_rect(c1_position.x() - radius_, c1_position.y() - radius_, 2 * radius_, 2* radius_);if(c1_rect.contains(position)){return MouseActivatedC1;}return MouseNormal;
}void CubicBezierWidget::toUpdate()
{update();
}QPointF CubicBezierWidget::c1() const
{return c1_;
}void CubicBezierWidget::setC1(QPointF c1)
{c1.setX(qBound(0.0, c1.x(), 1.0));c1.setY(qBound(0.0, c1.y(), 1.0));if (c1_ == c1)return;c1_ = c1;emit c1Changed();
}QPointF CubicBezierWidget::c2() const
{return c2_;
}void CubicBezierWidget::setC2(QPointF c2)
{c2.setX(qBound(0.0, c2.x(), 1.0));c2.setY(qBound(0.0, c2.y(), 1.0));if (c2_ == c2)return;c2_ = c2;emit c2Changed();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/190709.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DevChat:开发者专属的基于IDE插件化编程协助工具

DevChat&#xff1a;开发者专属的基于IDE插件化编程协助工具 一、DevChat 的介绍1.1 DevChat 简介1.2 DevChat 优势 二、DevChat 在 VSCode 上的使用2.1 安装 DevChat2.2 注册 DevChat2.3 使用 DevChat 三、DevChat 的实战四、总结 一、DevChat 的介绍 在AI浪潮的席卷下&#x…

基于开源项目OCR做一个探究(chineseocr_lite)

背景&#xff1a;基于图片识别的技术有很多&#xff0c;应用与各行各业&#xff0c;我们公司围绕电子身份证识别自动录入需求开展&#xff0c;以下是我的研究心得 技术栈&#xff1a;python3.6&#xff0c;chineseocr_lite的onnx推理 环境部署&#xff1a;直接上截图&#xff…

c语言-数据结构-栈和队列的实现和解析

目录 一、栈 1、栈的概念 1.2 栈的结构 2、栈的创建及初始化 3、压栈操作 4、出栈操作 5、显示栈顶元素 6、显示栈空间内元素的总个数 7、释放栈空间 8、测试栈 二、队列 1、队列的概念 1.2 队列的结构 2、队列的创建及初始化 3、入队 4、出队 5、显示队头、队…

在Spring Boot中使用JTA实现对多数据源的事务管理

了解事务的都知道&#xff0c;在我们日常开发中单单靠事务管理就可以解决绝大多数问题了&#xff0c;但是为啥还要提出JTA这个玩意呢&#xff0c;到底JTA是什么呢&#xff1f;他又是具体来解决啥问题的呢&#xff1f; JTA JTA&#xff08;Java Transaction API&#xff09;是…

CG Magic分享效果图中VRay的灯光设置分析

效果图制作中&#xff0c;一张图VRay效果图好不好看主要看灯光、材质、模型、相机、后期这五点。VRay的灯光设置来说是极为重要的。 VRay灯光设置不好&#xff0c;就会出现vray灯光颜色不能正常显示再或是vray的灯光不亮的问题。 vray的灯光怎么设置才能使效果图展现的更加真实…

LabVIEW中如何在网络上使用远程VI服务器

LabVIEW中如何在网络上使用远程VI服务器 如何在网络上使用远程VI服务器&#xff1f; 解答: 首先&#xff0c;需要在远程的计算机上打开一个在VI服务器上的LabVIEW应用程序的引用。这可以通过“Open ApplicationReference“函数实现。然后用“Open VI Reference”函数打开一个…

外贸开发信邮箱如何选?群发邮件有效技巧?

外贸开发信邮箱用哪种好&#xff1f;QQ邮箱群发邮件怎么发&#xff1f; 一个有效的外贸开发信邮箱可以帮助您建立联系、推销产品&#xff0c;并与潜在客户进行沟通。在本文中&#xff0c;蜂邮EDM将分享一些关于如何选择外贸开发信邮箱的建议&#xff0c;以确保您能够与全球客户…

大数据-玩转数据-Flume

一、Flume简介 Flume提供一个分布式的,可靠的,对大数据量的日志进行高效收集、聚集、移动的服务,Flume只能在Unix环境下运行。Flume基于流式架构,容错性强,也很灵活简单。Flume、Kafka用来实时进行数据收集,Spark、Flink用来实时处理数据,impala用来实时查询。二、Flume…

计算机视觉:使用opencv实现银行卡号识别

1 概述 1.1 opencv介绍 OpenCV是Open Source Computer Vision Library&#xff08;开源计算机视觉库&#xff09;的简称&#xff0c;由Intel公司在1999年提出建立&#xff0c;现在由Willow Garage提供运行支持&#xff0c;它是一个高度开源发行的计算机视觉库&#xff0c;可以…

ESP32 C3 smartconfig一键配网报错

AP配网 在调试我的esp32c3的智能配网过程中&#xff0c;发现ap配网使用云智能App是可以正常配置的。 切记用户如果在menu菜单里使能AP配网&#xff0c;默认SSID名字为adh_PK值_MAC后6位。用户可以修改这个apssid的键值&#xff0c;但是要使用云智能app则这个名字的开头必须为ad…

电源基础元件

文章目录 电源基础元件理想电压源理想电流源受控电源 电源基础元件 理想电压源 定义 其两端电压总能保持定值或一定的时间函数&#xff0c;其值与流过它的电流i无关的元件叫理想电压源 理想电压源的电压、电流关系 1.电源两端电压由电源本身决定&#xff0c;与外电路无关&…

windows安装nginx

一、下载安装Nginx 1、官网下载地址&#xff1a;nginx: download 2、下载教程&#xff1a;选择最新的Stable version&#xff08;稳定版本&#xff09;下载到本地 3、下载完成后&#xff0c;解压放入本地非中文的文件夹中&#xff1a; 4、启动nginx&#xff1a;切勿直接双击n…

2390 高校实验室预约系统JSP【程序源码+文档+调试运行】

摘要 本文介绍了一个高校实验室预约系统的设计和实现。该系统包括管理员、教师和学生三种用户&#xff0c;具有基础数据管理、学生管理、教师管理、系统公告管理、实验室管理、实验室预约管理和系统管理等模块。通过数据库设计和界面设计&#xff0c;实现了用户友好的操作体验…

taro(踩坑) npm run dev:weapp 微信小程序开发者工具预览报错

控制台报错信息&#xff1a; VM72:9 app.js错误: Error: module vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js is not defined, require args is ./vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js 环境&#xff1a; node 版本&#x…

Python数据容器(序列操作)

序列 1.什么是序列 序列是指&#xff1a;内容连续、有序。可以使用下标索引的一类数据容器 列表、元组、字符串。均可以视为序列 2.序列的常用操作 - 切片 语法&#xff1a;序列[起始下标:结束下标:步长]起始下标表示从何处开始&#xff0c;可以留空&#xff0c;留空视作从…

华为ensp:为vlan配置ip

配置对应vlan的ip vlan1 interface Vlanif 1 进入vlan1 ip address 192.168.1.254 24配置IP为192.168.1.254 子网掩码为24位 这样就配置上ip了 vlan2 interface Vlanif 2 ip address 192.168.2.254 24 vlan3 interface Vlanif 3 ip address 192.168.3.254 24 查看结果 …

JDK更换版本不生效问题

JDK版本更换 问题: 当本地电脑拥有多个版本jdk时, 切换jdk版本不生效 解决方案: 1.查看环境变量(高版本的jdk安装时自动注入环境变量) 2.将Path里面的jdk的bin配置上移到最上面 3.查看jdk版本, java -version 启动项目,显示如下使用了jdk17

【Python小程序】浮点矩阵加减法

一、内容简介 本文使用Python编写程序&#xff0c;实现2个m * n矩阵的加、减法。具体过程如下&#xff1a; 给定两个m*n矩阵A和B&#xff0c;返回A与B的和或差。 二、求解方法 将两个矩阵对应位置上的元素相加。 三、Python代码 import numpy as np# 用户输入两个矩阵的维…

贝锐向日葵如何实现无人值守远程控制?

1.适用场景 &#xff08;1&#xff09;远程公司电脑应急办公&#xff08;2&#xff09;远程家里电脑游戏挂机&#xff08;3&#xff09;异地远程传输文件 2.操作步骤 &#xff08;1&#xff09;电脑安装向日葵个人版并登录贝锐账号&#xff08;点击注册&#xff09;&#xf…

木板上的蚂蚁(c++题解)

题目描述 有一块木板&#xff0c;长度为 n 个 单位 。一些蚂蚁在木板上移动&#xff0c;每只蚂蚁都以 每秒一个单位 的速度移动。其中&#xff0c;一部分蚂蚁向 左 移动&#xff0c;其他蚂蚁向 右 移动。 当两只向 不同 方向移动的蚂蚁在某个点相遇时&#xff0c;它们会同时改…