在QT Quick中,Canvas就像一块可自由创作的数字画布,提供类似HTML5 Canvas的2D绘图能力,能轻松实现动态图形、自定义界面组件。本章从基础到进阶,带你掌握Canvas核心用法,为打造高性能个性化界面铺路。
学习目标:熟练运用Canvas核心API,独立绘制复杂图形并实现简单图像效果。
Canvas是QML中专门用于2D绘图的元素,核心是通过「绘图上下文(context)」执行绘图指令——相当于你拿画笔在画布上创作,上下文就是你的画笔与调色盘。
Canvas核心属性与方法:
width/height:定义画布尺寸,决定绘图范围;contextType:指定上下文类型,常用值为"2d",对应2D绘图能力;getContext(contextType):获取绘图上下文对象,所有绘图操作均通过它完成;requestPaint():请求重绘画布,内容更新时调用;onPaint信号:绘图触发点,所有绘图代码必须写在该信号处理器内,确保上下文状态正确。提示:切勿在onPaint外执行绘图操作,否则会出现绘制异常。
基本示例:绘制带边框的矩形
import QtQuick 2.15
Canvas {
id: myCanvas
width: 400
height: 300
onPaint: {
// 获取2D绘图上下文(拿起画笔)
var ctx = getContext("2d");
// 设置填充颜色为蓝色
ctx.fillStyle = "#4A90E2";
// 在(50,50)位置绘制200×150的填充矩形
ctx.fillRect(50, 50, 200, 150);
// 设置描边颜色为深灰,线条宽度3像素
ctx.strokeStyle = "#333";
ctx.lineWidth = 3;
// 给矩形添加边框
ctx.strokeRect(50, 50, 200, 150);
}
Component.onCompleted: {
// 组件加载完成后,请求首次绘制
requestPaint();
}
}
该示例会在画布左上角(50,50)位置,画出一个蓝色填充、深灰边框的200×150像素矩形。
Canvas的2D上下文提供了一套完整的绘图工具,可绘制形状、文本、图像,还能管理绘图状态。核心方法分为五大类:
fillRect()(填充矩形)、strokeRect()(描边矩形)、clearRect()(清空指定区域);beginPath()(开启新路径)、moveTo()(移动画笔位置)、lineTo()(画直线)、arc()(画圆弧)、closePath()(闭合路径)、fill()(填充路径)、stroke()(描边路径);fillText()(填充文本)、strokeText()(描边文本);drawImage()(绘制图像);save()(保存当前绘图状态,如颜色、变换)、restore()(恢复之前保存的状态),避免不同绘图操作互相干扰。路径绘制示例:绘制笑脸
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height); // 清空整个画布
// 绘制脸部:开启新路径,画圆形
ctx.beginPath();
// 在(200,150)位置画半径80的整圆(0到2π)
ctx.arc(200, 150, 80, 0, Math.PI * 2, false);
ctx.fillStyle = "#F8E71C"; // 填充黄色
ctx.fill();
ctx.strokeStyle = "#333"; // 描边深灰
ctx.lineWidth = 2;
ctx.stroke();
// 绘制左眼:开启新路径,画小圆形
ctx.beginPath();
ctx.arc(170, 120, 10, 0, Math.PI * 2, false);
ctx.fillStyle = "#333";
ctx.fill();
// 绘制右眼
ctx.beginPath();
ctx.arc(230, 120, 10, 0, Math.PI * 2, false);
ctx.fill();
// 绘制微笑嘴巴:用半圆描边
ctx.beginPath();
// 画从0到π的半圆,开口向上形成微笑
ctx.arc(200, 160, 40, 0, Math.PI, false);
ctx.strokeStyle = "#333";
ctx.lineWidth = 4;
ctx.stroke();
}
每绘制一个独立图形都需用beginPath()开启新路径,避免线条互相干扰,这是路径绘制的关键技巧。
路径是绘制复杂自定义图形的核心,它像画笔在画布上勾勒的轨迹,通过组合直线、曲线(如贝塞尔曲线)和弧线,可画出任意形状——从心形到复杂图标都能实现。
路径绘制基本步骤:
beginPath()开启新路径;moveTo()移动画笔到起始位置,再用lineTo()、bezierCurveTo()(三次贝塞尔曲线)等方法勾勒轨迹;closePath(),自动连接起点与终点形成闭合图形;fill()填充图形,或stroke()给图形描边。贝塞尔曲线绘制心形示例
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
// 从中心出发,用三次贝塞尔曲线勾勒心形左右两半
ctx.moveTo(200, 150);
ctx.bezierCurveTo(200, 130, 150, 100, 150, 130);
ctx.bezierCurveTo(150, 170, 200, 190, 200, 210);
ctx.bezierCurveTo(200, 190, 250, 170, 250, 130);
ctx.bezierCurveTo(250, 100, 200, 130, 200, 150);
ctx.closePath();
// 创建径向渐变填充:从浅粉到深粉
var gradient = ctx.createRadialGradient(200, 150, 10, 200, 150, 70);
gradient.addColorStop(0, "#FF9E9E"); // 中心浅粉
gradient.addColorStop(1, "#D81B60"); // 边缘深粉
ctx.fillStyle = gradient;
ctx.fill();
ctx.strokeStyle = "#880E4F"; // 描边深紫红色
ctx.lineWidth = 3;
ctx.stroke();
}
三次贝塞尔曲线通过三个控制点调整形状,灵活度极高,是绘制复杂曲线的首选工具。
渐变能让颜色过渡更平滑,Canvas支持两种渐变类型:
createLinearGradient(x0, y0, x1, y1)创建,参数为渐变起点(x0,y0)与终点(x1,y1);createRadialGradient(x0, y0, r0, x1, y1, r1)创建,参数为起始圆的中心、半径,以及结束圆的中心、半径。创建渐变后,需用addColorStop(位置, 颜色)添加颜色节点,位置为0-1之间的数值,0代表起点,1代表终点。
线性渐变示例:绘制渐变背景与文字
onPaint: {
var ctx = getContext("2d");
// 创建从左上角到右下角的线性渐变
var linGrad = ctx.createLinearGradient(0, 0, width, height);
linGrad.addColorStop(0, "#4A90E2"); // 起始色:蓝色
linGrad.addColorStop(0.5, "#7ED321"); // 中间色:绿色
linGrad.addColorStop(1, "#F8E71C"); // 结束色:黄色
// 用渐变填充整个画布作为背景
ctx.fillStyle = linGrad;
ctx.fillRect(0, 0, width, height);
// 在渐变背景上绘制带描边的白色文字
ctx.font = "bold 36px Arial"; // 设置字体样式
ctx.fillStyle = "white"; // 填充色白色
ctx.strokeStyle = "#333"; // 描边色深灰
ctx.lineWidth = 2;
ctx.fillText("QT Quick Canvas", 60, 180); // 填充文字
ctx.strokeText("QT Quick Canvas", 60, 180); // 描边文字,提升辨识度
}
该示例会生成蓝-绿-黄的渐变背景,叠加带深灰描边的白色文字,视觉效果醒目。
Canvas不仅能绘制图形,还可加载、绘制图像,甚至进行像素级操作实现滤镜效果。
图像绘制与裁剪示例
Canvas {
id: canvasWithImage
width: 400; height: 300
// 用隐藏的Image元素预加载图片,避免绘制卡顿
Image {
id: sourceImage
source: "qrc:/images/landscape.jpg"
visible: false
// 图片加载完成后请求绘制
onStatusChanged: if (status == Image.Ready) canvasWithImage.requestPaint();
}
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);
if (sourceImage.status == Image.Ready) {
// 1. 绘制完整图像,放在(10,10)位置,大小180×120
ctx.drawImage(sourceImage, 10, 10, 180, 120);
// 2. 裁剪图像部分区域绘制:从源图像(50,50)裁剪100×100区域,
// 放到画布(200,10)位置,放大为180×180
ctx.drawImage(sourceImage,
50, 50, 100, 100, // 源图像裁剪区域
200, 10, 180, 180); // 画布目标区域
// 3. 添加灰色滤镜:修改像素值实现
var imageData = ctx.getImageData(10, 10, 180, 120);
var data = imageData.data; // 像素数组,每个像素含R、G、B、Alpha四位
for (var i = 0; i < data.length; i += 4) {
// 计算RGB平均值,赋值给R、G、B,实现灰色效果
var avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // 红色通道
data[i + 1] = avg; // 绿色通道
data[i + 2] = avg; // 蓝色通道
// Alpha通道保持不变,不影响透明度
}
// 将修改后的像素放回画布(10,150)位置
ctx.putImageData(imageData, 10, 150);
}
}
}
性能提示:频繁调用getImageData()和putImageData()会消耗较多性能,复杂图像处理建议放到后台线程(如WorkerScript)执行,避免界面卡顿。
Canvas是QT Quick实现动态2D绘图的核心工具,用法与HTML5 Canvas高度相似,易上手、功能强:
掌握Canvas后,你将摆脱标准控件限制,打造独一无二的动态视觉效果与自定义图表组件,让QT Quick应用更具特色!