若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/78542856
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
目录
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)
Qt开发专栏:qml开发(点击传送门)
qml开发笔记(五): 可视化元素BorderImage、AnimatedImage、AnimatedSprite、SpriteSequence
前话
上一章节介绍了可视化元素Rectangle和Image。本章节将继续学习可视化元素BorderImage、AnimatedImage、AnimatedSprite和SpriteSequence。
BorderImage是单个图像分区域缩放;
AnimatedImage是播放gif;
AnimatedSprite是精灵动画;
SpriteSequence本人认为非常有必要认真阅读,实现了游戏当中的走路,代码很简单,原理是sprites精灵动画。
关于Sprites精灵动画
qml的sprites可参照css的sprites,我们可以把一个精灵当作一个动画。
CSSSprites在国内很多人叫CSS精灵,其实这个技术不新鲜,原理就是:靠不断的切换图片让人感觉视觉上不断在变化,例如gif动画之类的效果,前端实现精灵效果原理有两种方式:
第一种:传统的就是靠定时器不断去改变一个元素的background-image属性了,简单的来说就是靠不断的替换图片,但是值得注意的问题就是图片如果很多,加载会比较慢,会占用大量网络资源。
第二种:大多数的做法就是把图片都合成一张大图再利用CSS的属性(background-image、background-repeat、background-position)组合进行背景定位,background-position可以用数字精确的定位出背景图片的位置。
BorderImage
描述
本元素用于创建有边框分割的图像,图像可缩放和平铺其中的每个部分。一个本元素可将图片分成9个区域,如下图:
1. 角区域1/3/7/9是不会缩放的;
2. 区域2和8缩放是依赖horizontalTileMode;
3. 区域4和6缩放是依赖verticalTileMode;
4. 区域5缩放是依赖horizontalTileMode和verticalTileMode;
属性
- asynchronous : bool [指定本地文件系统上的图像应在单独的线程中异步加载。默认值为false,导致用户界面线程在加载图像时阻塞。将异步设置为true对保持响应性用户界面比立即可见图像更可取] (注意:此属性只对从本地文件系统读取的图像有效。通过网络资源(例如HTTP)加载的图像总是异步加载。)
- border [该边界是将borderImage分割城9个区域的边界]
Rectangle { Image { source: "3.png"; } BorderImage { x:150; source: "3.png"; width: 200; height: 200; border.left: 35; border.top: 35; border.right: 35; border.bottom: 35; } BorderImage { x:400; source: "3.png"; width: 200; height: 200; border.left: 45; border.top: 45; border.right: 45; border.bottom: 45; } BorderImage { x:650; source: "3.png"; width: 200; height: 200; horizontalTileMode: BorderImage.Repeat border.left: 35; border.top: 35; border.right: 35; border.bottom: 35; } BorderImage { x:900; source: "3.png"; width: 200; height: 200; horizontalTileMode: BorderImage.Round border.left: 35; border.top: 35; border.right: 35; border.bottom: 35; } }
- border.left : int [参照上面]
- border.right : int [参照上面]
- border.top : int [参照上面]
- border.bottom : int [参照上面]
- cache : bool [指定是否应缓存图像。默认值为true。在处理大型图像时,将缓存设置为false是很有用的,以确保它们不会以牺牲小UI元素的图像为代价进行缓存]
- horizontalTileMode : enumeration [如何重复或拉伸边界图像的中间部分,缺省为Stretch]
BorderImage.Stretch - Scales the image to fit to the available area. BorderImage.Repeat - Tile the image until there is no more space. May crop the last image. BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped.
- mirror : bool [rotation为0度时,以纵轴为轴心,做镜像,缺省false]
- progress : real [这个属性保存图像的加载进度,从0(无负载)到1(完成)]
- smooth : bool [此属性保存缩放或转换时图像是否平滑地过滤。平滑过滤提供了更好的视觉质量,但在某些硬件上可能会慢一些。如果图像以自然大小(本身大小)显示,则此属性没有视觉效果或性能效果。默认情况下,此属性设置为true]
- source : url [映像可以处理Qt支持的任何图像格式,它由Qt支持的任何URL方案加载。URL可能是绝对的,或者与组件的URL相对]
- sourceSize : QSize[此属性包含加载图像的实际宽度和高度。与宽度和高度缩放的属性不同,此属性设置存储的图像的实际像素数,以便大图像不使用比必要的更多内存,参照Image元素的例子,保证图像在内存不大于1024x1024像素]
- status : enumeration [这个属性保存图像的加载状态]
Image.Null - no image has been set Image.Ready - the image has been loaded Image.Loading - the image is currently being loaded Image.Error - an error occurred while loading the image
- verticalTileMode: enumeration[参照horizontalTileMode,缺省为Stretch]
AnimatedImage
描述
Animatedimage提供一种方式来播放存储包含一系列的帧的图像动画,如存储图像的gif文件。
Rectangle { width: animation.width; height: animation.height + 8; color: "black"; AnimatedImage { id: animation; source: "4.gif"; } Rectangle { // 下句报错"depends on non-NOTIFYable properties: // QQuickAnimatedImage::frameCount" // rectangle不显示 // property int frames: animation.frameCount; // 报错语句 width: 4; height: 8 // x: (animation.width - width) * animation.frames / frames; // 报错语句 x: (animation.width - width) * animation.currentFrame / animation.frameCount y: animation.height color: "red" }
属性
- currentFrame : int [当前正在显示的帧的序号]
- frameCount : int [帧的总数]
- paused : bool [暂停,播放成功后暂停会停止播放,paused前提是playing为true,每次playing从false变为true,paused会自动变为false]
- playing : bool [播放中,当成功加载图像后,变为true,用以标志图像加载结果]
- source : url [gif地址]
AnimatedSprite
描述
Animatedsprite提供渲染和提供了在同一个图像文件的多个帧的动画控制。可以以固定的速度播放,以显示的帧速率,或手动推进和控制进度。
属性
- currentFrame : int [当暂停时,可手动设置当前帧或者使用advance()推进一帧]
- frameCount : int [总帧数]
- frameDuration : int [每一帧动画的间隔,当等于或小于0时无效;如果frameRate有效,frameRate则会被用于计算该帧的持续时间;如果frameRate无效,但frameDuration有效,那么将使用frameDuration,注意:更改此参数将重新启动动画]
- frameHeight : int [单帧高度,如果都一样则唯一的,则可忽略]
- frameRate : qreal [每秒显示的动画帧数,当等于或小于0时无效;如果frameRate有效,frameRate则会被用于计算该帧的持续时间;如果frameRate无效,但frameDuration有效,那么将使用frameDuration,注意:更改此参数将重新启动动画]
- frameSync : bool [如果为真,则动画将没有持续时间。每次一帧渲染到屏幕时,动画将前进一帧。这使它与绘画速度同步,而不是经过时间。如果framesync设置为true,它将重写的帧率和frameduration。默认为false。更改此参数将重新启动动画]
- frameWidth : int [单帧宽度,如果都一样则唯一的,则可忽略]
- frameX : int [在animatedsprite第一帧图像文件的x坐标。如果第一帧在文件左上角开始,则可以忽略]
- frameY : int [在animatedsprite第一帧图像文件的y坐标。如果第一帧在文件左上角开始,则可以忽略]
- interpolate : bool [如果为true,则在精灵帧之间会出现插值,使动画看起来更平滑(前一张渐隐后一张渐显)。默认为true]
- loops : int [动画多次播放后,动画会自动停止。负值是无效的。如果设置为AnimatedSprite.Infinite,动画将会无限循环播放,且默认是无限AnimatedSprite.Infinite]
- paused : bool [暂停时,当前帧可以手动更新,默认为false。]
- reverse : bool [是否反向播放,默认为false]
- running : bool [是否正在播放动画,默认为true]
- source : url [动画地址]
方法
- int advance() [前进一帧]
- int pause() [暂停]
- int restart() [重新播放]
- int resume() [暂停后的恢复播放]
示例
Rectangle { visible: true; width: 360; height: 320; color: "white"; Image { id:image source: "./6.png" x:350 } AnimatedSprite { id: animated; width: image.width/3; // 显示窗口宽度 height: image.height/2; // 显示窗口高度 anchors.centerIn: parent; source: "6.png"; frameWidth: image.width/4; frameHeight: image.height/3; frameDuration: 200; frameCount: 16; frameX: 0; frameY: 0; onCurrentFrameChanged: { info.text = "%1/%2".arg(animated.currentFrame).arg(animated.frameCount); } } Row{ spacing: 4; anchors.horizontalCenter: parent.horizontalCenter; anchors.bottom: parent.bottom; anchors.bottomMargin: 4; Text { id: info; width: 60; height: 24; color: "red"; verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignRight; } Button { width: 60; height: 24; text: (animated.paused == true) ? "Play" : "Pause"; onClicked: (animated.paused == true) ? animated.resume() : animated.pause(); } Button { width: 70; height: 24; text: "Advance"; onClicked: animated.advance(); } Button { width: 70; height: 24; text: "Restart"; onClicked: animated.restart(); } Button { width: 60; height: 24; text: "Quit"; onClicked: Qt.quit(); } } }
运行时,Quit无法退出,报出错误:” Signal QQmlEngine::quit()emitted, but no receivers connected to handle it.”。因为本人采用的是在QtGui中加载qml,不是纯qml应用,所以qml需要与QtGui中的信号相关联;在QtGui中加入代码:
void MainWindow::initQmlWidget() { // 初始化quick窗口 _pQuickView = new QQuickView(); _pQuickView->setSource(QUrl("./qml/start.qml")); _pQuickWidget = QWidget::createWindowContainer(_pQuickView, this); _pQuickWidget->hide(); // 用于与qml交互 _pQmlContext = _pQuickView->rootContext(); // 显示qml _pQuickWidget->show(); // 代码补充处: QtGui 与 qml 退出信号关联 QObject::connect(_pQuickView->engine(), SIGNAL(quit()), qApp, SLOT(quit())); }
SpriteSequence
描述
用于作为一系列帧存储的多个动画之间的播放和转换。
属性
- currentSprite : string [只读][当前正在播放的精灵动画名字,只读,只有在暂停时才可以设置]
- goalSprite : string [下一个播放的精灵动画名字,如果有可能从目标状态的起始点返回到目标状态,那么它将继续这样做(不断0->goalSprite循环),直到目标状态被设置为“”或一个不可到达的状态]
- interpolate : bool [切换过渡优化,默认true]
- running : bool [是否正在播放精灵,默认为true]
- sprites : list<Sprite> [精灵播放列表,播放时精灵将会被缩放到这个项目的大小]
方法
- jumpTo(string sprite) [这个函数会导致立即跳转到指定的精灵,中间的精灵不会被播放。精灵的参数是你想要跳转到的精灵的名字]
入坑
捕捉键盘时,若一直按下按钮,会在短暂的停滞后Release然后不断的OnPressed和OnRelease,等同于连续敲击,这点与传统的桌面应用程序不一样。
示例
实现一个动画,按方向键上下左右,可以走动,松下就停止,效果如下图:
Rectangle { visible: true; width: 240; height: 200; color: "black"; Image { x:100; id: image1; source: "./11.png"; } SpriteSequence { id: sequence; width: 100; height: 100; interpolate: true; running: false; sprites: [ Sprite { name: "down"; source: image1.source; frameCount: 4; frameWidth: image1.width/4; frameHeight: image1.height/4; frameRate: 10; }, Sprite { name: "left"; source: image1.source; frameCount: 4; frameY: image1.height/4; frameWidth: image1.width/4; frameHeight: image1.height/4; frameRate: 10; }, Sprite { name: "right"; source: image1.source; frameCount: 4; frameY: image1.height/4*2; frameWidth: image1.width/4; frameHeight: image1.height/4; frameRate: 10; }, Sprite { name: "up"; source: image1.source; frameCount: 4; frameY: image1.height/4*3; frameWidth: image1.width/4; frameHeight: image1.height/4; frameRate: 10; } ] } focus: true; // 不获取焦点是无法获取键盘的 Keys.onPressed: { // 当持续按住up时,将会变成多次连击,不断pressed和release switch(event.key) { case Qt.Key_Up: // false时,跳转到"up",此时无法不跑,再将SpriteSequence.running设置true sequence.jumpTo("up"); sequence.running = true; text.text = "按下方向键上,正在向上走路"; break; case Qt.Key_Down: sequence.jumpTo("down"); sequence.running = true; text.text = "按下方向键下,正在向下走路"; break; case Qt.Key_Left: sequence.jumpTo("left"); sequence.running = true; text.text = "按下方向键左,正在向左走路"; break; case Qt.Key_Right: sequence.jumpTo("right"); sequence.running = true; text.text = "按下方向键右,正在向右走路"; break; default: ; } } Keys.onReleased: { sequence.running = false; text.text = "请按方向键走路:上、下、左、右"; } Text { id: text; y:200; anchors.horizontalCenter: parent.horizontalCenter; text:"请按方向键走路:上、下、左、右"; } }
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/78542856