Canvas:掌握图像变换合成与裁剪状态像素操作

想象一下,用几行代码就能创造出如此逼真的图像和动画,仿佛将艺术与科技完美融合,前端开发的Canvas技术正是这个数字化时代中最具魔力的一环,它不仅仅是网页的一部分,更是一个无限创意的画布,一个让你的想象力自由驰骋的平台。

目录

图像变换设置

图像合成设置

裁剪路径设置

状态保存与恢复

像素操作设置

图像变换设置

接下来我们开始讲解如何对canvas绘制的图像进行一些简单的操作,这里借助代码进行一个简单的概述及演示:

位移操作:这里借助translate函数传递两个参数代表水平和竖直移动的距离:

<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 位移,translate,位移坐标系,水平位移和竖直位移ctx.translate(100, 100)// 绘制矩形ctx.fillRect(0, 0, 50, 50)</script>
</body>

缩放操作:这里借助scale函数传递两个参数代表水平和竖直缩放的倍速:

<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 缩放,scale,水平缩放5倍,垂直缩放2倍ctx.scale(5, 2)// 绘制矩形ctx.fillRect(0, 0, 50, 50)</script>
</body>

旋转操作:这里借助rotate函数传递一个参数代表旋转的角度:

<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 旋转,rotate,旋转的角度ctx.rotate(Math.PI / 4);ctx.translate(300, 0);// 绘制矩形ctx.fillRect(-250, -25, 500, 50)</script>
</body>

综合操作:这里借助transform函数传递六个参数代表不同的作用:

<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// transform,参数分别代表:/*1. 水平缩放2. 水平倾斜3. 垂直倾斜4. 垂直缩放5. 水平位移6. 垂直位移*/ctx.transform(2, 1, -1, 2, 50, 0); // 绘制矩形ctx.fillRect(0, 0, 50, 50)</script>
</body>

图像合成设置

在canvas中不仅可以在已有图形后面再画新图形,还可以用来遮盖指定区域,清除画布中的某些部分(清除区域不仅限于矩形,像clearRect()方法做的那样)以及更多其他操作,这里就其做一个简单的概述,globalCompositeOperation = type 这个属性设定了在画新图形时采用的遮盖策略,其值是一个标识 12 种遮盖方式的字符串。这里给出其简易的示例代码:

source-over:这是默认设置,并在现有画布上下文之上绘制新图形。

source-in:新图形只在新图形和目标画布重叠的地方绘制。其他的都是透明的。

<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制矩形ctx.fillStyle = "rgba(0, 0, 255, 1)"; // 蓝色ctx.fillRect(300, 200, 100, 100)ctx.globalCompositeOperation='source-in'; // 蓝色和红色重叠部分的红色区域ctx.fillStyle = "rgba(255, 0, 0, 1)"; // 红色ctx.fillRect(250, 150, 100, 100)</script>
</body>

source-out:在不与现有画布内容重叠的地方绘制新图形。

source-atop:新图形只在与现有画布内容重叠的地方绘制。

destination-over:在现有的画布内容后面绘制新的图形。

destination-in:现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。

destination-out:现有内容保持在新图形不重叠的地方。

destination-atop:现有的画布只保留与新图形重叠的部分,新的图形是在画布内容后面绘制的。

lighter:两个重叠图形的颜色是通过颜色值相加来确定的。

copy:只显示新图形。

当然还有一些其他的属性,如下供大家参考:

xor:图像中,那些重叠和正常绘制之外的其他地方是透明的。

multiply:将顶层像素与底层相应像素相乘,结果是一幅更黑暗的图片。

screen:像素被倒转,相乘,再倒转,结果是一幅更明亮的图片。

overlay:multiply和screen的结合,原本暗的地方更暗,原本亮的地方更亮。

darken:保留两个图层中最暗的像素。

lighten:保留两个图层中最亮的像素。

color-dodge:将底层除以顶层的反置。

color-burn:将反置的底层除以顶层,然后将结果反过来。

hard-light:屏幕相乘(Acombinationofmultiplyandscreen)类似于叠加,但上下图层互换了。

soft-light:用顶层减去底层或者相反来得到一个正值。

difference:一个柔和版本的强光(hard-light)。纯黑或纯白不会导致纯黑或纯白。

exclusion:和difference相似,但对比度较低。

hue:保留了底层的亮度(luma)和色度(chroma),同时采用了顶层的色调(hue)。

saturation:保留底层的亮度(luma)和色调(hue),同时采用顶层的色度(chroma)。

color:保留了底层的亮度(luma),同时采用了顶层的色调(hue)和色度(chroma)。

luminosity:保持底层的色调(hue)和色度(chroma),同时采用顶层的亮度(luma)。

接下来我们可以借助globalCompositeOperation属性中的destination-out属性值实现一个类似刮刮卡的效果出来,具体的代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#ggk {width: 600px;height: 400px;font-size: 30px;font-weight: 900;text-align: center;line-height: 400px;overflow: hidden;position: absolute;left: 0;top: 0;z-index: -1;}</style>
</head>
<body><div id="ggk">谢谢回顾</div><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 是否可以挂卡let isDraw = false// 设置canvas背景let img = new Image();img.src = "./1.jpg"img.onload = function () {ctx.drawImage(img, 0, 0, 600, 400)}// 鼠标按下去可以挂卡canvas.onmousedown = function () {isDraw = true}// 鼠标抬起可以停止挂卡canvas.onmouseup = function () {isDraw = false}// 鼠标移动可以挂卡canvas.onmousemove = function (e) {if (isDraw) {let x = e.pageXlet y = e.pageYctx.globalCompositeOperation = "destination-out";ctx.arc(x, y, 20, 0, 2 * Math.PI)ctx.fill()}}// 随机中奖事件let random = Math.random()if (random < 0.1) {let ggkDiv = document.getElementById("ggk");ggkDiv.innerHTML = "恭喜你中奖了"}</script>
</body>
</html> 

最终呈现的效果如下所示:

裁剪路径设置

裁切路径和普通的canvas图形差不多,不同的是它的作用是遮罩,用来隐藏不需要的部分,如果和上面介绍的globalCompositeOperation属性作一比较,它可以实现与source-in和source-atop差不多的效果。最重要的区别是裁切路径不会在canvas上绘制东西,而且它永远不受新图形的影响。这些特性使得它在特定区域里绘制图形时相当好用。给出如下代码示例:

<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");let chatPath = new Path2D();chatPath.moveTo(200, 300)chatPath.quadraticCurveTo(150, 300, 150, 200); chatPath.quadraticCurveTo(150, 100, 300, 100); chatPath.quadraticCurveTo(450, 100, 450, 200); chatPath.quadraticCurveTo(450, 300, 250, 300); chatPath.quadraticCurveTo(250, 350, 150, 350); chatPath.quadraticCurveTo(200, 350, 200, 300); ctx.clip(chatPath) // 裁剪路径// 获取图片let img = new Image();img.src = "./1.jpg";img.onload = function () {ctx.drawImage(img, 0, 0, 600, 400)ctx.lineWidth = 5 // 路径的宽度ctx.stroke(chatPath) // 显示路径,可有可无}</script>
</body>

最终呈现的效果如下所示:

状态保存与恢复

canvas的状态就是当前画面应用的所有样式和变形的一个快照,save和restore方法是用来保存和恢复canvas状态的且都没有参数,canvas状态存储在栈中,每当save()方法被调用后,当前的状态就被推送到栈中保存。示例代码如下所示:

<body><canvas id="canvas" width="800" height="800"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制矩形ctx.fillStyle = "red";ctx.fillRect(0, 0, 100, 100);ctx.save() // 保存当前状态ctx.fillStyle = "green";ctx.fillRect(100, 100, 100, 100);ctx.save() // 保存当前状态ctx.fillStyle = "blue";ctx.fillRect(200, 200, 100, 100);ctx.save() // 保存当前状态ctx.fillStyle = "yellow";ctx.fillRect(300, 300, 100, 100);ctx.restore() // 恢复到上一次保存的状态ctx.fillRect(400, 400, 100, 100) // 蓝色ctx.restore() // 恢复到上一次保存的状态ctx.fillRect(500, 500, 100, 100) // 绿色</script>
</body>

最终呈现的效果如下所示:

像素操作设置

在canvas中我们可以直接通过ImageData对象操纵像素数据,直接读取或将数据数组写入该对象中,接下来我们开始讲解如何控制图像使其平滑(反锯齿)以及如何从canvas画布中保存对象。在让一些代码中我们通过getImageData获取图片的像素数据,并对其进行像素数据的修改,如下:

<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");      // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 获取图片let img = new Image();img.src = "./1.jpg"img.onload = function () {ctx.drawImage(img, 0, 0, 600, 400);let imageData = ctx.getImageData(0, 0, 600, 400) // 获取图片信息数组for (let i = 0; i < imageData.data.length; i+=4) {// 计算当前像素的平均值let avg = (imageData.data[i] + imageData.data[i+1] + imageData.data[i+2]) / 3;imageData.data[i] = avgimageData.data[i + 1] = avgimageData.data[i + 2] = avgimageData.data[i + 3] = 255}// 将修改后的数据呈现渲染到画布上ctx.putImageData(imageData, 0, 0)}</script>
</body>

最终呈现的效果如下所示,可以看到我们对原本的图片实现了一个复古效果的展示:

如果想实现对图片像素的反向操作,可以设置如下的代码进行实现:

呈现的效果如下所示: 

如果想绘制从某一坐标到另一坐标的的区域位置的话,可以通过如下代码操作:

ctx.putImageData(imageData, 0, 0, 300, 200, 600, 400)

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

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

相关文章

软件源码购买一般在哪个网站?避坑指南

在数字化转型的浪潮中&#xff0c;软件源码的购买已成为许多企业和个人开发者快速搭建项目、节省开发成本的重要途径。选择合适的购买平台&#xff0c;不仅能确保源码的质量与合法性&#xff0c;还能享受到便捷的交易流程与专业的售后服务。本文小编将为您分享几个常见的软件源…

安卓 APK 安装过程详解

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Android ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 1. 开机后连上网线 2. 查看网线的IP地址 3. 检查ADB连接 4. 修改文件权限 步骤 结语 我的其他博客 前言 在安卓设备上安装…

[PM]流程与结构设计

流程图 流程就是为了达到特定目标, 进行的一系列有逻辑性的操作步骤, 由两个及已上的步骤, 完成一个完整的行为过程, 即可称为流程, 流程图就是对这个过程的图形化展示 分类 业务流程图 概念: 描述业务流程的一种图, 通过特定符号和连线表示具体某个业务的处理步骤和过程作…

✈️一文带你入门【NestJS】

✈️引言 在现代Web开发领域&#xff0c;框架和技术的迭代速度令人咋舌。其中&#xff0c;NestJS作为一款基于Node.js的后端框架&#xff0c;以其卓越的设计理念和强大的功能集&#xff0c;迅速吸引了众多开发者的眼球。本文将带你深入了解NestJS的起源、发展&#xff0c;以及…

亚马逊自养号测评环境系统全解析:从注册到下单,一次成号无忧

亚马逊测评的关键在于养号&#xff0c;这是因为测评需要买家账号来操作。而养号不仅仅是让账号能多次使用&#xff0c;更重要的是通过维护让账号更健康、更有价值。很多人容易忽略的是&#xff0c;首次购买&#xff08;首单&#xff09;的成功率和它对账号的重要性。首单成功率…

Leetcode刷题——7 滑动窗口 双指针

注&#xff1a;以下代码均为c 1. 两数之和2&#xff08;输入有序数组&#xff09; // 法1&#xff1a;暴力 vector<int> twoSum1(vector<int>& numbers, int target) {vector<int> ans(2);int n numbers.size();for(int i 0; i < n-1; i){if(i ! 0…

递归(四)—— 初识暴力递归之“打印字符串的全排列”

题目1&#xff1a;序列打印一个字符串的全排列 题目分析&#xff1a;结合一实例来理解题目&#xff0c;str “abc”的全排列就是所求得的序列是 strp[0~2]的所有位的排列组合&#xff0c;strNew {“abc”, “acb”, “bac”, “bca”,”cba”,”cab”} 思路1&#xff1a;枚…

旷野之间3 – CTO 应具备的技能

​​​​​​ 随着技术渗透到商业的各个方面,首席技术官的角色变得越来越具有战略性和多面性。虽然深厚的技术技能仍然是基础,但今天的首席技术官还需要具备领导能力、商业敏锐度、沟通能力等优势。 根据我作为 CTO 的个人经验,我将深入探讨现代 CTO 所需的各种能力,包括:…

vue中,图片在div中按照图片原来大小等比例显示

图片在div中按照图片原来大小等比例显示&#xff0c;可以保证web上显示的图片和实际图片形状一样&#xff0c;保留原始图片效果 实现代码如下&#xff1a; <div style"padding: 0; width:400px;height:400px;position: absolute;border: 1px solid #eff2f6;">…

百问网全志D1h开发板红外控制LVGL界面切换

红外控制LVGL界面切换 1. 测试红外功能 1.1 配置设备树 查看原理图&#xff1a; 可以看到红外对应的引脚号是PG16。 进入目录&#xff1a; cd /home/ubuntu/tina-d1-h/device/config/chips/d1-h/configs/nezha/linux-5.4修改board.dts&#xff1a; vim board.dts修改引…

MUNIK解读ISO26262:安全计划

前言 当我们进行功能安全开发时&#xff0c;由于整个项目周期和内容较多&#xff0c;因此需要在项目前期对一些问题提前进行规划&#xff1a;比如功能安全开发具体分为几个阶段&#xff0c;应该怎么去做&#xff1f;对于不同的环节&#xff0c;有哪些人员来执行&#xff1f;资…

在网上申请流量卡审核失败,可能是你的年龄有问题!

在网上申请流量卡审核失败&#xff0c;可能是你的年龄有问题&#xff01; 先上个图&#xff1a; ​ 网上的流量卡并不是随意申请的&#xff0c;而是填写申请信息后由运营商进行审核&#xff0c;审核通过后才会发卡&#xff0c;如果你提交的订单没有审核通过&#xff0c;那么大…

体积大的快递怎么寄便宜?如何寄件寄包裹更省钱?

大学毕业了&#xff0c;面对即将到来的工作生活&#xff0c;小李不得不把宿舍里的大包小包打包寄回家。可是&#xff0c;当他真正开始打包行李时&#xff0c;才发现这可不是一件简单的事&#xff1a;衣服、被子、书籍、杂物……这些东西加起来体积不小&#xff0c;想要省钱寄快…

HippoRAG如何从大脑获取线索以改进LLM检索

知识存储和检索正在成为大型语言模型(LLM)应用的重要组成部分。虽然检索增强生成(RAG)在该领域取得了巨大进步&#xff0c;但一些局限性仍然没有克服。 俄亥俄州立大学和斯坦福大学的研究团队推出了HippoRAG&#xff0c;这是一种创新性的检索框架&#xff0c;其设计理念源于人类…

强化学习实战1:OpenAI Gym 实验环境介绍

环境配置 我的 torch 版本是 2.3.0&#xff0c;然后 gym 版本是 0.22.0&#xff0c;python 版本是 3.8 &#xff0c;pygame 版本是 2.6.0 。 首先安装一下 gym&#xff1a; pip install gym0.22.0 -i https://pypi.tuna.tsinghua.edu.cn/simple然后安装一下 pygame&#xff…

Linux服务器CPU占用率达到100%排查思路

1、找到最耗CPU的进程pid&#xff0c;执行命令 top 2、找到最耗CPU的线程tid // 执行 top -Hp [pid] 定位应用进程对应的线程 tid // 按shift p 组合键&#xff0c;按照CPU占用率排序 > top -Hp 14246 3、将线程pid转化为16进制 // printf "%x\n" [tid] 将tid…

Sharding-JDBC分库分表之SpringBoot主从配置

Sharding-JDBC系列 1、Sharding-JDBC分库分表的基本使用 2、Sharding-JDBC分库分表之SpringBoot分片策略 3、Sharding-JDBC分库分表之SpringBoot主从配置 前言 在开发中&#xff0c;如果对数据库的读和写都在一个数据服务器中操作&#xff0c;面对日益增加的访问量&#x…

无法找到模块“@wangeditor/editor-for-vue”的声明文件

vue3项目中使用wangeditor/editor遇到的问题 开发环境不管红线报错正常使用 打包的时候就会报错了 1.安装依赖 pnpm install --save wangeditor/editor wangeditor/editor-for-vuenext 2.遇到的问题 3.解决方法 在src目录下面创建 wangeditor-types.d.ts 文件 代码如下 de…

【ai_agent】从零写一个agent框架(五)基于egui制作一个agent/workflow在线编辑器

前言 上篇我们实现了基础节点&#xff0c;并暴露出grpc服务&#xff0c;但是手动编辑文本制作一个workflow实在强人所难。 所以本文我们做个webui自动生成workflow。 开搞之前先看看别人怎么做的。 Dify 的ui 效果如下图示&#xff1a; 支持多种功能节点 但只能打开一个节…