├── BezierWave.pro ├── BezierWave.pro.user ├── LICENSE ├── README.md ├── bezierwavebean.cpp ├── bezierwavebean.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui └── pictures └── picture.gif /BezierWave.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2019-05-31T12:01:22 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = BezierWave 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | CONFIG += c++11 26 | 27 | SOURCES += \ 28 | main.cpp \ 29 | mainwindow.cpp \ 30 | bezierwavebean.cpp 31 | 32 | HEADERS += \ 33 | mainwindow.h \ 34 | bezierwavebean.h 35 | 36 | FORMS += \ 37 | mainwindow.ui 38 | 39 | # Default rules for deployment. 40 | qnx: target.path = /tmp/$${TARGET}/bin 41 | else: unix:!android: target.path = /opt/$${TARGET}/bin 42 | !isEmpty(target.path): INSTALLS += target 43 | -------------------------------------------------------------------------------- /BezierWave.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {2a6768ec-1d4c-4eb5-99c7-39c6a5971fb8} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | true 41 | false 42 | 0 43 | true 44 | true 45 | 0 46 | 8 47 | true 48 | 1 49 | true 50 | true 51 | true 52 | false 53 | 54 | 55 | 56 | ProjectExplorer.Project.PluginSettings 57 | 58 | 59 | true 60 | 61 | 62 | 63 | ProjectExplorer.Project.Target.0 64 | 65 | Desktop Qt 5.13.1 GCC 64bit 66 | Desktop Qt 5.13.1 GCC 64bit 67 | qt.qt5.5131.gcc_64_kit 68 | 0 69 | 0 70 | 0 71 | 72 | /home/mrxy001/Qt/build-BezierWave-Desktop_Qt_5_13_1_GCC_64bit-Debug 73 | 74 | 75 | true 76 | qmake 77 | 78 | QtProjectManager.QMakeBuildStep 79 | true 80 | 81 | false 82 | false 83 | false 84 | 85 | 86 | true 87 | Make 88 | 89 | Qt4ProjectManager.MakeStep 90 | 91 | false 92 | 93 | 94 | false 95 | 96 | 2 97 | Build 98 | 99 | ProjectExplorer.BuildSteps.Build 100 | 101 | 102 | 103 | true 104 | Make 105 | 106 | Qt4ProjectManager.MakeStep 107 | 108 | true 109 | clean 110 | 111 | false 112 | 113 | 1 114 | Clean 115 | 116 | ProjectExplorer.BuildSteps.Clean 117 | 118 | 2 119 | false 120 | 121 | Debug 122 | Debug 123 | Qt4ProjectManager.Qt4BuildConfiguration 124 | 2 125 | true 126 | 127 | 128 | /home/mrxy001/Qt/build-BezierWave-Desktop_Qt_5_13_1_GCC_64bit-Release 129 | 130 | 131 | true 132 | qmake 133 | 134 | QtProjectManager.QMakeBuildStep 135 | false 136 | 137 | false 138 | false 139 | true 140 | 141 | 142 | true 143 | Make 144 | 145 | Qt4ProjectManager.MakeStep 146 | 147 | false 148 | 149 | 150 | false 151 | 152 | 2 153 | Build 154 | 155 | ProjectExplorer.BuildSteps.Build 156 | 157 | 158 | 159 | true 160 | Make 161 | 162 | Qt4ProjectManager.MakeStep 163 | 164 | true 165 | clean 166 | 167 | false 168 | 169 | 1 170 | Clean 171 | 172 | ProjectExplorer.BuildSteps.Clean 173 | 174 | 2 175 | false 176 | 177 | Release 178 | Release 179 | Qt4ProjectManager.Qt4BuildConfiguration 180 | 0 181 | true 182 | 183 | 184 | /home/mrxy001/Qt/build-BezierWave-Desktop_Qt_5_13_1_GCC_64bit-Profile 185 | 186 | 187 | true 188 | qmake 189 | 190 | QtProjectManager.QMakeBuildStep 191 | true 192 | 193 | false 194 | true 195 | true 196 | 197 | 198 | true 199 | Make 200 | 201 | Qt4ProjectManager.MakeStep 202 | 203 | false 204 | 205 | 206 | false 207 | 208 | 2 209 | Build 210 | 211 | ProjectExplorer.BuildSteps.Build 212 | 213 | 214 | 215 | true 216 | Make 217 | 218 | Qt4ProjectManager.MakeStep 219 | 220 | true 221 | clean 222 | 223 | false 224 | 225 | 1 226 | Clean 227 | 228 | ProjectExplorer.BuildSteps.Clean 229 | 230 | 2 231 | false 232 | 233 | Profile 234 | Profile 235 | Qt4ProjectManager.Qt4BuildConfiguration 236 | 0 237 | true 238 | 239 | 3 240 | 241 | 242 | 0 243 | 部署 244 | 245 | ProjectExplorer.BuildSteps.Deploy 246 | 247 | 1 248 | Deploy Configuration 249 | 250 | ProjectExplorer.DefaultDeployConfiguration 251 | 252 | 1 253 | 254 | 255 | dwarf 256 | 257 | cpu-cycles 258 | 259 | 260 | 250 261 | -F 262 | true 263 | 4096 264 | false 265 | false 266 | 1000 267 | 268 | true 269 | 270 | false 271 | false 272 | false 273 | false 274 | true 275 | 0.01 276 | 10 277 | true 278 | kcachegrind 279 | 1 280 | 25 281 | 282 | 1 283 | true 284 | false 285 | true 286 | valgrind 287 | 288 | 0 289 | 1 290 | 2 291 | 3 292 | 4 293 | 5 294 | 6 295 | 7 296 | 8 297 | 9 298 | 10 299 | 11 300 | 12 301 | 13 302 | 14 303 | 304 | 2 305 | 306 | BezierWave 307 | 308 | Qt4ProjectManager.Qt4RunConfiguration:/home/mrxy001/Qt/Bezier-Wave/BezierWave.pro 309 | 310 | 3768 311 | false 312 | true 313 | true 314 | false 315 | false 316 | true 317 | 318 | /home/mrxy001/Qt/build-BezierWave-Desktop_Qt_5_13_1_GCC_64bit-Debug 319 | 320 | 1 321 | 322 | 323 | 324 | ProjectExplorer.Project.TargetCount 325 | 1 326 | 327 | 328 | ProjectExplorer.Project.Updater.FileVersion 329 | 22 330 | 331 | 332 | Version 333 | 22 334 | 335 | 336 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 贝塞尔曲线波浪 2 | === 3 | 4 | ## 安装与运行环境 5 | 6 | - 语言:C++ 7 | - 框架:Qt 11.3 8 | - 平台:Windows 9 | 10 | ## 截图 11 | 12 | ![截图](pictures/picture.gif) 13 | 14 | ## 算法 15 | 16 | 将屏幕水平平均分为10块,在一定范围内随机高度的12个点(左右出头),通过贝塞尔曲线连接连续的点,即可绘制成一段段连续的波浪。 17 | 18 | 但是每两端波浪都会有折线凸起,根据贝塞尔曲线的原理,两个控制点中点作为新的控制点,原来的两点连线即为该控制点在新曲线上的切线,这样既可绘制成只由三个控制点影响的`B样条`。 19 | 20 | 为了逼真,通过时钟以及大量状态机确定随机范围,例如在某一段时间整体偏下方,某一段时间整体偏上方,模仿大自然水面潮涨潮落。 21 | 22 | ## 用法 23 | 24 | 创建: 25 | 26 | ```C++ 27 | MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) 28 | { 29 | bw1 = new BezierWaveBean(this); 30 | bw1->set_offsety(geometry().height()/20); // 垂直偏移位置【可选】 31 | bw1->set_speedx(4); // 设置速度 32 | bw1->start(); 33 | } 34 | ``` 35 | 36 | 绘制: 37 | 38 | ```C++ 39 | void MainWindow::paintEvent(QPaintEvent *e) 40 | { 41 | QPainter painter(this); 42 | painter.setRenderHint(QPainter::Antialiasing, true); // 抗锯齿 43 | 44 | painter.fillPath(bw1->getPainterPath(painter), QColor(255, 0, 0, 50)); 45 | 46 | return QMainWindow::paintEvent(e); 47 | } 48 | ``` 49 | 50 | 尺寸: 51 | 52 | ```C++ 53 | void MainWindow::resizeEvent(QResizeEvent *e) 54 | { 55 | bw1->set_rect(geometry()); 56 | return QMainWindow::resizeEvent(e); 57 | } 58 | ``` 59 | 60 | 开关: 61 | 62 | ```C++ 63 | bw1->start(); // 开始 64 | bw1->pause(); // 暂停 65 | bw1->resume(); // 继续 66 | ``` 67 | 68 | -------------------------------------------------------------------------------- /bezierwavebean.cpp: -------------------------------------------------------------------------------- 1 | #include "bezierwavebean.h" 2 | 3 | BezierWaveBean::BezierWaveBean(QWidget* parent) : QObject(parent), target(parent), mt(rd()) 4 | { 5 | srand(static_cast(time(nullptr))); 6 | 7 | count = 8; // 点的总数 8 | inter = target->geometry().width()/(count-4); 9 | appear_speedy = 3; // 出现时的最大速度 10 | 11 | // speedy = appear_speedy; // y移动速度 12 | offsety = 0; // y累计偏移 13 | offsety_direct = -1; // y短期偏移方向,-1上 和 1下 14 | speedx = 3; // x移动速度 15 | offsetx = 0; // x累计偏移 16 | 17 | speedy_step = 3; 18 | _offsety = 0; 19 | _max_offsety = target->geometry().height()/10; // 上下最大偏移的位置(注意:效果是正负翻倍的) 20 | _rect = target->geometry(); 21 | running = false; 22 | 23 | // 更新目标点的时钟:1.5秒钟左右 24 | update_timer = new QTimer(target); 25 | update_timer->setInterval(1000); 26 | connect(update_timer, SIGNAL(timeout()), this, SLOT(slotUpdateAims())); // 每隔一段时间更新一下位置 27 | 28 | // 移动波浪的时钟:40毫秒 29 | move_timer = new QTimer(this); 30 | move_timer->setInterval(getRandom(35, 45)); 31 | connect(move_timer, SIGNAL(timeout()), this, SLOT(slotMovePoints())); 32 | 33 | // y偏移波浪的时钟:4秒左右 34 | offset_timer = new QTimer(this); 35 | offset_timer->setInterval(4000); 36 | connect(offset_timer, SIGNAL(timeout()), this, SLOT(slotSetOffset())); 37 | 38 | // 慢慢暂停的时钟 39 | pause_timer = new QTimer(this); 40 | pause_timer->setInterval(3000); 41 | connect(pause_timer, &QTimer::timeout, [=]{ 42 | if (!running) 43 | { 44 | update_timer->stop(); 45 | move_timer->stop(); 46 | offset_timer->stop(); 47 | 48 | for(int i = 0; i < keys.length(); i++) 49 | keys[i].setY(target->geometry().height()); 50 | offsety = -_max_offsety/2; // 清空偏移(消失时默认都在最下面的,现在恢复) 51 | } 52 | }); 53 | } 54 | 55 | void BezierWaveBean::start() 56 | { 57 | // 初始化一个值 58 | aim_keys.clear(); 59 | keys.clear(); 60 | speedys.clear(); 61 | running = true; 62 | for (int i = 0; i < count; i++) 63 | aim_keys.append(QPoint(inter*(i-2), getRandomHeight())); 64 | for (int i = 0; i < aim_keys.length(); i++) 65 | keys.append(QPoint(aim_keys.at(i).x(), target->geometry().height())); 66 | for (int i = 0; i < aim_keys.length(); i++) 67 | speedys.append(- getRandom(appear_speedy * speedy_step / 2, appear_speedy * speedy_step)); 68 | qint64 timestamp = getTimestamp(); 69 | for (int i = 0; i < aim_keys.length(); i++) 70 | aim_updates_timestamp.append(timestamp); 71 | 72 | update_timer->start(); 73 | move_timer->start(); 74 | offset_timer->start(); 75 | 76 | } 77 | 78 | void BezierWaveBean::resume() 79 | { 80 | if (keys.length() == 0) 81 | return start(); 82 | if (running) return ; // 正在动画中,没必要 83 | update_timer->start(); 84 | move_timer->start(); 85 | offset_timer->start(); 86 | pause_timer->stop(); 87 | running = true; 88 | slotUpdateAims(); // 立即更新目标 89 | for (int i = 0; i < speedys.length(); i++) 90 | speedys[i] = appear_speedy * speedy_step; 91 | } 92 | 93 | void BezierWaveBean::pause() 94 | { 95 | running = false; 96 | for (int i = 0; i < aim_keys.length(); i++) 97 | aim_keys[i].setY(getRandomHeight()); 98 | pause_timer->start(); 99 | } 100 | 101 | void BezierWaveBean::set_count(int x) 102 | { 103 | if (x > 5) 104 | count = x; 105 | } 106 | 107 | void BezierWaveBean::set_offsety(int x) 108 | { 109 | _offsety = x; 110 | } 111 | 112 | void BezierWaveBean::set_speedx(int x) 113 | { 114 | speedx = x; 115 | } 116 | 117 | void BezierWaveBean::set_rect(QRect rect) 118 | { 119 | int delta = rect.height() - _rect.height(); 120 | inter = rect.width() / (count - 4); 121 | for (int i = 0; i < keys.length(); i++) 122 | { 123 | // 修改所有点的间隔 124 | //keys[i].setX(keys.at(i).x() * width / _rect.width()); // 按比例缩放…… 125 | keys[i].setX(inter*(i-2)+offsetx); 126 | aim_keys[i].setX(keys.at(i).x()); 127 | // 修改整体的高度 128 | keys[i].setY(keys.at(i).y()+delta); 129 | aim_keys[i].setY(aim_keys.at(i).y()+delta); 130 | } 131 | double time = pow(rect.width(), 1.0 / 5); // 代表一个点显示几秒钟 132 | speedx = rect.width() / (1000 / move_timer->interval()) / time; 133 | _rect = rect; 134 | } 135 | 136 | QPainterPath BezierWaveBean::getPainterPath(QPainter& painter) 137 | { 138 | Q_UNUSED(painter) 139 | 140 | // 从关键点生成绘图点 141 | QListpots; 142 | for (int i = 0; i < keys.length(); i++) 143 | { 144 | if (i & 1) // 奇数 145 | { 146 | int x = (keys.at(i-1).x()+keys.at(i).x())/2; 147 | int y = (keys.at(i-1).y()+keys.at(i).y())/2; 148 | pots.append(QPoint(x, y)); 149 | pots.append(keys.at(i)); 150 | } 151 | else // 偶数 152 | pots.append(keys.at(i)); 153 | } 154 | 155 | // y方向的偏移 156 | for (int i = 0; i < pots.length(); i++) 157 | { 158 | pots[i].setY(pots[i].y()+offsety); 159 | } 160 | 161 | // 画进行判断的点 162 | /*for (int i = 0; i < pots.length(); i++) 163 | { 164 | QPoint p = pots.at(i); 165 | painter.drawEllipse(QRect(p.x()-2, p.y()-2, 4, 4)); 166 | }*/ 167 | 168 | // 开始画图 169 | QPainterPath bezier; 170 | bezier.moveTo(0, target->geometry().height()); 171 | bezier.lineTo(pots.at(1)); 172 | //painter.drawLine(pots.at(0), pots.at(1)); 173 | for (int i = 2; i+2 < pots.count(); i+=3) 174 | { 175 | // 画切线线条 176 | //painter.drawLine(pots.at(i-1), pots.at(i)); 177 | //painter.drawLine(pots.at(i+1), pots.at(i+2)); 178 | 179 | // 画三阶贝塞尔曲线 180 | bezier.cubicTo(pots.at(i), pots.at(i+1), pots.at(i+2)); 181 | } 182 | bezier.lineTo(target->geometry().bottomRight()); 183 | 184 | return bezier; 185 | } 186 | 187 | 188 | int BezierWaveBean::getRandomHeight() 189 | { 190 | if (running) 191 | { 192 | int y = getRandom(target->geometry().height() / 2, target->geometry().height() * 7 / 8); 193 | return y; 194 | } 195 | else 196 | return int(target->geometry().height()*1.5); 197 | } 198 | 199 | int BezierWaveBean::getRandom(int min, int max) 200 | { 201 | #if defined (Q_OS_WIN) || defined(Q_OS_LINUX) 202 | return mt() % (max-min+1) + min; 203 | #elif defined(Q_OS_MAC) 204 | 205 | #else 206 | return rand() % (max-min+1) + min; 207 | #endif 208 | } 209 | 210 | qint64 BezierWaveBean::getTimestamp() 211 | { 212 | return QDateTime::currentDateTime().toMSecsSinceEpoch(); 213 | } 214 | 215 | void BezierWaveBean::slotUpdateAims() 216 | { 217 | if (!running) return ; 218 | /*for (int i = 0; i < speedys.length(); i++) 219 | if (speedys.at(i) == appear_speedy*speedy_step) 220 | speedys[i] = 1;*/ 221 | // 生成随机的目标关键点 222 | int delta = target->geometry().height()/4; 223 | qint64 timestamp = getTimestamp(); 224 | for (int i = 0; i < aim_keys.length(); i++) 225 | { 226 | if (aim_updates_timestamp.at(i) + 3000 > timestamp) // 3秒更新一次 227 | continue; 228 | int y = aim_keys.at(i).y() + getRandom(-delta, delta); 229 | if (y < target->geometry().height() / 2) 230 | y = target->geometry().height() / 2; 231 | else if (y > target->geometry().height()*7/8) 232 | y = target->geometry().height()*7/8; 233 | aim_keys[i].setY(y + _offsety); 234 | aim_updates_timestamp[i] = timestamp; 235 | } 236 | } 237 | 238 | void BezierWaveBean::slotMovePoints() 239 | { 240 | // Y方向慢慢移动当前关键点到目标关键点 241 | for (int i = 0; i < keys.length(); i++) 242 | { 243 | QPoint& cur = keys[i]; 244 | QPoint aim = aim_keys[i]; 245 | int del = aim.y()-cur.y(); 246 | if (del > 0) // 应当往下移动 247 | { 248 | speedys[i]++; // 向下的加速度 249 | // speedys[i] += sqrt(del)/speedx/speedy_step+1; 250 | /*if (speedys.at(i)/speedy_step + abs(del)/100 < del) 251 | cur.setY(cur.y()+speedys.at(i)/speedy_step+del/100); 252 | else 253 | cur.setY(cur.y()+del);*/ 254 | } 255 | else if (del < 0) // 应当往上移动 256 | { 257 | speedys[i]--; // 向上的加速度/ 258 | // speedys[i] -= sqrt(-del)/speedx/speedy_step+1; 259 | /*if (speedys.at(i)/speedy_step+abs(del)/50 < -del) 260 | cur.setY(cur.y()-speedys.at(i)/speedy_step+del/50); 261 | else 262 | cur.setY(cur.y()+del);*/ 263 | } 264 | cur.setY(cur.y()+speedys.at(i)/speedy_step); 265 | // qDebug() << sqrt(abs(del))/speedx/speedy_step+1; 266 | } 267 | // qDebug() << aim_keys[3].y() << keys[3].y() << speedys[3]; 268 | 269 | // 每次绘图,更新一次Y方向的整体偏移量 270 | if (offsety+offsety_direct > -_max_offsety && offsety+offsety_direct < _max_offsety) 271 | offsety += offsety_direct; 272 | else if (offsety <= -_max_offsety) 273 | offsety = -_max_offsety; 274 | else if (offsety >= _max_offsety) 275 | offsety = _max_offsety; 276 | 277 | // x轴方向的偏移 278 | for (int i = 0; i < keys.length(); i++) 279 | { 280 | keys[i].setX(keys[i].x()+speedx); 281 | aim_keys[i].setX(keys[i].x()); 282 | } 283 | offsetx += speedx; 284 | 285 | // 判断需不需要把右边移动到左边去 286 | if (offsetx >= inter) 287 | { 288 | qint64 timestamp = getTimestamp(); 289 | QPoint p1(keys[0].x()-inter*2, getRandomHeight()); 290 | QPoint p2(keys[0].x()-inter, getRandomHeight()); 291 | keys.insert(0, p2); 292 | keys.insert(0, p1); 293 | aim_keys.insert(0, QPoint(p2)); 294 | aim_keys.insert(0, QPoint(p1)); 295 | speedys.insert(0, speedys.at(speedys.length()-1)); 296 | speedys.insert(0, speedys.at(speedys.length()-2)); 297 | aim_updates_timestamp.insert(0, timestamp); 298 | aim_updates_timestamp.insert(0, timestamp); 299 | 300 | offsetx -= inter*2; 301 | 302 | if (keys.length() > count+2) 303 | { 304 | keys.removeLast(); 305 | keys.removeLast(); 306 | aim_keys.removeLast(); 307 | aim_keys.removeLast(); 308 | speedys.removeLast(); 309 | speedys.removeLast(); 310 | aim_updates_timestamp.removeLast(); 311 | aim_updates_timestamp.removeLast(); 312 | } 313 | } 314 | 315 | // 更新当前界面 316 | target->update(); 317 | 318 | /*QString ans = ""; 319 | for (int i = 5; i < 8; i++) 320 | ans += " (" + QString::number(keys.at(i).y()) + ","+QString::number(keys.at(i).y())+")"+QString::number(speedys.at(i)); 321 | qDebug() << ans;*/ 322 | } 323 | 324 | void BezierWaveBean::slotSetOffset() 325 | { 326 | if (!running) 327 | { 328 | offsety_direct = 1; 329 | return ; 330 | } 331 | // 每隔一段时间,稍微修改波浪的上下偏移位置 332 | if (getRandom(0,1)) 333 | offsety_direct = 1; 334 | else 335 | offsety_direct = -1; 336 | } 337 | -------------------------------------------------------------------------------- /bezierwavebean.h: -------------------------------------------------------------------------------- 1 | #ifndef BEZIERWAVEBEAN_H 2 | #define BEZIERWAVEBEAN_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class BezierWaveBean : public QObject 15 | { 16 | Q_OBJECT 17 | public: 18 | BezierWaveBean(QWidget *parent); 19 | 20 | void start(); 21 | void resume(); 22 | void pause(); 23 | 24 | void set_count(int x); 25 | void set_offsety(int x); 26 | void set_speedx(int x); 27 | void set_rect(QRect rect); 28 | 29 | QPainterPath getPainterPath(QPainter &painter); 30 | 31 | protected: 32 | int getRandomHeight(); 33 | int getRandom(int min, int max); 34 | qint64 getTimestamp(); 35 | 36 | signals: 37 | 38 | public slots: 39 | void slotUpdateAims(); 40 | void slotMovePoints(); 41 | void slotSetOffset(); 42 | 43 | protected: 44 | QWidget* target; 45 | int _offsety; // 多层波浪y轴偏移 46 | int _max_offsety; // y轴上下偏移的最大值 47 | QRect _rect; 48 | 49 | QTimer* update_timer; 50 | QTimer* move_timer; 51 | QTimer* offset_timer; 52 | QTimer* pause_timer; 53 | 54 | bool running; 55 | 56 | int count; 57 | int inter; 58 | int appear_speedy; 59 | 60 | int speedy, offsety; 61 | int offsety_direct; 62 | int speedx, offsetx; 63 | QListspeedys; 64 | int speedy_step; 65 | QListaim_updates_timestamp; 66 | 67 | QListkeys; 68 | QListaim_keys; 69 | 70 | std::random_device rd; 71 | std::mt19937 mt; 72 | }; 73 | 74 | #endif // BEZIERWAVEBEAN_H 75 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | MainWindow::MainWindow(QWidget *parent) : 5 | QMainWindow(parent), ui(new Ui::MainWindow), 6 | bw1(nullptr), bw2(nullptr), bw3(nullptr), bw4(nullptr), mt(rd()) 7 | { 8 | ui->setupUi(this); 9 | 10 | bw1 = new BezierWaveBean(this); 11 | // bw1->set_offsety(geometry().height()/20); 12 | bw1->set_speedx(getRandom(1,5)); 13 | bw1->start(); 14 | 15 | bw2 = new BezierWaveBean(this); 16 | // bw2->set_offsety(geometry().height()/10); 17 | bw2->set_speedx(getRandom(1,5)); 18 | bw2->start(); 19 | 20 | bw3 = new BezierWaveBean(this); 21 | // bw3->set_offsety(geometry().height()*3/20); 22 | bw3->set_speedx(getRandom(1,5)); 23 | bw3->start(); 24 | 25 | bw4 = new BezierWaveBean(this); 26 | // bw4->set_offsety(geometry().height()/5); 27 | bw4->set_speedx(getRandom(1,5)); 28 | bw4->start(); 29 | } 30 | 31 | MainWindow::~MainWindow() 32 | { 33 | delete ui; 34 | } 35 | 36 | void MainWindow::paintEvent(QPaintEvent *e) 37 | { 38 | QPainter painter(this); 39 | painter.setRenderHint(QPainter::Antialiasing, true); 40 | 41 | QPainterPath bezier; 42 | 43 | if (bw1 != nullptr) 44 | bezier = bw1->getPainterPath(painter); 45 | painter.fillPath(bezier, QColor(255, 0, 0, 50)); 46 | 47 | if (bw2 != nullptr) 48 | bezier = bw2->getPainterPath(painter); 49 | painter.fillPath(bezier, QColor(255, 0, 0, 50)); 50 | 51 | if (bw3 != nullptr) 52 | bezier = bw3->getPainterPath(painter); 53 | painter.fillPath(bezier, QColor(255, 0, 0, 50)); 54 | 55 | if (bw4 != nullptr) 56 | bezier = bw4->getPainterPath(painter); 57 | painter.fillPath(bezier, QColor(255, 0, 0, 50)); 58 | 59 | return QMainWindow::paintEvent(e); 60 | } 61 | 62 | void MainWindow::resizeEvent(QResizeEvent *e) 63 | { 64 | bw1->set_rect(geometry()); 65 | bw2->set_rect(geometry()); 66 | bw3->set_rect(geometry()); 67 | bw4->set_rect(geometry()); 68 | return QMainWindow::resizeEvent(e); 69 | } 70 | 71 | int MainWindow::getRandom(int min, int max) 72 | { 73 | #if defined (Q_OS_WIN) || defined(Q_OS_LINUX) 74 | return mt() % (max-min+1) + min; 75 | #elif defined(Q_OS_MAC) 76 | 77 | #else 78 | return rand() % (max-min+1) + min; 79 | #endif 80 | } 81 | 82 | void MainWindow::on_pushButton_clicked() 83 | { 84 | bw1->pause(); 85 | bw2->pause(); 86 | bw3->pause(); 87 | bw4->pause(); 88 | } 89 | 90 | void MainWindow::on_pushButton_2_clicked() 91 | { 92 | bw1->resume(); 93 | bw2->resume(); 94 | bw3->resume(); 95 | bw4->resume(); 96 | } 97 | -------------------------------------------------------------------------------- /mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "bezierwavebean.h" 6 | 7 | namespace Ui { 8 | class MainWindow; 9 | } 10 | 11 | class MainWindow : public QMainWindow 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit MainWindow(QWidget *parent = nullptr); 17 | ~MainWindow(); 18 | 19 | protected: 20 | void paintEvent(QPaintEvent* e) override; 21 | void resizeEvent(QResizeEvent* e) override; 22 | 23 | int getRandom(int min, int max); 24 | 25 | private: 26 | 27 | 28 | public slots: 29 | 30 | 31 | private slots: 32 | void on_pushButton_clicked(); 33 | 34 | void on_pushButton_2_clicked(); 35 | 36 | private: 37 | Ui::MainWindow *ui; 38 | 39 | BezierWaveBean *bw1, *bw2, *bw3, *bw4; 40 | 41 | std::random_device rd; 42 | std::mt19937 mt; 43 | }; 44 | 45 | #endif // MAINWINDOW_H 46 | -------------------------------------------------------------------------------- /mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 40 21 | 10 22 | 75 23 | 23 24 | 25 | 26 | 27 | 暂停 28 | 29 | 30 | 31 | 32 | 33 | 130 34 | 10 35 | 75 36 | 23 37 | 38 | 39 | 40 | 继续 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /pictures/picture.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwxyi/Bezier-Wave/0d1deb85dec839e09a7723d3535b862ec8badffe/pictures/picture.gif --------------------------------------------------------------------------------