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

news2024/11/19 1:37:06

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.coloradmin.cn/o/1105840.html

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

相关文章

诈骗分子投递“大闸蟹礼品卡”,快递公司如何使用技术手段提前安全预警?

目录 快递公司能不能提前识别? 如何通过技术有效识别 为即将带来的双十一提供安全预警 金秋十月,正是品尝螃蟹的季节。中秋国庆长假也免不了走亲访友,大闸蟹更是成了热门礼品。10月7日,演员孙艺洲发布微博称,“收到…

优思学院|质量保证是什么?提高产品和服务质量的关键方法

在当今竞争激烈的市场中,企业需要确保他们的产品和服务能够满足客户的期望。质量保证是实现这一目标的关键。本文将探讨质量保证的重要性,以及提高产品和服务质量的方法。 1. 了解质量保证的概念 质量保证是指一系列的活动和措施,旨在确保产…

STM32 裸机编程 03

MCU 启动和向量表 当 STM32F429 MCU 启动时,它会从 flash 存储区最前面的位置读取一个叫作“向量表”的东西。“向量表”的概念所有 ARM MCU 都通用,它是一个包含 32 位中断处理程序地址的数组。对于所有 ARM MCU,向量表前 16 个地址由 ARM …

Databend 开源周报第 115 期

Databend 是一款现代云数仓。专为弹性和高效设计,为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务:https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展,遇到更贴近你心意的 Databend 。 聚合索引 Data…

再现ORA-600 4000故障处理---惜分飞

有一个10g的库,由于redo损坏导致无法正常recover成功 正常途径无法open成功,尝试强制打开库 Wed Oct 18 11:23:25 2023 alter database open resetlogs Wed Oct 18 11:23:25 2023 RESETLOGS is being done without consistancy checks. This may result in a corr…

端到端测试(End-to-end tests)重试策略

作者|Giuseppe Donati,Trivago公司Web测试自动化工程师 整理|TesterHome 失败后重试,是好是坏? 为什么要在失败时重试所有测试?为什么不? 作为Trivago(德国酒店搜索服务平台&…

Cesium Vue(四)— 物体(Entity)的添加与配置

1. 添加标签和广告牌 // 添加文字标签和广告牌var label viewer.entities.add({position: Cesium.Cartesian3.fromDegrees(113.3191, 23.109, 750),label: {text: "广州塔",font: "24px sans-serif",fillColor: Cesium.Color.WHITE,outlineColor: Cesium.…

什么是美颜SDK?解析主播直播美颜SDK的技术原理与应用

当下,越来越多的主播和内容创作者依赖直播平台来与观众互动、分享内容和实时传达信息。然而,为了在激烈的竞争中脱颖而出,许多主播需要借助美颜技术来提高他们的外貌吸引力。这就引入了主播直播美颜SDK,一个结合了技术原理与应用的…

链表 oj2 (7.31)

206. 反转链表 - 力扣(LeetCode) 我们通过头插来实现 将链表上的节点取下来(取的时候需要记录下一个节点),形成新的链表,对新的链表进行头插。 /*** Definition for singly-linked list.* struct ListNode…

2024年湖北建筑安全员abc三类人员考试新题库考试题库

2024年湖北建筑安全员abc三类人员考试新题库考试题库 湖北三类人员建筑安全员ABC证新题库是存在的,因为安管系统老更新,每次更新后,新题库(重点题库)就会有所变化。新题库主要是针对考试的,提高考试合格率…

基于selenium的pyse自动化测试框架

介绍: pyse基于selenium(webdriver)进行了简单的二次封装,比selenium所提供的方法操作更简洁。 特点: 默认使用CSS定位,同时支持多种定位方法(id\name\class\link_text\xpath\css)…

新工控机的基本配置过程

我们50水球上的工控机使用的是:英特尔 NUC10i7FNH 迷你电脑(通过查看铭牌获知)。对应官网: https://www.intel.cn/content/www/cn/zh/support/products/188811/intel-nuc/intel-nuc-kits/intel-nuc-kit-with-10th-generation-inte…

【temu】分析拼多多跨境电商Temu数据分析数据采集

Temu是拼多多旗下跨境电商平台,于2022年9月1日在美国、加拿大、新加坡、中国台湾、中国香港等市场上线。本文作者从销售额、销量、产品分布等方面,对Temu产品进行了分析,一起来看一下吧。 item_get获得商品详情item_review获得商品评论列表it…

线上购药小程序的崭新时代:医疗与科技的完美结合

在当今数字化时代,医疗和科技的融合已经催生了许多创新的解决方案,其中线上购药小程序正是医疗与科技完美结合的杰出代表。在这篇文章中,我们将一起探讨如何使用现代技术来创建一个简单的线上购药小程序原型,以展示医疗和科技的崭…

Mysql 约束,基本查询,复合查询与函数

文章目录 约束空属性约束默认值约束zerofill主键约束自增长约束唯一键约束外键约束 查询select的执行顺序单表查询排序 updatedelete整张表的拷贝复合语句group by分组查询 函数日期函数字符串函数数学函数其他函数 复合查询合并查询union 约束 空属性约束 两个值&#xff1a…

springcloud之项目实战服务治理

写在前面 在这篇文章 我们已经搭建完成了优惠券模块的单体版本,为了向微服务化迈出坚实的一步,这部分来看下服务治理的内容,并在我们的项目中引入服务治理,下面我们就开始吧! 源码 。 1:什么是&#xff…

基于Java公益志愿捐赠管理系统设计与实现(源码+LW+调试+开题报告)

项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问…

卡尔曼滤波器融合六轴IMU数据

卡尔曼滤波 卡尔曼滤波是一种常用的状态估计方法,其基本原理是对系统状态进行最优估计。其主要思想是将系统的状态分为先验状态和后验状态,通过将先验状态和观测值进行加权平均,得到最优估计状态。 卡尔曼滤波增益的计算是使后验误差协方差…

程序员工作5年,还是个中级程序员,如何再快速晋升?

我曾经就是这个状态,5年工作经验就像是一年工作经验用了5年。职业生涯遇到了瓶颈,无法突破。分析原因有很多,一方面是基本功没练好,像操作系统底层、数据结构、算法、计算机网络这些计算机基础知识掌握的不扎实,不能灵…

vue项目的创建——总结版

目录 前提: 一、vue项目的创建 二、浏览项目页面 三、vue项目目录文件含义和作用 ​四、修改端口号 前提: 首先要有Node.js环境。 1.安装vue npm install vue 2.命令行工具(CLI)为单页面应用快速搭建繁杂的脚手架 npm in…