QML 之 画布元素学习

画布 

画布元素(canvas element)的基本思想是 使用一个2D 对象来渲染路径。2D对象支持画笔,填充、渐变,文本和绘制路径创建命令。

  • strokeStyle: 画笔样式
  • fillStyle: 填充样式

  注意: 只有调用 stroke 或 fill 函数, 创建的路径才会绘制

有调⽤ stroke 或者 fill函数,创建的路径才会绘制, 它们与其它的函数使用是 相互独立的。

注意:画布元素 充当绘制的容器。 2D 绘制对象提供了实际绘制的方法,绘制需要在 onPaint 事件中完成。在重置路径后,需要设置一个开始点,所以在 beginPath() 这个操作后,需要使用 moveTo 来设置开始点。

         
  画布简单示例
代码:

便捷接口

我们获取的 2d 对象  提供一些常用 图形接口(矩形,圆形、弧形等)不需要 调用stroke 或者 fill 完成。

注意:画笔的绘制区域 由中间向 两边延展。 一个宽度为 4 像素的画笔 将会在绘制路径的 里面 绘制2个 像素,外面绘制 2个像素。

onPaint: {
    var ctx = getContext("2d")
    ctx.fillStyle = 'green'
    ctx.strokeStyle = "blue"
    ctx.lineWidth = 4
    // draw a filles rectangle
    ctx.fillRect(20, 20, 80, 80)

    // 绘制圆形
    ctx.beginPath();
    ctx.arc(200, 75, 50, 0, 2 * Math.PI, false);
    ctx.fill();  

    // 绘制弧线
    ctx.beginPath();
    ctx.arc(200, 225, 50, 0, Math.PI, false);
    ctx.stroke();
     
    // cut our an inner rectangle
    ctx.clearRect(30,30, 60, 60)
    // stroke a border from top-left to
    // inner center of the larger rectangle
    ctx.strokeRect(20,20, 40, 40)

    // 绘制路径  三角形
    ctx.beginPath();
    ctx.moveTo(250, 25);
    ctx.lineTo(350, 25);
    ctx.lineTo(350, 125);
    ctx.closePath(); // 关闭路径
    ctx.stroke();
}

渐变

 

渐变⾊在停⽌点定义⼀个颜⾊,范围从0.0到 1.0。

渐变色是在 画布坐标下定义的, 而不是在 绘制路径 相对坐标下定义的。 画布中没有相对坐标的概念。 

阴影

2d 对象 的路径可以使用阴影增强来显示效果。阴影是 一个区域的轮廓线 使用偏移量、颜色和模糊来实现的。黑色背景可能有更好效果

  • shadowColor: 阴影色
  • shadowOffsetX:阴影 X 轴偏移值
  • shadowOffsetY:阴影Y轴偏移值
  • shadowBlur: 阴影模糊

带模糊效果(shadowBlur): 

图片

QML 画布支持多种资源的图片绘制 ,drawImage 常用的版本 接受三个参数:要绘制的图像、目标位置的 x 坐标和 y 坐标。

注意:画布中使用 一张图片 需要 先加载图片资源(可以使用Componet中的loadImage 或者直接使用 Image 而不显示)

 绘制一个足球并进行裁剪

  • translate: 平移坐标系
  • clip: 裁剪

绘制特定部分 

ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh); 

  • sx, sy:源图像中要复制区域的左上角坐标。
  • sw, sh:源图像中要复制区域的宽度和高度。
  • dx, dy:目标画布上放置图像的左上角坐标。
  • dw, dh:目标画布上绘制图像的宽度和高度。

放大后的效果:

 转换

转换坐标系(缩放(scale)、旋转( rotate)、平移(translate))

先绘制一个一个矩形,移动坐标系,然后再绘制一个矩形 

 

 ctx.translate(120,60)  x移动到120,y移动到60。注意 如果重复移动 是相对新的坐标原点

注意: 重置矩阵可以 调用 resetTransform() 函数来完成,这个函数会将转换矩阵还原为单位矩阵

组合模式

组合模式允许 绘制一个形状然后与已有的像素点集合 混合

使⽤globalCompositeOperation(mode)来设置
常用的模式
  • source-over:新的绘制内容(即源 source) 会被绘制到现有内容(即目标, destination)之上。 如果新绘制的内容和已有内容有重叠,重叠部分会显示新的内容, 而新内容之外的部分保持不变。
  • source-in : 同上,在里面
  • source-out:  同上,在外面
  • source-atop:同上, 在下面
xor 模式绘制时,如果新的形状与已有形状重叠的地方,重叠部分将变得透明或被移除,而非重叠的部分会 被保留并显示出来
xor 模式效果:
常用模式:
常用模式模式:

所有代码: 

import QtQuick 2.15

Canvas{
    id: root
    width: 1200
    height: 700

    // onPaint: {
    //     var ctx = getContext("2d")
    //     ctx.lineWidth = 4
    //     ctx.strokeStyle = "blue"
    //     ctx.fillStyle = "steelblue"
    //     // begin a new path to draw
    //     ctx.beginPath()
    //     // top-left start point  左上角的开始点
    //     ctx.moveTo(50,50)
    //     // upper line
    //     ctx.lineTo(150,50)
    //     // right line
    //     ctx.lineTo(150,150)
    //     // bottom line
    //     ctx.lineTo(50,150)
    //     // left line through path closing
    //     ctx.closePath()
    //     // fill using fill style
    //     ctx.fill()
    //     // stroke using line width and stroke style
    //     ctx.stroke()
    // }

    // onPaint: {
    //     var ctx = getContext("2d")
    //     ctx.fillStyle = 'green'
    //     ctx.strokeStyle = "blue"
    //     ctx.lineWidth = 4
    //     // draw a filles rectangle
    //     ctx.fillRect(20, 20, 80, 80)

    //     // 绘制圆形
    //     ctx.beginPath();
    //     ctx.arc(200, 75, 50, 0, 2 * Math.PI, false);
    //     ctx.fill();  

    //     // 绘制弧线
    //     ctx.beginPath();
    //     ctx.arc(200, 225, 50, 0, Math.PI, false);
    //     ctx.stroke();
         
    //     // cut our an inner rectangle
    //     ctx.clearRect(30,30, 60, 60)
    //     // stroke a border from top-left to
    //     // inner center of the larger rectangle
    //     ctx.strokeRect(20,20, 40, 40)

    //     // 绘制路径  三角形
    //     ctx.beginPath();
    //     ctx.moveTo(250, 25);
    //     ctx.lineTo(350, 25);
    //     ctx.lineTo(350, 125);
    //     ctx.closePath(); // 关闭路径
    //     ctx.stroke();
    // }


    // onPaint:{
    //     var ctx = getContext("2d")

    //     // var gradient = ctx.createLinearGradient(100,0,100,200)
    //     // gradient.addColorStop(0, "blue")
    //     // gradient.addColorStop(0.5, "lightsteelblue")

    //     // 创建线性渐变
    //     var gradient = ctx.createLinearGradient(0, 0, 300, 100); // 从左上角到右下角
    //     //addColorStop 方法用来设置渐变的颜色过渡点,其中第一个参数是位置,第二个参数是颜色。
    //     gradient.addColorStop(0, "red");  // 起始颜色
    //     gradient.addColorStop(0.5, "yellow"); // 中间颜色
    //     gradient.addColorStop(1, "blue");  // 结束颜色

    //     // 设置填充样式为渐变
    //     ctx.fillStyle = gradient
    //     //渐变⾊的定义⽐我们想要绘制的矩形更⼤,所以矩形在它定义的范围内对渐变进⾏了裁剪。
    //     //ctx.fillRect(100,0,100,100)
    //     ctx.fillRect(0,0,300,100)
    // }

    // onPaint:{
    //     var ctx =  getContext("2d")

    //     ctx.strokeStyle = "#333"
    //     ctx.shadowColor = "blue";
    //     ctx.shadowOffsetX = 2;
    //     ctx.shadowOffsetY = 2;
    //     // next line crashes
    //     // ctx.shadowBlur = 10;
    //     ctx.font = 'Bold 80px ';
    //     ctx.fillStyle = "#33a9ff";
    //     ctx.fillText("Earth",30,180);
    // }

    // onPaint:{
    //     var ctx = getContext("2d")
    //     // //要绘制的图像、目标位置的 x 坐标和 y 坐标。
    //     // ctx.drawImage("football.jpg", -150, -150)
    //     // // 确保图像已经加载完成
    //     // // if (myImage.status === Image.Ready) {
    //     // //         // 绘制图像
    //     // //         ctx.drawImage(myImage,-150, -150);  // 在 (-150, -150) 位置开始绘制图像
    //     // // }    
    //     // ctx.save()
    //     // ctx.strokeStyle = 'red'//
    //     // // create a triangle as clip region
    //     // ctx.beginPath()
    //     // ctx.moveTo(10,10)
    //     // ctx.lineTo(610,10)
    //     // ctx.lineTo(300,300)
    //     // ctx.closePath()
        
    //     // // translate(平移) coordinate system
    //     // //translate 方法接受两个参数:要移动的 x 和 y 坐标值 调用 translate 后,所有后续的绘图操作都会相对于新的坐标系进行。
    //     // ctx.translate(150,0)
    //     // //clip  之前所有的绘制操作都会⽤来进⾏裁剪
    //     // ctx.clip() // create clip from triangle path
    //     // // draw image with clip applied
    //     // ctx.drawImage("football.jpg", -150, -150)//在裁剪区域重新绘制
    //     // // draw stroke around path
    //     // ctx.stroke()
    //     // // restore previous setup
    //     // ctx.restore()


    //     // // 清除画布
    //     // ctx.clearRect(0, 0, width, height);
    //     // // 检查图像是否已准备好
    //     // if (myImage.status === Image.Ready) {
    //     //     // 绘制图像的某一部分
    //     //     ctx.drawImage(myImage, 50, 50, 300, 300, 50, 50, 600, 600);
    //     //     // 从原图(50, 50)处开始,取100x100大小的区域,然后绘制到画布(50, 50)处,并放大至200x200
    //     // }             
    // }

    // onPaint:{
    //     var ctx = getContext("2d")
    //     ctx.strokeStyle = "blue" 
    //     ctx.lineWidth = 4
    //     ctx.beginPath()
    //     // ctx.rect(-20, -20, 40, 40)
    //     ctx.rect(50, 50, 40, 40)
    //     ctx.translate(200, 200)
    //     ctx.stroke()

    //     ctx.strokeStyle = "green"
    //     ctx.rotat(Math.PI/4)
    //     ctx.stroke()
    // }

    // Component.onCompleted: {
    //     loadImage("football.jpg")
    // }

    // // 加载图像
    // Image {
    //     id: myImage
    //     source: "football.jpg"  // 替换为你的图片路径
    //     visible: false  // 不需要显示这个Image元素
    // }


    // onPaint: {
    //     var ctx = getContext("2d")
    //     ctx.lineWidth = 4
    //     ctx.strokeStyle = "blue"
    //     ctx.fillStyle = "steelblue"
    //     // begin a new path to draw
    //     ctx.beginPath()
    //     // top-left start point
    //     ctx.moveTo(50,50)
    //     // upper line
    //     ctx.lineTo(150,50)
    //     // right line
    //     ctx.lineTo(150,150)
    //     // bottom line
    //     ctx.lineTo(50,150)
    //     // left line through path closing
    //     ctx.closePath()
    //     // fill using fill style
    //     ctx.fill()
    //     // stroke using line width and stroke style
    //     ctx.stroke()
    // }


    // onPaint: {
    //     var ctx = getContext("2d")
    //     ctx.fillStyle = 'green'
    //     ctx.strokeStyle = "blue"
    //     ctx.lineWidth = 4
    //     // draw a filles rectangle
    //     ctx.fillRect(20, 20, 80, 80)
    //     // cut our an inner rectangle
    //     ctx.clearRect(30,30, 60, 60)
    //     // stroke a border from top-left to
    //     // inner center of the larger rectangle
    //     ctx.strokeRect(20,20, 40, 40)
    // }

    // onPaint: {
    //     var ctx = getContext("2d")
    //     var gradient = ctx.createLinearGradient(100,0,100,200)
    //     gradient.addColorStop(0, "blue")
    //     gradient.addColorStop(0.5, "lightsteelblue")
    //     ctx.fillStyle = gradient
    //     ctx.fillRect(50,50,100,100)

    // }


    // transform: [
    //         Rotation { origin.x: 120; origin.y: 60; angle: 45 }  // 绕 (120, 60) 点旋转 45 度
    //     ]
    // onPaint: {
    //     var ctx = getContext("2d")
    //     ctx.strokeStyle = "blue"
    //     ctx.lineWidth = 4
    //     ctx.beginPath()
    //     ctx.rect(100, 40, 40, 40)
    //     //ctx.rect(4, 4, 40, 40)
    //     // 设置旋转中心点
    //     ctx.translate(120,60)
    //     ctx.stroke()
    //     // 设置旋转中心点
    //     //ctx.translate(120,60)
    //     ctx.rotate(Math.PI/4)
    //     ctx.beginPath()
    //     // draw path now rotated
    //     ctx.strokeStyle = "green"
    //     ctx.rect(-20, -20, 40, 40)
    //     ctx.stroke()


    //     // var ctx = getContext("2d");
    //     // ctx.clearRect(0, 0, width, height);

    //     // // 保存当前上下文状态
    //     // ctx.save();
    //     // // ctx.strokeStyle = "blue"
    //     // // ctx.rect(-20, -20, 40, 40)
    //     // // 设置旋转中心点
    //     // //ctx.translate(width / 2, height / 2);
    //     // // 旋转上下文
    //     // //ctx.rotate(Math.PI / 4);  // 旋转 45 度

    //     // // 绘制矩形
    //     // ctx.fillStyle = "red";
    //     // ctx.fillRect(-50, -50, 100, 100);

    //     // // 恢复上下文状态
    //     // ctx.restore();
    // }

    // //组合模式--xor 模式
    // onPaint: {
    //     var ctx = getContext("2d")
    //     ctx.globalCompositeOperation = "xor"
    //     ctx.fillStyle = "#33a9ff"
    //     for(var i=0; i<40; i++) {
    //         ctx.beginPath()
    //         ctx.arc(Math.random()*400, Math.random()*200, 20, 0, 2*Math.PI)
    //         ctx.closePath()
    //         ctx.fill()
    //     }
    // }



    //组合模式--多模式
    property var operation : [
        'source-over', 'source-in', 'source-out',
        'source-atop', 'destination-over', 'destination-in',
        'destination-out', 'destination-atop', 'lighter',
        'copy', 'xor', 'qt-clear', 'qt-destination','qt-multiply', 'qt-screen', 'qt-overlay', 'qt-darken',
        'qt-lighten', 'qt-color-dodge', 'qt-color-burn',
        'qt-hard-light', 'qt-soft-light', 'qt-difference',
        'qt-exclusion'
    ]

    onPaint: {
        var ctx = getContext('2d')
        for(var i=0; i<operation.length; i++) {
            var dx = Math.floor(i%6)*100
            var dy = Math.floor(i/6)*100
            ctx.save()
            ctx.fillStyle = '#33a9ff'
            ctx.fillRect(10+dx,10+dy,60,60)
            // TODO: does not work yet
            ctx.globalCompositeOperation = root.operation[i]
            ctx.fillStyle = '#ff33a9'
            ctx.globalAlpha = 0.75
            ctx.beginPath()
            ctx.arc(60+dx, 60+dy, 30, 0, 2*Math.PI)
            ctx.closePath()
            ctx.fill()
            ctx.restore()
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值