第九章:画布
第九章:画布
QtQt QuickQML https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations有几行代码需要修改:
- Qt Quick要求定义变量,所以要添加 var 声明:
for (var i=0;i<3;i++) {
...
}
- 绘画方法要接收2D上下文对象
function draw(ctx) {
...
}
- 由于尺寸不同,我们需要调整每个螺旋
ctx.translate(20+j*50,20+i*50);
最后,完成onPaint
函数,在函数里获取上下文并调用绘图函数。
onPaint: {
var ctx = getContext("2d");
draw(ctx);
}
结果是使用 QML 画布运行的移植螺旋图图形。
如你所见,在不改变实际逻辑,仅改动相对较少的代码格式,就可以完成从HTML5到QML的移植。
pretty glowing lines有着非常棒的特性,这让移植更具挑战性。
HTML>
<html lang="en">
<head>
<title>Pretty Glowing Linestitle>
head>
<body>
<canvas width="800" height="450">canvas>
<script>
var context = document.getElementsByTagName('canvas')[0].getContext('2d');
// initial start position
var lastX = context.canvas.width * Math.random();
var lastY = context.canvas.height * Math.random();
var hue = 0;
// closure function to draw
// a random bezier curve with random color with a glow effect
function line() {
context.save();
// scale with factor 0.9 around the center of canvas
context.translate(context.canvas.width/2, context.canvas.height/2);
context.scale(0.9, 0.9);
context.translate(-context.canvas.width/2, -context.canvas.height/2);
context.beginPath();
context.lineWidth = 5 + Math.random() * 10;
// our start position
context.moveTo(lastX, lastY);
// our new end position
lastX = context.canvas.width * Math.random();
lastY = context.canvas.height * Math.random();
// random bezier curve, which ends on lastX, lastY
context.bezierCurveTo(context.canvas.width * Math.random(),
context.canvas.height * Math.random(),
context.canvas.width * Math.random(),
context.canvas.height * Math.random(),
lastX, lastY);
// glow effect
hue = hue + 10 * Math.random();
context.strokeStyle = 'hsl(' + hue + ', 50%, 50%)';
context.shadowColor = 'white';
context.shadowBlur = 10;
// stroke the curve
context.stroke();
context.restore();
}
// call line function every 50msecs
setInterval(line, 50);
function blank() {
// makes the background 10% darker on each call
context.fillStyle = 'rgba(0,0,0,0.1)';
context.fillRect(0, 0, context.canvas.width, context.canvas.height);
}
// call blank function every 50msecs
setInterval(blank, 40);
script>
body>
html>
在HTML5里,上下文2D对象可以在画布上随时随处绘制。在QML里,只能在onPaint
的处理函数处理。HTML5里使用setInterval
定时器触发画线和清空背景。由于QML的不同处理方式,不能仅调用这些函数,因为需要通过onPaint
函数来处理。而颜色展现也需要调整。一起逐个修改吧。
一切从画布元素开始。简单起见,直接使用Canvas
元素做为QML文件的根元素。
import QtQuick
Canvas {
id: canvas
width: 800; height: 450
...
}
为了解耦setInterval
对函数的直接调用,我们将对setInterval
的调用替换为两个会请求重绘的定时器。Timer
定时器定时触发并执行我们设定的代码。因为我们无法区分哪个绘制函数将被触发,我们为每个操作定义一个请求操作的布尔型的标记,然后触发一个重绘请求。
这是画线操作的代码。清空背景的操作是类似的。
...
property bool requestLine: false
Timer {
id: lineTimer
interval: 40
repeat: true
triggeredOnStart: true
onTriggered: {
canvas.requestLine = true
canvas.requestPaint()
}
}
Component.onCompleted: {
lineTimer.start()
}
...
现在我们就知道了哪此操作(画线、清空或二者都)需要在onPaint
函数中进行。当我们为每个绘制请求进入onPaint
时,我们需要将变量的初始化放在画布元素中。
Canvas {
...
property real hue: 0
property real lastX: width * Math.random();
property real lastY: height * Math.random();
...
}
现在,我们的绘制函数应该象这样:
onPaint: {
var context = getContext('2d')
if(requestLine) {
line(context)
requestLine = false
}
if(requestBlank) {
blank(context)
requestBlank = false
}
}
line函数被抽象出来,将画布作为其参数。
function line(context) {
context.save();
context.translate(canvas.width/2, canvas.height/2);
context.scale(0.9, 0.9);
context.translate(-canvas.width/2, -canvas.height/2);
context.beginPath();
context.lineWidth = 5 + Math.random() * 10;
context.moveTo(lastX, lastY);
lastX = canvas.width * Math.random();
lastY = canvas.height * Math.random();
context.bezierCurveTo(canvas.width * Math.random(),
canvas.height * Math.random(),
canvas.width * Math.random(),
canvas.height * Math.random(),
lastX, lastY);
hue += Math.random()*0.1
if(hue > 1.0) {
hue -= 1
}
context.strokeStyle = Qt.hsla(hue, 0.5, 0.5, 1.0);
// context.shadowColor = 'white';
// context.shadowBlur = 10;
context.stroke();
context.restore();
}
最大的改动是QML函数Qt.rgba()
和Qt.hsla()
的使用,其要求采用QML中使用的0.0...0.1范围内的值。
同样应用到清屏函数blank
function blank(context) {
context.fillStyle = Qt.rgba(0,0,0,0.1)
context.fillRect(0, 0, canvas.width, canvas.height);
}
最后的效果类似下图: