canvas系列教程04 —— 渐变、阴影、路径、状态、Canvas对象、图形重叠模式

简介: canvas系列教程04 —— 渐变、阴影、路径、状态、Canvas对象、图形重叠模式

渐变

线性渐变

在一条直线上进行渐变

  1. 调用createLinearGradient()方法创建一个linearGradient对象
var gnt = cxt.createLinearGradient(x1, y1, x2, y2);

  • x1、y1表示渐变色开始点的坐标
  • x2、y2表示渐变色结束点的坐标
  1. 添加渐变(开始)
gnt.addColorStop(value1,color1);
  • value1 渐变开始的位置,取值 0~1
  • color1 渐变开始的颜色
  1. 添加渐变(结束)
gnt.addColorStop(value2,color2);
  • value2 渐变结束的位置,取值 0~1
  • color2 渐变结束的颜色

第2和第3步可以反复多次添加多个渐变,每次渐变都以上一个渐变的结束位置为开始位置

  1. 填充渐变
cxt.fillStyle = gnt; 
cxt.fill();
             var gnt = cxt.createLinearGradient(0,150, 200, 150);
             gnt.addColorStop(0, "HotPink");
             gnt.addColorStop(1, "white");
             cxt.fillStyle = gnt;
             cxt.fillRect(0, 0, 200, 150);

             var text = "绿叶学习网";
             cxt.font = "bold 50px 微软雅黑";

             var gnt = cxt.createLinearGradient(0, 75, 200, 75);
             gnt.addColorStop(0, "HotPink");
             gnt.addColorStop(1, "LightSkyBlue");

             cxt.fillStyle = gnt;
             cxt.fillText(text, 10, 90);

径向渐变(用得少)

径向渐变,是一种从起点到终点、颜色从内到外进行的圆形渐变(从中间向外拉,像圆一样)。径向渐变是圆形渐变或椭圆形渐变,颜色不再沿着一条直线渐变,而是从一个起点向所有方向渐变。

  1. 调用createLinearGradient()方法创建一个radialGradient对象
var gnt = cxt.createRadialGradient(x1,y1,r1,x2,y2,r2);
  • (x1,y1)表示渐变开始圆心的坐标,r1表示渐变开始圆心的半径。
  • (x2,y2)表示渐变结束圆心的坐标,r2表示渐变结束圆的半径。
  • 当起点圆与终点圆的圆心坐标相同时,会有一种圆形渐变的效果
  1. 添加渐变(开始)
gnt.addColorStop(value1,color1);
  • value1 渐变开始的位置,取值 0~1
  • color1 渐变开始的颜色
  1. 添加渐变(结束)
gnt.addColorStop(value2,color2);
  • value2 渐变结束的位置,取值 0~1
  • color2 渐变结束的颜色

第2和第3步可以反复多次添加多个渐变,每次渐变都以上一个渐变的结束位置为开始位置

  1. 填充渐变
cxt.fillStyle = gnt; 
cxt.fill();

fill()可以改为fillRect()或fillText()。其中fillRect()表示矩形渐变,fillText()表示文字渐变。

             //画圆
             cxt.beginPath();
             cxt.arc(80, 80, 50, 0, Math.PI * 2, true);
             cxt.closePath();
             //渐变
             var gnt = cxt.createRadialGradient(100, 60, 10, 80, 80, 50);
             gnt.addColorStop(0, "white");
             gnt.addColorStop(0.9, "orange");
             gnt.addColorStop(1, "rgba(0,0,0,0)");
             //填充
             cxt.fillStyle = gnt;
             cxt.fill();

             gradient = cxt.createRadialGradient(60, 60, 0, 60, 60, 60);
             gradient.addColorStop("0", "magenta");
             gradient.addColorStop("0.25", "blue");
             gradient.addColorStop("0.50", "green");
             gradient.addColorStop("0.75", "yellow");
             gradient.addColorStop("1.0", "HotPink");
             cxt.fillStyle = gradient;
             cxt.fillRect(0, 0, 120, 120);

             var i = 0;
             setInterval(function () {
                  gradient = cxt.createRadialGradient(60, 60, 0, 60, 60, 60);
                  gradient.addColorStop(i * 0, "magenta");
                  gradient.addColorStop(i * 0.25, "blue");
                  gradient.addColorStop(i * 0.50, "green");
                  gradient.addColorStop(i * 0.75, "yellow");
                  gradient.addColorStop(i * 1.0, "HotPink");
                  cxt.fillStyle = gradient;

                  i = i + 0.1;
                  if (i >= 1) { //超过颜色点值后,自动归0
                        i = 0;
                  }
                  cxt.fillRect(0, 0, 120, 120);
             }, 50);


阴影

属性

说明

shadowOffsetX

阴影与图形的水平距离,默认值为0。大于0时向右偏移,小于0时向左偏移

shadowOffsetY

阴影与图形的垂直距离,默认值为0。大于0时向下偏移,小于0时向上偏移

shadowColor

阴影的颜色,默认值为黑色

shadowBlur

阴影的模糊值,默认值为0。该值越大,模糊度越强;该值越小,模糊度越弱

Canvas阴影属性使用的也是W3C坐标系,y 轴向下

             //设置左上方向的阴影
             cxt.shadowOffsetX = -5;
             cxt.shadowOffsetY = -5;
             cxt.shadowColor = "LightSkyBlue ";
             cxt.shadowBlur = 1;
             cxt.fillStyle = "HotPink";
             cxt.fillRect(30, 30, 50, 50);

             //设置右下方向的阴影
             cxt.shadowOffsetX = 5;
             cxt.shadowOffsetY = 5;
             cxt.shadowColor = "LightSkyBlue ";
             cxt.shadowBlur = 10;
             cxt.fillStyle = "HotPink";
             cxt.fillRect(100, 30, 50, 50);

             //定义文字
             var text = "绿叶学习网";
             cxt.font = "bold 60px 微软雅黑";

             //定义阴影
             cxt.shadowOffsetX = 5;
             cxt.shadowOffsetY = 5;
             cxt.shadowColor = "LightSkyBlue ";
             cxt.shadowBlur = 10;

             //填充文字
             cxt.fillStyle = "HotPink";
             cxt.fillText(text, 10, 90);

             //创建image对象
             var image = new Image();
             image.src = "images/princess.png";

             image.onload = function () {
                 //定义阴影
                 cxt.shadowOffsetX = 5;
                 cxt.shadowOffsetY = 5;
                 cxt.shadowColor = "HotPink";
                 cxt.shadowBlur = 10;
                 cxt.fillRect(40, 15, 120, 120);

                 cxt.drawImage(image, 40, 15);
             }

四个方向的阴影效果,只需要shadowOffsetX和shadowOffsetY这两个属性的值都定义为0就可以了。

             //创建image对象
             var image = new Image();
             image.src = "images/princess.png";

             image.onload = function () {
                 //定义阴影
                 cxt.shadowOffsetX = 0;
                 cxt.shadowOffsetY = 0;
                 cxt.shadowColor = "HotPink";
                 cxt.shadowBlur = 10;
                 cxt.fillRect(40, 15, 120, 120);

                 cxt.drawImage(image, 40, 15);
             }

路径

方法

说明

beginPath()

开始一条新的路径

closePath()

关闭当前路径

isPointInPath()

判断某一个点是否存在于当前路径内

beginPath()

cxt.beginPath();

用于开始一个新路径(同时也是结束上一个路径),刚开始绘图时,其实都默认执行了一次 beginPath() 开始了一个新路径,直到再次遇到 beginPath() 才会结束默认创建的路径,而开始一段新路径。


Canvas基于“状态”绘制图形。每一次绘制(stroke()或fill()),Canvas会检测整个程序定义的所有状态,这些状态包括strokeStyle、fillStyle、lineWidth等。当一个状态值没有被改变时,Canvas就一直使用最初的值,当一个状态值被改变时,需要分两种情况考虑。


(1)如果使用beginPath()开始一个新的路径,则不同路径使用不同的值。

(2)如果没有使用beginPath()开始一个新的路径,则后面的值会覆盖前面的值(后来者居上原则)。

             cxt.lineWidth = 5;

             //第1条直线
             cxt.moveTo(50, 40);
             cxt.lineTo(150, 40);
             cxt.strokeStyle = "red";
             cxt.stroke();

             //第2条直线
             cxt.moveTo(50, 80);
             cxt.lineTo(150, 80);
             cxt.strokeStyle = "green";
             cxt.stroke();

             //第3条直线
             cxt.moveTo(50, 120);
             cxt.lineTo(150, 120);
             cxt.strokeStyle = "blue";
             cxt.stroke();

三条直线都属于同一个路径,所以cxt.strokeStyle=‘green’;会覆盖cxt.strokeStyle=‘red’;,然后cxt.strokeStyle=‘blue’;会覆盖cxt.strokeStyle=‘green’;。因此strokeStyle属性最终取值为blue,即三条直线都是blue。

             cxt.lineWidth = 5;

             //第1条直线
             cxt.beginPath();
             cxt.moveTo(50, 40);
             cxt.lineTo(150, 40);
             cxt.strokeStyle = "red";
             cxt.stroke();

             //第2条直线
             cxt.beginPath();
             cxt.moveTo(50, 80);
             cxt.lineTo(150, 80);
             cxt.strokeStyle = "green";
             cxt.stroke();

             //第3条直线
             cxt.beginPath();
             cxt.moveTo(50, 120);
             cxt.lineTo(150, 120);
             cxt.strokeStyle = "blue";
             cxt.stroke();

使用beginPath()后,三条直线将位于不同的路径中。因此,不同路径中定义的状态不会像上一个例子那样发生覆盖。

判断是否属于同一路径的标准是:是否使用了beginPath(),而不是视觉上是否有首尾连线。

Canvas中的绘制方法如stroke()、fill()等,都是以“之前最近的beginPath()”后面所有定义的状态为基础进行绘制的。

closePath()

cxt.closePath();

用于关闭路径,即将同一个路径的起点与终点连接起来,使其成为一个封闭的图形。

常用于绘制多边形的最后一步。

如果Canvas只有一条线段的话,那么closePath()方法就什么都不做。

             cxt.arc(70, 70, 50, 0, -90 * Math.PI / 180, true);
             cxt.stroke();
             cxt.closePath();


             cxt.lineWidth = 10;
             cxt.strokeStyle = "HotPink";

             cxt.moveTo(40, 60);
             cxt.lineTo(100, 60);
             cxt.lineTo(100, 30);
             cxt.lineTo(150, 75);
             cxt.lineTo(100, 120);
             cxt.lineTo(100, 90);
             cxt.lineTo(40, 90);
             cxt.lineTo(40, 60);
             cxt.stroke();

将lineWidth定义得足够大(10px)时,如果使用lineTo()方法来关闭图形,会有一个如图所示的“缺口”小问题。有两种方法可以解决:

  1. 定义lineCap属性值为square。
  2. 在stroke()方法之前使用closePath()关闭图形(推荐)

isPointInPath()

cxt.isPointInPath(x , y);

用于判断点 x,y 是否在当前路径中,若在返回true,否则返回false,仅对 rect()方法有效,对 strokeRect() 和 fillRect() 无效。

             cxt.strokeStyle = "HotPink";
             cxt.rect(50, 50, 80, 80);
             cxt.stroke();
             if (cxt.isPointInPath(100, 50)) {
                   alert("点(100,100)存在于当前路径中");
             }

             cxt.moveTo(50, 50);
             cxt.lineTo(150, 50);
             cxt.stroke();
             if (cxt.isPointInPath(100, 50)) {
                   alert("点(50,100)存在于当前路径中");
             }

上面代码只在IE浏览器中运行中有效果,但是在Google浏览器和Firefox中不会弹出对话框。实际上,当我们想要使用isPointInPath()方法判断某个点是否位于一条直线上时,在Goole和Firefox浏览器中都是无法实现的。不过可以使用isPointInPath()方法判断某个点是否位于一个图形(如矩形、圆形等)上面。

状态

Canvas基于“状态”来绘制图形。每一次绘制(stroke()或fill()),Canvas会检测整个程序定义的所有状态,这些状态包括strokeStyle、fillStyle、lineWidth等。当一个状态值没有被改变时,Canvas就会一直使用最初的值。当一个状态值被改变时,分两种情况考虑:

(1)如果使用beginPath()开始一个新的路径,则不同路径使用不同的值。

(2)如果没有使用beginPath()开始一个新的路径,则后面的值会覆盖前面的值(后来者居上原则)。

状态的保存和恢复

Canvas提供了两个操作状态的方法:


save() 保存“当前状态”,如剪切状态、变换状态和绘图状态(各种绘图样式属性),不能保存路径状态(开始新的路径,只有beginPath()一个方法),也不能保存图形(Canvas只有当前一个上下文环境,如果想要恢复图形,就只能清空画布再重绘。)。

restore() 恢复“之前保存的状态”。

save()和restore()一般情况下都是成对配合使用,使用场景如下:


(1)图形或图片裁切。

(2)图形或图片变换。

(3)以下属性改变的时候:fillStyle、font、globalAlpha、globalCompositeOperation、lineCap、lineJoin、lineWidth、miterLimit、shadowBlur、shadowColor、shadowOffsetX、shadowOffsetY、strokeStyle、textAlign、textBaseline。

应用一:清除剪切区域

创建剪切区域 clip()

创建剪切区域后,绘制的图形都只限于这个剪切区域之内,超出剪切区域的部分不会显示(被剪切掉了)。

把整个画布(Canvas)看成一个房子,clip()方法的剪切区域则可以看成一扇窗户。即使房子再大,最终透过窗户所能看到的空间也就只有窗户这么大。

cxt.clip();
  1. 绘制一个基本图形
  2. 调用 clip() 创建一个剪切区域

clip()方法也不支持Canvas自带的两个方法:strokeRect()、fillRect()。如果要使用strokeRect()和fillRect(),请使用rect()方法来代替。

             //绘制一个"描边圆",圆心为(50,50),半径为40
             cxt.beginPath();
             cxt.arc(50, 50, 40, 0, 360 * Math.PI / 180, true);
             cxt.closePath();
             cxt.strokeStyle = "HotPink";
             cxt.stroke();

             //使用clip(),使得"描边圆"成为一个剪切区域
             cxt.clip();

             //绘制一个"填充矩形"
             cxt.beginPath();
             cxt.fillStyle = "#66CCFF";
             cxt.fillRect(50, 50, 100, 80);


若注释掉 cxt.clip();

清除剪切区域

在图形或者图片剪切(clip())之前使用save()方法来保持当前状态,然后在剪切(clip())之后使用restore()方法恢复之前保存的状态。

如果不使用save()和restore(),即便使用clearRect()方法清空画布,后面绘制的所有图形或图片也都会限制在这个剪切区域内。

             //save()保存状态
             cxt.save();
             //使用clip()方法指定一个圆形的剪切区域
             cxt.beginPath();
             cxt.arc(70, 70, 50, 0, 360 * Math.PI / 180, true);
             cxt.closePath();
             cxt.stroke();
             cxt.clip();
             //绘制一张图片
             var image = new Image();
             image.src = "images/princess.png";
             image.onload = function () {
                 cxt.drawImage(image, 10, 20);
             }
             $$("btn").onclick = function () {
                 //restore()恢复状态
                 cxt.restore();
                 //清空画布
                 cxt.clearRect(0, 0, cnv.width, cnv.height);
                 //绘制一张新图片
                 var image = new Image();
                 image.src = "images/Judy.png";
                 image.onload = function () {
                     cxt.drawImage(image, 10, 20);
                 }
             }

删除 cxt.save()和cxt.restore() 后效果为:

应用二:图片/图形的变换

             cxt.fillStyle = "HotPink";
             cxt.translate(30, 30);
             cxt.fillRect(0, 0, 100, 50);

             cxt.fillStyle = "LightSkyBlue ";
             cxt.translate(60, 60);
             cxt.fillRect(0, 0, 100, 50);

蓝色矩形预期坐标为 (60,60),最终效果却是 (90,90),因为之前 translate(30, 30),整个坐标系的原点已经发生了位移变化 !

解决方案是使用 save()和restore()来实现

             cxt.save();
             cxt.fillStyle = "HotPink";
             cxt.translate(30, 30);
             cxt.fillRect(0, 0, 100, 50);
             cxt.restore();

             cxt.fillStyle = "LightSkyBlue ";
             cxt.translate(60, 60);
             cxt.fillRect(0, 0, 100, 50);

在变换操作(平移、缩放、旋转)中,一般都是在操作之前使用save()方法保存当前状态,其中当前状态包括参考坐标、图形大小等。然后再使用restore()方法来恢复之前保存的状态。

Canvas对象

属性

  • globalAlpha 透明度
    默认值为1.0(完全不透明),取值范围为:0.0~1.0。其中0.0表示完全透明,1.0表示完全不透明。globalAlpha属性必须在图形绘制之前定义才有效。
  • width 宽度
  • height 高度

常用于文字水平居中对齐和清空画布

cxt.clearRect(0, 0, cnv.width, cnv.height);

方法

  • getContext(“2d”) 获取Canvas 2D上下文环境对象
  • toDataURL() 获取Canvas对象产生的位图的字符串,即将Canvas画布转换为图片的base64格式的字符串。
cnv.toDataURL(type);

type 可选,默认值为image/png类型

直接在Canvas画布上点击鼠标右键,在弹出的快捷菜单中也能“另存为”图片为本地图片,为什么还要那么麻烦地使用toDataURL()呢?


事实上,很多旧版本的浏览器并不具备这个功能。因此为了兼容性,建议使用toDataURL()方法进行处理。


canvas保存图片时,谷歌浏览器Chrome报错【解决方案】Not allowed to navigate top frame to data URL

https://blog.csdn.net/weixin_41192489/article/details/124386704

图形的重叠模式

默认情况下,后绘制的图形会覆盖之前绘制的图形。

通过 globalCompositeOperation 可以修改重叠模式

cxt.globalCompositeOperation = 属性值;

不同属性值,效果如下图:

属性值

说明

source-over

默认值,新图形覆盖旧图形

copy

只显示新图形,旧图形作透明处理

darker

两种图形都显示,在重叠部分,颜色由两个图形的颜色值相减后形成

destination-atop

只显示新图形与旧图形重叠部分以及新图形的其余部分,其他部分作透明处理

destination-in

只显示旧图形中与新图形重叠部分,其他部分作透明处理

destination-out

只显示旧图形中与新图形不重叠部分,其他部分作透明处理

destination-over

与source-over属性相反,旧图形覆盖新图形

lighter

两种图形都显示,在图形重叠部分,颜色由两个图形的颜色值相加后形成

source-atop

只显示旧与新图形重叠部分及旧图形的其余部分,其他部分作透明处理

source-in

只显示新图形中与旧图形重叠部分,其他部分作透明处理

source-out

只显示新图形中与旧图形不重叠部分,其余部分作透明处理

xor

两种图形都绘制,其中重叠部分透明处理

             cxt.globalCompositeOperation = "xor";

             //绘制矩形
             cxt.fillStyle = "HotPink";
             cxt.fillRect(30, 30, 60, 60);
             //绘制圆形
             cxt.beginPath();
             cxt.arc(100, 100, 40, 0, Math.PI * 2, true);
             cxt.closePath();
             cxt.fillStyle = "LightSkyBlue";
             cxt.fill();

             cxt.globalCompositeOperation = "xor";

             //绘制第1个矩形
             cxt.fillStyle = "HotPink";
             cxt.fillRect(30, 30, 60, 60);
             //绘制圆形
             cxt.beginPath();
             cxt.arc(100, 100, 40, 0, Math.PI * 2, true);
             cxt.closePath();
             cxt.fillStyle = "LightSkyBlue";
             cxt.fill();
             //绘制第2个矩形
             cxt.fillStyle = "HotPink";
             cxt.fillRect(110, 30, 60, 60);


目录
相关文章
|
4月前
|
前端开发 图形学
小功能⭐️3DCanvas交互方法、Unity自动调整文本框大小、改变文字大小和颜色
小功能⭐️3DCanvas交互方法、Unity自动调整文本框大小、改变文字大小和颜色
|
5月前
|
前端开发
css动画 —— 自定义不规则的动画路径 offset-path (含不规则的动画路径参数获取方法)
css动画 —— 自定义不规则的动画路径 offset-path (含不规则的动画路径参数获取方法)
120 1
|
5月前
|
前端开发
Canvas绘画设计之网格线背景设置成网格源码
Canvas绘画设计之网格线背景设置成网格源码
|
5月前
|
机器学习/深度学习 前端开发 算法
canvas系列教程03 —— 线的样式、绘制文本、操作图片(图片的渲染、缩放、裁剪、切割、平铺、特效)、变换元素(平移、缩放、旋转)(二)
canvas系列教程03 —— 线的样式、绘制文本、操作图片(图片的渲染、缩放、裁剪、切割、平铺、特效)、变换元素(平移、缩放、旋转)(二)
91 0
|
5月前
|
存储 前端开发 JavaScript
canvas系列教程03 —— 线的样式、绘制文本、操作图片(图片的渲染、缩放、裁剪、切割、平铺、特效)、变换元素(平移、缩放、旋转)(一)
canvas系列教程03 —— 线的样式、绘制文本、操作图片(图片的渲染、缩放、裁剪、切割、平铺、特效)、变换元素(平移、缩放、旋转)(一)
483 0
|
7月前
|
移动开发 前端开发 JavaScript
html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
226 0
html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
|
7月前
|
Web App开发 前端开发
canvas详解02-样式和颜色控制
canvas详解02-样式和颜色控制
125 1
|
7月前
|
前端开发
canvas实现动态替换人物的背景颜色
canvas实现动态替换人物的背景颜色
|
7月前
|
前端开发
Canvas设置样式无效导致圆变成椭圆的问题研究剖析
Canvas设置样式无效导致圆变成椭圆的问题研究剖析
54 0
|
存储 前端开发
canvas自定义绘制顺序解决遮挡问题
canvas自定义绘制顺序解决遮挡问题
252 0