Canvas绘图学习笔记:画笔的路径与状态

beginPath

beginPath表示开始一个路径,我们在上一章画弧的时候用到过好多次,他的API非常简单:

context.beginPath();

开始路径有2层意思,一个就是本次绘制的起点是新的(不再是上次结束的点了),另外一个意思就是绘制的样式也是新的(不再与之前的样式有关联)。对于第一条上一个章节我们已经见识过了,如果不开始一个新的路径那么描边弧线的时候就会有一条上次绘制结束到弧线开始时的连线(当然moveTo可以避免,但需要计算,不方便)。
现在考虑这么一个需求,我们需要画3条线,线的颜色分别是红绿蓝,使用之前的知识,你应该可以的,你先试一下?或许你写的代码是这样的:

context.moveTo(10, 50);
context.lineTo(290, 50);
context.strokeStyle='#FF0000';
context.stroke();context.moveTo(10, 75);
context.lineTo(290, 75);
context.strokeStyle='#00FF00';
context.stroke();context.moveTo(10, 100);
context.lineTo(290, 100);
context.strokeStyle='#0000FF';
context.stroke();

效果如下:

颜色出现错误

我们发现颜色是错误的,但是又不清楚哪里有问题了。难道是它的API有bug了吗?我们打断点,然后单步执行,看看上面三个stroke依次执行后的效果:

红色执行后


 

绿色执行后

蓝色执行后

我们可以看到,当代码执行到红色以后是对的(虽然把坐标系也变成红色了);然后绿色执行后把绿色这条渲染对了,但是又用绿色渲染了一下红色的那条线,使得红色的线变成2者的叠加色了;当绿色的执行完了以后,把最后一条线描边成绿色,但是又把前面的也渲染了一遍,所以最终的颜色就是我们之前看到的。要让新的线不在绘制之前的就用beginPath来开启一个新的路径。看看我们使用后的效果:

context.beginPath();
context.moveTo(10, 50);
context.lineTo(290, 50);
context.strokeStyle='#FF0000';
context.stroke();context.beginPath();
context.moveTo(10, 75);
context.lineTo(290, 75);
context.strokeStyle='#00FF00';
context.stroke();context.beginPath();
context.moveTo(10, 100);
context.lineTo(290, 100);
context.strokeStyle='#0000FF';
context.stroke();

效果如下:

正常渲染

总结一下:使用beginPath路径将不再与之前的联系,绘制时也不再绘制之前的(所以已绘制图案的样式不再叠加)。

closePath

closePath是闭合路径,注意是闭合路径而不是结束路径,虽然目前的位置是在beginPath后面,但是两者没什么关系,它并不是endPath(没有这个)。

现在有需求,需要描边一个45°的扇形,你以你现在的技术完全可以胜任,大笔一挥:

context.beginPath();
context.moveTo(150, 75);
context.arc(150, 75, 80, Math.PI / 180 * 0, Math.PI / 180 * 45);
context.lineTo(150, 75);
context.stroke();

结果如下: 

描边扇形

效果不错,挺满意的。现在我们观察倒数第二行代码,我们使用context.lineTo(150, 75);画了一条回到圆心(起点)的线。在stroke的时候回到起点可以绘制出一个闭合的图形,这种操作实在太多了,为了简化这个步骤,我们就可以使用closePath。现在直接把context.lineTo(150, 75);替换为context.closePath();你会发现效果是一样的,这样就省去了自己计算起点位置的步骤了。我强烈建议在闭合路径的时候使用closePath
需要顺便提醒一下,填充(fill)的时候,对于一个终点和起点没有闭合的路径,默认会闭合了再去填充(不然没得玩了),如下。当然如果还有其他没有闭合的时候(就比如平行的2个线段),那么就真的没的完了,他也“不会”绘制了。

context.beginPath();
context.moveTo(150, 75);
context.arc(150, 75, 80, Math.PI / 180 * 0, Math.PI / 180 * 45);
context.fill();

上面没有闭合,直接填充,结果和闭合了以后是一样的效果:

填充扇形

点是否在路径内部

跟路径有关的一个常见问题,就是需要判断点是否在一个路径的内部。canvas考虑到大家的这个需要,给了大家提供了这样的API:

// 坐标(x, y)是否在路径内部 如果在就返回true否则就返回false
context.isPointInPath(x, y);

这里需要注意的有三点:

  1. 如果一个路径结束和开始的位置没有闭合,判断的时候会按照闭合来处理(如果结束点和开始点闭合后整个路径还没有闭合,那么就返回false)。
  2. strokeRectfillRect不会保留绘制的矩形路径,所以isPointInPath不能对他们进行判断,可以使用rect代替。
  3. 如果刚刚在路径所处的直线上,那么需要根据线宽来决定,如果路径内与线中心一侧的时候那么返回false,其他的时候返回true,举个例子比如线宽是1,那么如果在线上,说明是内部;如果线宽是3,那么在内部和前2个像素上是内部,外面的一个像素是外部。

看了第三条你可能又会问那么就只想知道是否在线上怎么办,那就可能会用到另一个API了:

// 坐标(x, y)是否在描边上 如果在就返回true否则就返回false
context.isPointInStroke(x, y);

此时你可能还会问,你只想知道是否在路径的内部,根本不关心在不在描边上,那么怎么办?给你提醒一下,把这两个API综合起来判断就可以了,相信你一定可以做到的。此外这两个API比较简单就不再给出例子了,感兴趣的同学可以自己研究下。

裁剪区域

路径学完了我们先额外插播一个小知识,就是裁剪区域,先看个例子,我们先描边一个圆形,再填充一个矩形:

context.beginPath();
context.arc(150, 75, 40, Math.PI / 180 * 0, Math.PI / 180 * 360);
context.stroke();// 开始新的路径 与之前的不再有关系 如果不开始 下面的fill的时候会把上面圆也fill了
context.beginPath();
context.rect(150, 75, 40, 40);
context.fill();

结果如下: 

描边圆,填充矩形

然后我们按照圆的样子裁剪矩形,稍微修改一下代码:

context.beginPath();
context.arc(150, 75, 40, Math.PI / 180 * 0, Math.PI / 180 * 360);
context.stroke();// 按照圆裁剪
context.clip();context.beginPath();
context.rect(150, 75, 40, 40);
context.fill();

 结果如下:

按照圆裁剪矩形

这里需要注意的是裁剪也是基于路径来的,所以strokeRectfillRect是不生效的。
我们再画一个矩形:

context.beginPath();
context.arc(150, 75, 40, Math.PI / 180 * 0, Math.PI / 180 * 360);
context.stroke();// 按照圆裁剪
context.clip();context.beginPath();
context.rect(150, 75, 40, 40);
context.fill();// 再画一个矩形
context.beginPath();
context.rect(190, 35, 80, 80);
context.fill();

结果: 

再画一个矩形

什么放错图了?没错,就是这个样子!我们分析一下,上面画了一个圆,然后描边了,然后按照圆裁剪,那么下面画的第一个矩形会按照圆来裁剪,没问题。然后画了第二个矩形,那么问题来了,这个矩形也被裁剪了!那么怎么让第二个矩形不再裁剪呢?如果后面的一直都被裁剪,那么每裁剪一次就缩小一点点距离,那多痛苦。

状态的保存于恢复

接下来就是我们的处理办法了,如果裁剪前把当前状态保存了,然后裁剪完第一个矩形后,再把状态恢复了,不是很好的解决了这个问题吗?canvas也是这么做的:

context.beginPath();
context.arc(150, 75, 40, Math.PI / 180 * 0, Math.PI / 180 * 360);
context.stroke();// 保存状态
context.save();context.clip();context.beginPath();
context.rect(150, 75, 40, 40);
context.fill();// 恢复之前保存的状态,即没有裁剪时那个状态
context.restore();context.beginPath();
context.rect(190, 35, 80, 80);
context.fill();

结果:

恢复状态

通常裁剪前一般都会保存路径的,裁剪完后,一般都会恢复的。除此之外保存与恢复也可以用在某些样式状态上,还可以用在形变(后面会讲到的,类似与CSS3的transform)的状态保存上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/110262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

强大的下载管理器:Progressive Downloader for Mac

Progressive Downloader for Mac是一种强大的下载管理器,它可以帮助用户更快速、更稳定地下载文件。相比于其他下载工具,PD下载管理器具有很多独特的功能和优势,本文将对其进行详细推广。 在数字化时代,下载已成为我们日常工作中必…

pdf压缩文件怎么压缩最小?

pdf压缩文件怎么压缩最小?我们很多项目介绍或是学术的报告都是采用的这个pdf格式,那么我们在存储或是需要进行分享的时候,可能就会因为文件过大而导致无法打开或是发送了。那么就需要将其进行压缩。PDF文件压缩方法很多,pdf压缩文…

web前端面试-- http的各个版本的区别(HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2.0、HTTP/3.0)

本人是一个web前端开发工程师,主要是vue框架,整理了一些面试题,今后也会一直更新,有好题目的同学欢迎评论区分享 ;-) web面试题专栏:点击此处 http的各个版本的区别 HTTP(超文本传输协议&…

el-input: 把不符合正则校验的值动态清空,只保留符合的值

<el-input v-model"form.profit" placeholder"请输入授权专利新增利润" input"handleInput" clearable />/*** 不符合正则校验,清空*/const handleInput () > {if (form.value.profit) {if (!/^\d*\.?\d*$/.test(form.value.profit))…

Elasticsearch7.9.3保姆级安装教程

Linux版本Elasticsearch版本(待安装)Kibana版本(待安装)CentOS 77.9.37.9.3 一、下载地址 1、官网下载 打开地址 https://www.elastic.co/cn/downloads/past-releases#elasticsearch&#xff0c;按如图所示选择对应版本即可 2、采用wget下载 为了不必要的麻烦&#xff0c;建…

【vscode编辑器插件】前端 php unity自用插件分享

文章目录 一篇一句前言前端vuegitphpunity后端其他待续完结 一篇一句 “思考是最困难的工作&#xff0c;这也许是为什么很少有人这样做。” - 亨利福特&#xff08;Henry Ford&#xff09; 前言 无论是什么语言&#xff0c;我都会选择使用vscode进行开发&#xff0c;我愿称v…

麒麟kylinOS 2303制作自定义免交互安装镜像

原文链接&#xff1a;麒麟kylinOS 2303制作自定义免交互安装镜像 hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇麒麟kylinOS 2303制作自定义免交互ISO安装镜像的文章&#xff0c;内容相对来说比较简单&#xff0c;测试安装了一个360浏览器软件&#xff0c;后续复杂…

openstack 云主机 linux报 login incorrect

还未输入密码就提示login incorrect 不给输密码位置 完全不给输密码的机会 关机进入单用户 检查登录安全记录 vi /var/log/secure 发现 /usr/lib64/security/pam_unix.so 报错 将正常的机器提取/usr/lib64/security/pam_unix.so 比对MD5一致&#xff0c; 另外判断 libtir…

读《中国省级移动政务服务报告2023》

报告地址&#xff1a; 中国省级移动政务服务报告2022 https://www.digitalelite.cn/h-nd-7846.html 中国省级移动政务服务报告2023 中国省级移动政务服务报告2023 报告分为 引言、评估方法、概貌、指数、标杆、建言 六个部分。 一些思考 移动政务服务应用针对各省的常驻人口…

手术麻醉临床信息管理系统源码,客户端可以接入监护仪、麻醉机、呼吸机

一、手术麻醉临床信息管理系统介绍 1、手术麻醉临床信息管理系统是数字化手段应用于手术过程中的重要组成部分&#xff0c;用数字形式获取并存储手术相关信息&#xff0c;既便捷又高效。既然是管理系统&#xff0c;那就是一整套流程&#xff0c;管理患者手术、麻醉的申请、审批…

uniapp collapse动态生成多个折叠面板手动展开收起(包括uni-ui版)

前言 官方文档没有暴露出相关api&#xff0c;那就看看组件源码。 以下示例均通过 vue-cli 创建的 uni-app h5 项目 uView&#xff08;1.x&#xff09;版本 源码 node_modules\uview-ui\components\u-collapse-item\u-collapse-item.vue 这个方法是用来改变折叠面板子组件收起还…

解决Windows内存溢出/占满死机问题-PoolMon工具

某一天&#xff0c; 工作所用笔记本突然越来越卡直至死机 以为只是windows11的抽风行为&#xff0c;之前就因为windows11资源管理器经常卡死&#xff08;后升级小版本好多了&#xff09;。 遂长按电源键强制关机重启。 然慢慢又越来越卡&#xff0c;直至卡死&#xff0c;无…

Unity之ShaderGraph如何实现积雪效果

前言 我们在一些特殊场景&#xff0c;比如冰雪天&#xff0c;经常会对周围物体添加一些积雪效果&#xff0c;如果我们直接把积雪做到模型上&#xff0c;就无法更加灵活的表现其他天气的环境了&#xff0c;比如春夏秋冬切换。所以一般这种需求我们都是使用Shader来表现。 入下图…

QGIS如何将路网中的多条路段合并成一条完整的路

1、单条路数据提取 我的gis数据是放在postgresql中的&#xff0c;所以使用sql筛选数据&#xff0c; 然后执行sql筛选数据 将筛选的数据生成新的图层&#xff1a; 注意&#xff01;&#xff01;&#xff01; 生成的新图层要保存成shp文件&#xff0c;否则后面没有办法编辑图…

浅谈压力测试的重要目标及意义

随着互联网应用的快速发展&#xff0c;软件系统的稳定性和性能成为了用户和企业关注的焦点。用户期望应用程序能够在高负载下依然保持稳定和高效。为了满足这一需求&#xff0c;压力测试成为了不可或缺的一环。本文将探讨压力测试的重要性以及如何进行压力测试。 一、压力测试的…

游戏设计模式专栏(十一):在Cocos游戏开发中运用享元模式

点击上方亿元程序员关注和★星标 引言 大家好&#xff0c;我是亿元程序员&#xff0c;一位有着8年游戏行业经验的主程。 本系列是《和8年游戏主程一起学习设计模式》&#xff0c;让糟糕的代码在潜移默化中升华&#xff0c;欢迎大家关注分享收藏订阅。 享元模式&#xff08…

如何创建前端自定义主题和样式?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

PG集合查询

1.运算符 1.1 union并集 连接上下语句 union distinct连接并且去重 all不去重 1.2 intersect交集 上下交集 distinct连接并且去重 all不去重 1.3 except除外 上面除了下面 distinc去重 all不去重

ORACLE 特殊日期时间转换,计算

一&#xff1a;特殊日期处理 如该字段存储日期形式为&#xff1a;2023/4/23 9:00&#xff0c;2023-3-1 12:23。将这样的数据转换成正确的格式&#xff08;yyyy-mm-dd HH24:mi:ss&#xff09;&#xff0c;即为&#xff1a;2023-04-23 09:00:00。这里举例的字段为&#xff1a;JS…