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.mzph.cn/news/140035.shtml

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

相关文章

基于SSM的数据结构课程网络学习平台

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

Git系列之分支与标签的使用及应用场景模拟

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《Git实战开发》。&#x1f3af;&#x1f3af; &a…

Java学习_对象

对象在计算机中的执行原理 类和对象的一些注意事项 this关键字 构造器 构造器是一种特殊的方法 : 特殊之处在于&#xff0c;名字必须与所在类的名字一样&#xff0c;而且不能写返回值类型 封装 封装的设计规范&#xff1a;合理隐藏、合理暴露 实体类 成员变量和局部变量的区别 …

微信聊天,收到二维码图片就自动帮你提取出来的方法

10-3 如果你是二维码收集的重度用户&#xff0c;那我非常推荐你好好阅读本文&#xff0c;也许可以帮你解决你的问题&#xff0c;比如做网推的人&#xff0c;需要常年混迹在各种微信群&#xff0c;那如何在各个微信群中收集到群友分享出来的二维码&#xff0c;并且要立即保存出…

组件的设计原则

目录 插槽的基本概念 基础用法 具名插槽 使用场景 布局控制 嵌套组件 组件的灵活性 高级用法 作用域插槽 总结 前言 Vue 的 slot 是一项强大的特性&#xff0c;用于组件化开发中。它允许父组件向子组件传递内容&#xff0c;使得组件更加灵活和可复用。通过 slot&…

Python之函数进阶-nonlocal和LEGB

Python之函数进阶-nonlocal和LEGB nonlocal语句 nonlocal:将变量标记为不在本地作用域定义&#xff0c;而是在上级的某一级局部作用域中定义&#xff0c;但不能是全局作用域中定义。 函数的销毁 定义一个函数就是生成一个函数对象&#xff0c;函数名指向的就是函数对象。可…

华为云Ascend310服务器使用

使用华为云服务器 cpu: 16vCPUs Kunpeng 920 内存&#xff1a;16GiB gpu&#xff1a;4* HUAWEI Ascend 310 cann: 20.1.rc1 操作系统&#xff1a;Ubuntu aarch64目的 使用该服务器进行docker镜像编译&#xff0c;测试模型。 已知生产环境&#xff1a;mindx版本为3.0.rc3&a…

【机器学习】Kmeans聚类算法

一、聚类简介 Clustering (聚类)是常见的unsupervised learning (无监督学习)方法&#xff0c;简单地说就是把相似的数据样本分到一组&#xff08;簇&#xff09;&#xff0c;聚类的过程&#xff0c;我们并不清楚某一类是什么&#xff08;通常无标签信息&#xff09;&#xff0…

通义千问, 文心一言, ChatGLM, GPT-4, Llama2, DevOps 能力评测

引言 “克隆 dev 环境到 test 环境&#xff0c;等所有服务运行正常之后&#xff0c;把访问地址告诉我”&#xff0c;“检查所有项目&#xff0c;告诉我有哪些服务不正常&#xff0c;给出异常原因和修复建议”&#xff0c;在过去的工程师生涯中&#xff0c;也曾幻想过能够通过这…

【FAQ】Gradle开发问题汇总

1. buildSrc依赖Spring Denpendency时报错 来自预编译脚本的插件请求不能包含版本号。请从有问题的请求中删除该版本&#xff0c;并确保包含所请求插件io.spring.dependency-management的模块是一个实现依赖项 解决方案 https://www.5axxw.com/questions/content/uqw0grhttps:/…

基于springboot实现桥牌计分管理系统项目【项目源码】计算机毕业设计

基于springboot实现桥牌计分管理系统演示 JAVA简介 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#…

MYSQL操作详解

一)计算机的基本结构 但是实际上&#xff0c;更多的是这种情况: 二)MYSQL中的数据类型: 一)数值类型: 数据类型内存大小(字节)说明bit(M)M指定位数,默认为1单个二进制位值&#xff0c;或者为0或者为1&#xff0c;主要用于开/关标志tinyint1字节1个字节的整数值&#xff0c;支持…

使用openvc进行人脸检测:Haar级联分类器

1 人脸检测介绍 1.1 什么是人脸检测 人脸检测的目标是确定图像或视频中是否存在人脸。如果存在多个面&#xff0c;则每个面都被一个边界框包围&#xff0c;因此我们知道这些面的位置 人脸检测算法的主要目标是准确有效地确定图像或视频中人脸的存在和位置。这些算法分析数据…

一文入门Springboot+actuator+Prometheus+Grafana

环境介绍 技术栈 springbootmybatis-plusmysqloracleactuatorPrometheusGrafana 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 本地主机应用 192.168.1.9:8007 PrometheusGrafana安装在同一台主机 http://…

[西湖论剑 2022]real_ez_node

文章目录 前置知识EJS模板注入&#xff08;CVE-2022-29078&#xff09;原型链污染漏洞 &#xff08;CVE-2021-25928&#xff09;HTTP响应拆分攻击&#xff08;CRLF&#xff09; 解题过程代码审计构造payload 前置知识 EJS模板注入&#xff08;CVE-2022-29078&#xff09; EJS…

.net在使用存储过程中IN参数的拼接方案,使用Join()方法

有时候拼接SQL语句时&#xff0c;可能会需要将list中的元素都加上单引号&#xff0c;并以逗号分开&#xff0c;但是Join只能简单的分开&#xff0c;没有有单引号&#xff01; 1.第一种拼接方案 List<string> arrIds new List<string>(); arrIds.Add("aa&qu…

Cross-Origin跨站问题详解(跨站请求、跨站cookie)

背景&#xff1a;我部署frontend和backend到两个不同的docker容器&#xff0c;前端路径为http://localhost:3000&#xff0c;后端路径为http://localhost:4000。我设置了用户登录功能&#xff0c;并使用cookie进行session管理。当我的前端登录时&#xff0c;创建了一个session&…

【Java 进阶篇】Java与JQuery选择器:解锁前端开发的魔法大门

在前端开发的世界中&#xff0c;选择器是我们与HTML文档进行互动的钥匙&#xff0c;而Java和JQuery则为我们提供了强大的工具&#xff0c;使得前端开发不再是一个艰深的谜题。本篇博客将围绕Java与JQuery选择器展开&#xff0c;深入解析选择器的奥秘&#xff0c;为你打开前端开…

C++ 模板保姆级详解——template<class T>(什么是模板?模板分哪几类?模板如何应用?)

目录 一、前言 二、 什么是C模板 &#x1f4a6;泛型编程的思想 &#x1f4a6;C模板的分类 三、函数模板 &#x1f4a6;函数模板概念 &#x1f4a6;函数模板格式 &#x1f4a6;函数模板的原理 &#x1f4a6;函数模板的实例化 &#x1f34e;隐式实例化 &#x1f349;显式实…

【STM32】TIM2的PWM:脉冲宽度调制--标准库

注意点&#xff1a; TIM_Period---->指要进行比较的值Compare TIM_Prescaler----> 指要进行分频的值【分频值/原始时钟值】 PWM是一种周期固定&#xff0c;脉宽可调整的输出波形。 https://www.cnblogs.com/brianblog/p/7117896.html 0.通用寄存器输出 1.捕获/比较通道…