qml中Canvas使用示例
总是忘记Canvas怎么用,每次用了都得去翻资料,干脆自己写一下,方便下次查。
创建Canvas
在qml里创建一个Canvas,并将画布清空为白色
Canvas{
id: canvas
anchors.fill: parent
onPaint: {
var ctx = getContext("2d")
ctx.fillStyle = "#FFFFFF"
ctx.fillRect(0, 0, width, height) // 填充一个矩形,这里用白色填充整个画布
}
}
画一个矩形框
ctx.beginPath()
ctx.rect(80, 80, 100, 70) // (startX, startY, width, height)
ctx.stroke()
画一个填充矩形
ctx.fillStyle = "#4f4f4f"
ctx.fillRect(80, 80, 100, 70)
画一个圆
ctx.beginPath()
ctx.arc(120, 120, 90, 0, 360) // (centerX, centerY, radius, angle_start, angle_end)
ctx.stroke()
画一个填充圆
ctx.beginPath()
ctx.arc(120, 120, 90, 0, 360)
ctx.fillStyle = "#4f4f4f"
ctx.fill();
ctx.stroke()
画一条直线
ctx.beginPath()
ctx.moveTo(40, 40) // (startX, startY)
ctx.lineTo(120, 90) // (endX, endY)
ctx.stroke()
颜色、粗细
ctx.strokeStyle = "#FF0000" // 边框颜色
ctx.fillStyle = "#FF0000" // 填充颜色
ctx.lineWidth = 5 // 笔头粗细
现在把这些功能集合起来,做一个简单的画板
需要注意的是,因为在qml中使用了enum,所以qml文件名必须以大写开头,否则无法调用enum中的参数。
// Main_t.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Dialogs 1.3
import QtQuick.Controls 1.4
Window {
width: 640
height: 480
visibility: "Maximized"
visible: true
title: qsTr("Hello World")
color: "#444444"
enum Draw_Type {
Type_Rect = 0, // 外框矩形
Type_Rect_Fill, // 填充矩形
Type_Circle, // 外框圆
Type_Circle_Fill, // 填充圆
Type_Line, // 线条
Type_Paint // 画笔随笔画
}
property int currentType: Main_t.Draw_Type.Type_Rect
property point startPoint: Qt.point(0, 0)
property point endPoint: Qt.point(0, 0)
property bool needClearSaved: true
property string borderColor: "#4f4f4f"
property string fillColor: "#4f4f4f"
property int currLineWidth: 1
Item{
width: parent.width - 5
height: parent.height - 5
anchors.centerIn: parent
// 保存画板
Canvas {
id: canvas_save
width: parent.width
anchors.bottom: parent.bottom
anchors.top: menuRec.bottom
anchors.topMargin: 1
onPaint: {
var ctx = getContext("2d");
if (needClearSaved){
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, width, height); // 填充一个矩形,这里用白色填充整个画布
needClearSaved = false;
return;
}
ctx.strokeStyle = borderColor
ctx.fillStyle = fillColor
ctx.lineWidth = currLineWidth
switch (currentType) {
case Main_t.Draw_Type.Type_Rect:
ctx.beginPath()
ctx.rect(startPoint.x, startPoint.y,
endPoint.x - startPoint.x,
endPoint.y - startPoint.y) // (startX, startY, width, height)
ctx.stroke()
break;
case Main_t.Draw_Type.Type_Rect_Fill:
ctx.beginPath()
ctx.rect(startPoint.x, startPoint.y,
endPoint.x - startPoint.x,
endPoint.y - startPoint.y) // (startX, startY, width, height)
ctx.fill();
ctx.stroke()
break;
case Main_t.Draw_Type.Type_Circle:
ctx.beginPath()
var centerX = (startPoint.x + endPoint.x) / 2
var centerY = (startPoint.y + endPoint.y) / 2
var radius = distance(startPoint, endPoint) / 2
ctx.arc(centerX, centerY, radius, 0, 360) // (centerX, centerY, radius, angle_start, angle_end)
ctx.stroke();
break;
case Main_t.Draw_Type.Type_Circle_Fill:
ctx.beginPath()
var centerX = (startPoint.x + endPoint.x) / 2
var centerY = (startPoint.y + endPoint.y) / 2
var radius = distance(startPoint, endPoint) / 2
ctx.arc(centerX, centerY, radius, 0, 360) // (centerX, centerY, radius, angle_start, angle_end)
ctx.fill();
ctx.stroke();
break;
case Main_t.Draw_Type.Type_Line:
ctx.beginPath()
ctx.moveTo(startPoint.x, startPoint.y) // (startX, startY)
ctx.lineTo(endPoint.x, endPoint.y) // (endX, endY)
ctx.stroke()
break;
case Main_t.Draw_Type.Type_Paint:
ctx.beginPath()
ctx.moveTo(startPoint.x, startPoint.y) // (startX, startY)
ctx.lineTo(endPoint.x, endPoint.y) // (endX, endY)
ctx.stroke()
startPoint = endPoint
return;
}
// 画完后,把实时绘画面板给清除掉
startPoint = Qt.point(0, 0)
endPoint = Qt.point(0, 0)
canvas_ing.requestPaint()
}
}
// 实时绘图画板
Canvas {
id: canvas_ing
anchors.fill: canvas_save
onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height)
ctx.strokeStyle = borderColor
ctx.fillStyle = fillColor
ctx.lineWidth = currLineWidth
switch (currentType) {
case Main_t.Draw_Type.Type_Rect:
ctx.beginPath();
ctx.rect(startPoint.x, startPoint.y,
endPoint.x - startPoint.x,
endPoint.y - startPoint.y); // (startX, startY, width, height)
ctx.stroke();
break;
case Main_t.Draw_Type.Type_Rect_Fill:
ctx.beginPath()
ctx.rect(startPoint.x, startPoint.y,
endPoint.x - startPoint.x,
endPoint.y - startPoint.y) // (startX, startY, width, height)
ctx.fill();
ctx.stroke()
break;
case Main_t.Draw_Type.Type_Circle:
ctx.beginPath()
var centerX = (startPoint.x + endPoint.x) / 2
var centerY = (startPoint.y + endPoint.y) / 2
var radius = distance(startPoint, endPoint) / 2
ctx.arc(centerX, centerY, radius, 0, 360) // (centerX, centerY, radius, angle_start, angle_end)
ctx.stroke();
break;
case Main_t.Draw_Type.Type_Circle_Fill:
ctx.beginPath()
var centerX = (startPoint.x + endPoint.x) / 2
var centerY = (startPoint.y + endPoint.y) / 2
var radius = distance(startPoint, endPoint) / 2
ctx.arc(centerX, centerY, radius, 0, 360) // (centerX, centerY, radius, angle_start, angle_end)
ctx.fill();
ctx.stroke();
break;
case Main_t.Draw_Type.Type_Line:
ctx.beginPath()
ctx.moveTo(startPoint.x, startPoint.y) // (startX, startY)
ctx.lineTo(endPoint.x, endPoint.y) // (endX, endY)
ctx.stroke()
break;
case Main_t.Draw_Type.Type_Paint:
// 这个不需要实时显示了,直接画在显示面板上就行
break;
}
}
MouseArea{
anchors.fill: parent
onPressed: {
startPoint = Qt.point(mouseX, mouseY)
if(currentType === Main_t.Draw_Type.Type_Paint) {
endPoint = Qt.point(mouseX, mouseY)
canvas_save.requestPaint()
}
}
onPositionChanged: {
if(!pressed) return;
endPoint = Qt.point(mouseX, mouseY)
canvas_ing.requestPaint()
if(currentType === Main_t.Draw_Type.Type_Paint) {
canvas_save.requestPaint()
}
}
onReleased: {
endPoint = Qt.point(mouseX, mouseY)
canvas_save.requestPaint()
}
}
}
// 菜单区域
Rectangle {
id: menuRec
width: parent.width
height: 40
anchors.top: parent.top
color: "#CCCCCC"
Row{
width: parent.width- 20
height: parent.height
anchors.centerIn: parent
spacing: 20
Text {
text: qsTr("清空画板")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
color: "#888888"
MouseArea {
anchors.fill: parent
onClicked: {
needClearSaved = true;
canvas_save.requestPaint();
}
}
}
Rectangle { // 填充颜色
id: fillColorRec
height: 30
width: parent.height
color: fillColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
onClicked: {
colorFillSelect.open()
}
}
}
Rectangle { // 边框颜色
id: borderColorRec
height: 30
width: parent.height
color: "transparent"
border.width: 2
border.color: borderColor
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
onClicked: {
colorBorderSelect.open()
}
}
}
Item{
width: 120
height: parent.height
Text {
id: lineWidthLabel
text: qsTr("线条粗细 ")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
}
SpinBox{
anchors.left: lineWidthLabel.right
anchors.verticalCenter: parent.verticalCenter
minimumValue: 1
value: currLineWidth
onValueChanged: {
currLineWidth = value
}
}
}
Text {
text: qsTr("空矩形")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
color: currentType === Main_t.Draw_Type.Type_Rect ? "#444444" : "#888888"
MouseArea {
anchors.fill: parent
onClicked: {
currentType = Main_t.Draw_Type.Type_Rect
}
}
}
Text {
text: qsTr("填充矩形")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
color: currentType === Main_t.Draw_Type.Type_Rect_Fill ? "#444444" : "#888888"
MouseArea{
anchors.fill: parent
onClicked: {
currentType = Main_t.Draw_Type.Type_Rect_Fill
}
}
}
Text {
text: qsTr("空圆形")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
color: currentType === Main_t.Draw_Type.Type_Circle ? "#444444" : "#888888"
MouseArea{
anchors.fill: parent
onClicked: {
currentType = Main_t.Draw_Type.Type_Circle
}
}
}
Text {
text: qsTr("填充圆形")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
color: currentType === Main_t.Draw_Type.Type_Circle_Fill ? "#444444" : "#888888"
MouseArea{
anchors.fill: parent
onClicked: {
currentType = Main_t.Draw_Type.Type_Circle_Fill
}
}
}
Text {
text: qsTr("直线")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
color: currentType === Main_t.Draw_Type.Type_Line ? "#444444" : "#888888"
MouseArea{
anchors.fill: parent
onClicked: {
currentType = Main_t.Draw_Type.Type_Line
}
}
}
Text {
text: qsTr("画笔")
font.pixelSize: 17
anchors.verticalCenter: parent.verticalCenter
color: currentType === Main_t.Draw_Type.Type_Paint ? "#444444" : "#888888"
MouseArea{
anchors.fill: parent
onClicked: {
currentType = Main_t.Draw_Type.Type_Paint
}
}
}
}
}
}
ColorDialog{
id: colorFillSelect
visible: false
onAccepted: {
fillColor = colorFillSelect.color
}
}
ColorDialog{
id: colorBorderSelect
visible: false
onAccepted: {
borderColor = colorBorderSelect.color
}
}
function distance(pointOne, pointTwo) {
var xDiff = pointTwo.x - pointOne.x;
var yDiff = pointTwo.y - pointOne.y;
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
}
}
结果显示: