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.pageX
                let y = e.pageY
                ctx.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] = avg
                imageData.data[i + 1] = avg
                imageData.data[i + 2] = avg
                imageData.data[i + 3] = 255
            }
            // 将修改后的数据呈现渲染到画布上
            ctx.putImageData(imageData, 0, 0)
        }
    </script>
</body>

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

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

呈现的效果如下所示: 

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW优化氢燃料电池

太阳能和风能的发展引入了许多新的能量储存方法。随着科技的发展&#xff0c;能源储存和需求平衡的方法也需要不断创新。智慧城市倡导放弃石化化合物&#xff0c;采用环境友好的发电和储能技术。氢气系统和储存链在绿色能源倡议中起着关键作用。然而&#xff0c;氢气密度低&…

git为文件添加可执行权限

查看文件权限 git ls-files --stage .\SecretFinder.py100644 表示文件的所有者有读取和写入权限 添加可执行权限 git update-index --chmod x .\SecretFinder.py再次查看文件权限 git ls-files --stage .\SecretFinder.py100755 表示文件的所有者有读取、写入和执行权限

记录|C#安装+HslCommunication安装

记录线索 前言一、C#安装1.社区版下载2.VS2022界面设置 二、HslCommunication安装1.前提2.安装3.相关文件【重点】 更新记录 前言 初心是为了下次到新的电脑上安装VS2022做C#上机位项目时能快速安装成功。 一、C#安装 1.社区版下载 Step1. 直接点击VS2022&#xff0c;跳转下…

Python基于you-get下载网页上的视频

​ 1.python 下载地址 下载 : https://www.python.org/downloads/ 2. 配置环境变量 配置 python_home 地址 配置 python_scripts 地址 在path 中加入对应配置 3. 验证 ​ C:\Users>python --version Python 3.12.4C:\Users>wheel version wheel 0.43.04. 下载 c…

JS之防抖和节流

防抖 (debounce) 所谓防抖&#xff0c;就是指触发事件后在 n 秒内函数只能执行一次&#xff0c;如果在 n 秒内又触发了事件&#xff0c;则会重新计算函数执行时间。 ps: 重置普攻&#xff0c;百度翻译要输完停止一定时间后才翻译。 没有防抖和节流的缺点&#xff1a; 函数触发…

一天20MW!天途推出无人机全自主光伏巡检平台

01 光伏电站的运维挑战 光伏发电为人类提供了可持续的清洁能源供给。一般集中式电站建设在空旷的地区&#xff0c;如荒地、沙漠等地区&#xff1b;分布式电站建设在用户的屋顶和建筑物表面&#xff0c;如住宅、商业建筑、工业厂房等地区。 随着光伏电站的大规模的使用&#x…

解决:WPS,在一个表格中,按多次换行,无法换到下一页

现象&#xff1a;在一个表格里面&#xff0c;多次按下回车&#xff0c;始终无法到下一页 解决方法&#xff1a;右击—>表格属性—>选择行—>勾选 允许跨页断行 效果演示 对比展示

Centos7 被停用!如何利用 Ora2Pg 将 Oracle 迁移至 IvorySQL?

在过去的社区讨论中&#xff0c;想要使用或正在使用IvorySQL的社区用户&#xff0c;经常问到Oracle 如何迁移到 IvorySQL 中&#xff0c;而且近期随着 Centos7 官方已经停止维护&#xff0c;这一变动促使了很多将 Oracle 部署在 Centos7 上的 Oracle 用户&#xff0c;开始准备 …

Qt开发 | Qt绘图技术 | 常见图像绘制 | Qt移动鼠标绘制任意形状 | Qt绘制带三角形箭头的窗口

文章目录 一、基本绘图技术介绍二、常见的18种图形、路径、文字、图片绘制三、Qt移动鼠标绘制任意形状四、Qt绘制带三角形箭头的窗口 一、基本绘图技术介绍 Qt提供了绘图技术&#xff0c;程序员可以在界面上拖动鼠标&#xff0c;或者在代码里指定参数进行绘图。 Qt绘图技术介绍…

Linux开发板(正点原子阿尔法_IMX6U)QT5.12.9交叉编译到ARM开发板(已解决)

问题记录&#xff1a;Qt下ctrlR直接构建项目&#xff0c;然后在build-01_led-Desktop_Qt_5_12_9_GCC_64bit-Debugz中将构建的执行文件&#xff0c;scp到ARM开发板下&#xff0c;发现通过指令./01_led后出现以下报错。 问题原因&#xff1a;因为Qt构建默认使用的是64bit的gcc&am…

钉钉扫码登录第三方

钉钉文档 实现登录第三方网站 - 钉钉开放平台 (dingtalk.com) html页面 将html放在 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>登录</title>// jquery<script src"http://code.jqu…

【优先级队列PriorityQueue】

目录 1&#xff0c;优先级队列 1.1 概念 2&#xff0c;优先级队列的模拟实现 2.1 堆的概念 2.2 堆的存储方式 2.3 堆的创建 2.3.1 堆的向下调整&#xff08;大根堆&#xff09; 2.3.2 建堆的时间复杂度​编辑 2.4 堆的插入与删除 2.4.1 堆的插入 2.4.2 堆的删除 3&a…

香橙派5plus上跑云手机方案二 waydroid

前言 上篇文章香橙派5plus上跑云手机方案一 redroid(带硬件加速)说了怎么跑带GPU加速的redroid方案&#xff0c;这篇说下怎么在香橙派下使用Waydroid。 温馨提示 虽然能运行&#xff0c;但是体验下来只能用软件加速&#xff0c;无法使用GPU加速&#xff0c;所有会很卡。而且…

SpringCloudAlibaba Nacos配置中心与服务发现

目录 1.配置 1.1配置的特点 只读 伴随应用的整个生命周期 多种加载方式 配置需要治理 1.2配置中心 2.Nacos简介 2.1特性 服务发现与服务健康检查 动态配置管理 动态DNS服务 服务和元数据管理 3.服务发现 1.配置 应用程序在启动和运行的时候往往需要读取一些配置信…

AI古风插画视频:成都亚恒丰创教育科技有限公司

AI古风插画视频&#xff1a;科技与传统美学的诗意交融 在数字技术的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;以其惊人的学习能力与创造力&#xff0c;正逐步渗透并重塑着艺术的边界。成都亚恒丰创教育科技有限公司其中&#xff0c;AI古风插画视频作为一股清流&a…

Windows 黑暗模式是什么意思?如何开启它?

随着计算机和移动设备的普及&#xff0c;长时间盯着屏幕已经成为现代人生活和工作的常态。为了减轻眼睛疲劳&#xff0c;并在低光环境中提供更舒适的视觉体验&#xff0c;许多操作系统和应用程序都引入了黑暗模式&#xff08;Dark Mode&#xff09;。 Windows 黑暗模式就是其中…

Xubuntu24.04之图形界面挂载硬盘(二百六十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

双端队列,双指针

思路&#xff1a; 其实很容易想到是双指针或者双端队列。 我们设置一个type表示当前区间已经有了多少种厨师&#xff0c;同时还需要记录区间中每个元素出现的次数&#xff0c;然后比较棘手的是移动问题了&#xff0c;什么时候移动呢&#xff1f; 我们可以发现当区间当队头元…

静脉分割YOLOV8-SEG

静脉分割&#xff0c;YOLOV8*SEG资源-CSDN文库 首先使用YOLOV8-SEG训练&#xff0c;得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV的DNN调用&#xff0c;从而摆脱PYTORCH依赖&#xff0c;支持C,PYTHON,ANDROID调用

STM32G474使用HRTIM触发多路ADC采样,通过DMA传输,通过串口打印显示,实现PWM中间时刻采样,避免开关噪声

本工程使用CUBEIDE进行配置以及编译调试&#xff0c;使用的硬件为STM32G474官方开发板NUCLEO-G474RE CUBEIDE配置 HRTIM配置 本章工程使用HRTIM定时器进行ADC的触发&#xff0c;打开主定时器&#xff0c;子定时器A,B,C。&#xff08;本工程未使用到A与C定时器&#xff0c;配置…