canvas学习笔记,实现刮刮卡、在线签名、图片涂抹裁切

起初在boss上看到一个面试官发题,利用canvas实现图片裁切,在学习过程中实现三个小功能,并在此基础上深入学习明白相关工具和能力。 这篇博客主要实现三个功能:刮刮卡、在线签名、图片涂抹裁切具体实现效果下图:

一、基础知识

在实现下面功能首先的先明白canvas是做什么的?能解决什么问题?

Canvas是一个HTML元素,可用于在网页上绘制图形、动画和其他可视化效果。它通过JavaScript编程接口来实现。

Canvas可以用于各种任务,包括绘制图表、创建游戏、制作交互式的用户界面等。它可以处理大量的图形和动画,并且具有良好的性能和响应速度。

Canvas的优势包括:

  1. 强大的绘图功能:Canvas提供了丰富的API,可以实现复杂的绘图操作。
  2. 高性能:由于直接操作像素,Canvas通常比其他方法(如SVG)更高效。
  3. 跨浏览器支持:Canvas是HTML5标准的一部分,得到了广泛的支持。

Canvas的一些劣势:

  1. 缺乏内置的事件处理:相比其他更高级的技术(如React或Angular),Canvas需要手动处理用户交互和事件。
  2. 不支持文本选择和复制:由于Canvas是基于像素的,无法直接选中或复制其中的文本。
  3. 难以维护和重构:由于Canvas是基于绘图指令的,代码可能会变得冗长和难以理解,尤其是对于复杂的图形和动画效果。

Canvas可以实现功能,例如:

  • 绘制动态图表和数据可视化:Canvas可以根据数据的变化实时更新图表,让用户能够更好地理解数据。
  • 创建基于Web的游戏:Canvas提供了一个绘制图形和动画的环境,使开发者能够创建复杂的游戏效果。
  • 实现自定义用户界面(banner广告):Canvas可以用于绘制自定义的用户界面元素,使应用程序有更多的创意和自由度。

二 canvas使用

1 创建画布和画笔

提供一个<canvas>标签(html)、有一个canvas对象(js-画布)、有一个context对象(js-画笔)、CanvasRenderingContex t2D。有两种声明方式: 一种直接定义canvas标签、另一种通过js创建canvas并通过dom元素添加到页面。

第一种

html 复制代码
<canvas id="c1"></canvas>
<script>
    const convas1 = document.querySelector('#c1');
    const context1 = canvas1.getContext('2d');
</script>

第二种

js 复制代码
var canvas = document.createElement("canvas");
const context = canvas.getContext('2d');
document.body.appendChild(canvas);

注:实际操作使用第二种更加友好,因为通过第二种的添加方式,可以在调用canvasAPI时候有代码提示。

一般设置画布和画笔都会做前置条件,例如:设置画布大小,画笔的粗细颜色等信息,例如:

js 复制代码
var canvas = document.createElement("canvas");
canvas.classList.add("scratcanvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext('2d');

2 画笔

画笔有两种3D和2D两种,演示功能主要使用2d画笔,所以接下来围绕2d画笔说。

2D画笔具有以下功能:

  1. 绘制形状:可以使用画笔绘制直线、曲线、多边形、矩形、圆和椭圆等基本形状。
  2. 填充和描边:可以为绘制的形状设置填充色和描边色,以实现不同的样式效果。
  3. 文字绘制:可以使用画笔在画布上绘制文本,包括设置字体、大小、对齐方式等。
  4. 图像绘制:可以将图像绘制到画布上,用于显示图片或者创建动画效果。
  5. 渐变和模式:可以创建线性渐变、径向渐变和图案等特殊效果,以增加绘制的视觉吸引力。
  6. 变换操作:可以对绘制的形状进行平移、旋转、缩放等变换操作,以实现更复杂的绘图效果。
  7. 合成和透明度:可以设置不同的合成操作和透明度,从而实现图像的混合和透明效果。
  8. 图形裁剪:可以通过路径裁剪,只显示路径内的内容,实现自定义的图形遮罩效果。

具体各种用法可以看官方文档Canvas - Web API 接口参考,这里用划线举个例子: 绘制直线&折线 两天之间的连线:直线;多个直线连接 : 折线。使用ctx.moveTo(x,y) 将画笔放置到指定的坐标位置 (起始点)。使用ctx.lineTo(x,y) 从上一个点绘制直线路径到指定的点。

  • 上一个点可以是moveTo指定的点。
  • 上一个点也可以是上一次lineTo指定的点。也就是可以多个lineTo连续使用,形成折线。

3 beginPath

使用context.beginPath()方法,为不同部分的途径设置开关(设置分组)。只对紧邻这组路径进行绘制。通过这种方式,表示绘图上下文以下绘画的路径方式将不会影响之前绘画内容。

js 复制代码
 ctx.beginPath();
 ctx.moveTo(50,50);//开始绘画点
 ctx.lineTo(250,50);//经过点
 ctx.stroke();//结束直线绘制

举个例子

js 复制代码
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// 开始创建新的路径
ctx.beginPath();
// 定义路径
ctx.moveTo(50, 50);
ctx.lineTo(100, 50);
ctx.lineTo(100, 100);
// 设置路径样式
ctx.lineWidth = 2;
ctx.strokeStyle = 'red';
// 绘制路径
ctx.stroke();
// 开始创建另一个新的路径
ctx.beginPath();
// 定义另一个路径
ctx.arc(150, 75, 50, 0, Math.PI * 2);
// 设置路径样式
ctx.fillStyle = 'blue';
// 填充路径
ctx.fill();


在上述示例中,首先调用了beginPath()来开始创建第一个路径,然后绘制了一条由两个线段组成的路径,最后调用stroke()方法将路径描边。 接着,又调用了beginPath()来开始创建第二个路径,然后使用arc()方法定义了一个圆形路径,并使用fill()方法填充路径。 通过使用beginPath()方法将不同的路径分隔开来,可以确保不同路径的样式和绘制操作互不影响。 ## 4 清除画布 当画笔在画布很多内容,想清空可以调`clearRect`方法,通过调用clearRect()方法并传入相应的参数,可以在Canvas上清除特定区域的绘制内容。

clearRect()方法接受四个参数,分别是:

  1. x:矩形左上角的 x 坐标。
  2. y:矩形左上角的 y 坐标。
  3. width:矩形的宽度。
  4. height:矩形的高度。
js 复制代码
context4.closePath()
context4.clearRect(0,0,width,height);

5 引入图像

在画布上引入图像的方式需要通过

  • Image对象 对应img标签。
    • 可以是图片的路径
    • 图片的base64表示

通过drawImage将image塞入到canvas组件中,绘制图像参数:

  • image:要绘制的图像。
  • x:目标图像左上角的 x 坐标。
  • y:目标图像左上角的 y 坐标。
  • width:目标图像的宽度。
  • height:目标图像的高度。
js 复制代码
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
  let preimg = new Image();
  preimg.src = "./sub.png";
   preimg.onload = function(){
    ctx.drawImage(preimg,0,0,width,height);
  }  

6 图像合成

图像合成是指在Canvas中将多个图像或者样式叠加在一起,从而创建出复杂的绘图效果。这个就是canvas能够实现很多复杂多变效果的方式,目前还没有研究透彻。 主要是利用是globalCompositeOperation属性。该属性用于设置绘制操作时的合成模式,决定了新的绘制内容如何与已有的内容进行合成。 列举一些 globalCompositeOperation属性可以设置为不同的值来实现不同的合成效果。以下是常见的一些值:

  • source-over(默认):新的绘制内容会覆盖在已有内容上方。
  • source-in:新的绘制内容只会覆盖与已有内容重叠的部分,并保留其他部分的透明度。
  • source-out:新的绘制内容只会覆盖与已有内容不重叠的部分,并保留其他部分的透明度。
  • source-atop:新的绘制内容会覆盖与已有内容重叠的部分,并保留其他部分的透明度。
  • destination-over:新的绘制内容会在已有内容下方进行绘制。
  • destination-in:新的绘制内容只会在与已有内容重叠的部分显示,并保留其他部分的透明度。
  • destination-out:新的绘制内容只会在与已有内容不重叠的部分显示,并保留其他部分的透明度。
  • destination-atop:新的绘制内容会在与已有内容重叠的部分显示,并保留其他部分的透明度。
  • lighter:新的绘制内容与已有内容进行颜色相加,产生更亮的效果。
  • xor:新的绘制内容与已有内容进行异或运算。 合成效果如下图: 可以详细的使用可以看这篇帖子Canvas学习:globalCompositeOperation详解 - 方帅 - 博客园 (cnblogs.com)

二、刮刮乐效果

实现刮刮乐效果是通过两张图层进行重叠实现,一张是img或div作为结果放在底部,在上面覆盖一层canvas灰色的画布进行遮盖,当鼠标在canvas上移动则消除灰色区域从而实现刮刮卡效果

1 初始化

在页面上写一个div

html 复制代码
    <div id="card">
    </div>

接下来在card的div中绘制开奖结果div

js 复制代码
//统一宽高
const width = 400;
const height = 300;

//创建一个开奖结果
//创建画布,用户刮开则显示开奖结果
let div = document.createElement("div");
div.innerText = "一等奖"
div.className = "scrat"
document.getElementById("card").appendChild(div);

2 创建canvas

使用canvas创建绘制刮刮乐蒙版

js 复制代码
//创建一个canvas
var canvas = document.createElement("canvas");
canvas.classList.add("scratcanvas");
//设置宽高
canvas.width = width;
canvas.height = height;
//设置画笔
const context = canvas.getContext('2d');

通过画笔中的fillstyle实现将某个区域填充灰色。

js 复制代码
//画一个灰色画布遮住界面
context.fillStyle = "#EFEFEF";
context.fillRect(0,0,width,height);

再通过画笔绘制线条,之后通过线条和页面中重叠的地方进行消除,即可实现刮刮卡效果,将canvas添加到class叫scrat的div中。

js 复制代码
//画一个圆
//画笔分割
context.beginPath();
//设置画笔的粗细
context.lineWidth=20;
//转角和端点设置为圆弧
context.lineCap ="round"
context.lineJoin = "round"
document.getElementsByClassName("scrat")[0].appendChild(canvas);

3 添加鼠标事件

添加点击移动事件,主要是监听用户鼠标三个事件,1、按下鼠标左键;2、移动鼠标;3、抬起鼠标左键。具体实现通过document.addEventListener来监听mousedownmousemovemouseup三种情况,修改鼠标状态isMouseDown的true和false来实现。

新旧图层重叠部分透明是通过## 6 图像合成的方法globalCompositeOperation的参数destination-out实现新图层和旧图层重叠,则展示旧图层未被重叠的区域。

js 复制代码
//鼠标是否按下状态
var isMouseDown = false;

//鼠标按下,划线
document.addEventListener('mousedown', function(event) {
    isMouseDown = true;
//新旧图层重叠部分透明
context.globalCompositeOperation = 'destination-out';
    //封装函数,通过getCanvasPosition传入鼠标位置,获取在canvas中对应的位置
    var {x,y} = getCanvasPosition(event);
    //开始画线
    context.moveTo(x,y);
});
// 在这里可以对鼠标位置进行处理
document.addEventListener('mousemove', function(event) {
    if(!isMouseDown) return;
    var {x,y} = getCanvasPosition(event);
    context.lineTo(x,y);
    context.stroke();
});
document.addEventListener('mouseup', function(event) {
    isMouseDown = false;
});
//封装函数。计算canvas位置
function getCanvasPosition(event) {
    //获取鼠标当前位置
    var x = event.clientX;
    var y = event.clientY;
        //减去div的位置
    x -= div.offsetLeft;
    y -= div.offsetTop;

    //减去canvas的位置
    x -= canvas.offsetLeft;
    y -= canvas.offsetTop;
    return {x,y};
}

最后再调整下页面的样式,通过子绝父相的css来实现重叠。

css 复制代码
      .scrat  {
        width: 400px;
        height: 300px;
        text-align: center;
        line-height: 300px;
        font-size: 64px;
        position: relative;
        border: 1px solid #000;
        }
        .scratcanvas{
            position: absolute;
            top: 0;
            left: 0;
             border: 1px solid #000;
        }

4 最终效果

三、电子签字组件

当会了刮刮卡效果后可以在此基础上实现电子签名的功能,同样是使用画笔的功能,同样可以先创建一个div,并添加三个按钮

html 复制代码
<div>
        <div class="sign">
            <div>
                
                <button id="save">开始</button>
                <button id="anew">重签</button>
                <button id="eraser">橡皮擦</button>
            </div>
        </div>
    </div>

1 初始化

在初始化设置canvas的基本属性,例如:宽度、高度、笔触转角和端点,并设置基础参数是否开启签名状态、是否开启橡皮擦、笔粗细、笔颜色等。

js 复制代码
/**
 * 签名组件
 */
const width = 400;
const height = 300;

var isSign = false;//是否开启签名状态
var isEraser = false;//是否开启橡皮擦
var brushSize = 10;//笔粗细
var brushColor = "#000";//笔颜色

//签名组件
const draw = document.createElement("canvas");
draw.classList.add("draw");
draw.width = width;
draw.height = height-30;
draw.style.border = "1px solid #ccc";//设置边框
var temp = document.getElementsByClassName("sign")[0];
temp.appendChild(draw);

//2d画笔
var context2 = draw.getContext('2d');
context2.lineCap = "round";
context2.lineJoin = "round";
context2.lineWidth = brushSize;

2 鼠标事件

封装一个画笔事件drawEvent(),之前通过document.addEventListener来监听mousedownmousemovemouseup三种,其实也可以通过组件的onmousedownonmousemoveonmouseup实现:1、按下鼠标左键;2、移动鼠标;3、抬起鼠标左键的事件监听,原理像常用的onclick一样。

由于是签字所以会涉及两个状态,isSign(是否开启签名状态)、isEraser(是否开启橡皮擦)通过这两个状态来控制是否清除画布。

最后鼠标抬起,则清空鼠标移动和抬起事件,当用户再次按下鼠标则再次循环执行该事件,具体实现可以看下列详细代码。

js 复制代码
//调用画笔事件方法
drawEvent() ;
//封装画笔事件
function drawEvent() {
    draw.onmousedown = function(event) {
        //判断是否开启签字
        if (!isSign) return;
        // 判断是签字还是橡皮擦
        if (isEraser) {
            // 如果橡皮擦则调用destination-out进行覆盖清除画布
            context2.globalCompositeOperation = 'destination-out';
        }else{
            //如果是签字则让签字的内容进行重叠显示
            context2.globalCompositeOperation = 'source-over';
        }    
        context2.beginPath();
        //开始绘画
        context2.moveTo(event.offsetX,event.offsetY);
        设置点击事件
        draw.onmousemove = function(event) {
            //判断是否用橡皮擦
            if (isEraser) {
                // 设置橡皮擦粗细
                context2.lineWidth = brushSize; 
            }else{
                //设置签字颜色
                context2.strokeStyle = brushColor;
            }
            //绘画路径坐标
            context2.lineTo(event.offsetX,event.offsetY);
            //结束绘画
            context2.stroke();
        }
        //当鼠标左键抬起,则将移动事件和抬起事件设置为空
        draw.onmouseup = function(event) {
            draw.onmousemove = null;
            draw.onmouseup = null;
        }
    }
}

3 开启or保存下载

由于上面封装画笔事件所以可以改变画笔状态调用drawEvent()方法即可,给开启签字添加相关事件

js 复制代码
//开启签字,或关闭签字
document.getElementById("save").onclick = function(){
    isSign = !isSign
    if (isSign){
        //开始签名
        // console.log("开始签名");
        document.getElementById("save").innerText = "保存";
    }else{
        //保存,生成png图片
        // console.log("保存");
        document.getElementById("save").innerText = "重新";
        
        download(draw.toDataURL(),"签名.png");
        //设置初始值
        isEraser = false;//关闭橡皮擦
    }
}

在上面download(draw.toDataURL(),"签名.png")保存后调用download下载方法,并传入了canvas当前画部的数据和导出名字draw.toDataURL()是获取一个base64的图片数据,这时如何进行下载?可以通过调用a标签实现图片下载。

js 复制代码
//封装a标签下载组件
function download(data64, fileName) {
    console.log();
    let a = document.createElement("a");
    a.href = data64;
    a.download = fileName;
    //将a标签添加在页面上
    document.body.append(a);
    //下载
    a.click();
    //下载完成移除a标签
    a.remove();
}

4 清空画布

清空画布主要调用画笔的clearRect方法,他可以清空指定区域的内容。只需要设置好坐标位置即可。

js 复制代码
//清空画布内容
document.getElementById("anew").onclick = function(){
    if (!isSign) return;
    //清空画布先结束之前操作
    context2.closePath()
    console.log("清空画布内容");
    context2.clearRect(0,0,width,height);
    context2.beginPath();
    //设置橡皮擦
    isEraser = false;
    drawEvent();
}

5 橡皮擦·

由于之前封装好了画笔事件,现在只需要改变isEraser状态,并重新调用画笔事件drawEvent()

js 复制代码
//橡皮擦功能
document.getElementById("eraser").onclick = function(){
    if (!isSign) return;
    isEraser =!isEraser;
    //重新调用画笔事件
    drawEvent();
}

6 最终效果

四、图板涂抹截取

这个demo主要是想实现涂抹某个区域然后对其进行截取为单独图片,他的难点在于理解canvas画笔每个坐标点位与像素的之间关系,和透明颜色相关知识。

1 透明颜色概念

透明的含义是"看得透"。在计算机图形学中,透明通常用来描述某一部分的图像或者界面是可以看透的。例如,我们可以使用透明效果来实现深色图像的遮盖以及透视效果的创建。

透明度通常用一个0到1之间的实数值来表示,0表示完全透明,1表示完全不透明。

理解RGB色彩模式

RGB色彩模式是一种基于三原色的表示法,其中R表示红色,G表示绿色,B表示蓝色。通过混合不同比例的三原色,可以得到不同的颜色。透明度通常由一个透明度值表示,这个值也是一个0到1的实数。

下面是一个使用RGB表示法表达透明颜色的例子:

css 复制代码
background: rgba(255, 0, 0, 0.5);

在上面的例子中,我们使用rgba()函数来表示一个透明度为0.5的红色背景。其中,"a"表示alpha channel,也就是透明度通道。

用刚才导出图片为例子,可以看出透明区域的rgba和写了文字的rgba是通过最后一个a值判断,透明区域的rgba(0,0,0,0) 而有文字区域rgba(0,0,0,1)可以明确一点最后一个值肯定 a>0

2 涂抹截取原理

涂抹截取原理原理主要是两步: 第一步设置一张背景和一个canvas,在canvas上面涂抹会生成一个图像数组,这个图像数据会记录下每个rgba点位信息。 第二步对比,将img图片中的每个像素点和canvas上的比较,如果canvas上的点位不为空,则保留img图片上的像素点,这样得到一个新的图像数组,将其导出到另外地方可以看到效果。

3 实现代码

1、初始化

先声明两个div,一个用来操作实现【涂抹】【重置】【橡皮擦】功能,另一个则点击【导出】展示最终结果。

html 复制代码
    <div>
        <div class="shear">
            <div>
                <button id="derive">导出</button>
                <button id="dedaub">涂抹</button>
                <button id="shanew">重置</button>
                <button id="sheraser">橡皮擦</button>
            </div>
        </div>
    </div>
    <div>
        <div class="prectemp">
            <div style="height: 22.81px;">
                截取涂抹内容
            </div>
            <div id="sheartemp">
            </div>
        </div>

    </div>

按照电子签字组件实现逻辑先声明两个变量保存是否开启涂抹状态、是否开启橡皮擦和宽度高度等基本信息。

js 复制代码
/**
 * 涂抹截取
 * 1、创建图片作为背景;
 * 
 */
var isDaub = false;// 是否开启涂抹状态
var isErase = false;//是否开启橡皮擦
const width = 400;
const height = 300;

创建一个img图层放在页面class为shear的位置上,同时创建canvas图层也放在页面class为shear的位置上。

js 复制代码
const img = document.createElement("img");
img.src = "./21092414350ML0-0-lp-350.jpg";
img.classList.add("daub");
img.width =width;
img.height = height-30;
document.getElementsByClassName("shear")[0].appendChild(img);


const daub = document.createElement("canvas");
daub.classList.add("daub");
daub.width = width;
daub.height = height-30;
document.getElementsByClassName("shear")[0].appendChild(daub);

设置画笔基本属性,因为这次是在img上涂抹,所以需要将画笔颜色设置为半透明,这样才不会完全遮盖图片context4.strokeStyle = "rgb(232 149 38/ 50%)

js 复制代码
//2d画笔
var context4 = daub.getContext('2d');
context4.lineCap = "round";
context4.lineJoin = "round";
context4.lineWidth = 20;
context4.strokeStyle = "rgb(232 149 38/ 50%)"; //设置透明渡的颜色选取内容

2、鼠标事件

这里涂抹是橡皮擦实现原理和签字组件类似,封装一个画笔事件drawEvent(),之前通过组件的onmousedownonmousemoveonmouseup实现:1、按下鼠标左键;2、移动鼠标;3、抬起鼠标左键的事件监听,使用方法如常用的onclick一样。 根据两个状态,isDaub(是否开启涂抹状态)、isErase(是否开启橡皮擦)通过这两个状态来控制是否清除画布。 最后鼠标抬起,则清空鼠标移动和抬起事件,当用户再次按下鼠标则再次循环执行该事件,具体实现可以看下列详细代码。

js 复制代码
/**
 * 涂抹/橡皮擦事件
 * 
 */
daubEvent();
function daubEvent() {
    daub.onmousedown = function(event) {
        if (!isDaub) return;
        // 开始新的路径或继续现有路径
        if (isErase) {
            context4.globalCompositeOperation = 'destination-out';
        }else{
            context4.globalCompositeOperation = 'source-over';
        }  
        context4.beginPath();
        context4.moveTo(event.offsetX,event.offsetY);
        daub.onmousemove = function(event) {
            if (isErase) {
                // 设置橡皮擦粗细
                context4.lineWidth = 20; //设置字体10号
                context4.strokeStyle = "rgba(0, 0, 0, 1)";
                context4.globalAlpha = 1;
            }else{
                context4.strokeStyle = "rgb(232 149 38/ 75%)"; //设置透明渡的颜色选取内容
                context4.lineWidth = brushSize; //设置字体10号
                context4.globalAlpha = 0.01;
            }
            context4.lineTo(event.offsetX,event.offsetY);   
            context4.stroke();  
        }
        daub.onmouseup = function(event) {
            daub.onmousemove = null;
            daub.onmouseup = null;
        }
    }
}

涂抹、重置、橡皮擦功能同样和电子签字一致,由于刚才已经封装了点击事件,只需要通过控制isDaub和isErase两个状态来管理画布如何绘画。

js 复制代码
//点击【开始】可以进行涂抹
document.getElementById("dedaub").onclick = function(){
    isDaub = !isDaub
    if (isDaub){
        document.getElementById("dedaub").innerText = "停止";
    }else{
        document.getElementById("dedaub").innerText = "涂抹";
        isErase = false;//关闭橡皮擦
    }
}

//点击【重置】清空画布内容
document.getElementById("shanew").onclick = function(){
    if (!isDaub) return;
    //清空画布先结束之前操作
    context4.closePath()
    context4.clearRect(0,0,width,height);
    context4.beginPath();
    //设置橡皮擦
    isErase = false;
    daubEvent();
}
//点击【橡皮擦】可以进行橡皮擦
document.getElementById("sheraser").onclick = function(){
    if (!isDaub) return;
    isErase =!isErase
    if (isErase){
        document.getElementById("sheraser").innerText = "画笔";
    }else{
        document.getElementById("sheraser").innerText = "橡皮擦";
    }
    console.log("橡皮擦",isErase);
    daubEvent();
}

3、导出图片

js 复制代码
//点击【导出】创建一张图片,循环图片rgb不透明的点,将原图内容放在这个位置
document.getElementById("derive").onclick = function(){
    console.log(document.querySelector("#newCanvas"));
    //判断是否生成过导出结果,存在则移除
    document.querySelector("#newCanvas") && document.querySelector("#newCanvas").remove();
    //调用contrast方法重新生成导出结果
    contrast();
}

将img按照canvas结果进行重新绘制需要裁切以下步骤:

  1. 获取img图片、canvas图层
  2. 创建一个新的canvas图层newcanvas,声明一个newcontext新图层,画笔将img绘制在上面
  3. 获取旧的canvas图层像素转成rgba的数组 context4.getImageData(0, 0, width, height).data
  4. 通过canvas图层长宽进行循环,可以得到每个像素点
  5. 由于每个像书点包含四个元素:红色通道绿色通道蓝色通道透明度 ,所以可以计算出每个点位的rgba值下标index = (y * width + x) * 4;
  6. 判断像素是否为主体区域(透明度大于0),如果等于0则设置为透明颜色。
  7. 最终完成newcanvas渲染到页面上,实现图片根据涂抹内容裁剪渲染
js 复制代码
/**
 * 根据图层点位,将图片内容绘制到新的canvas上
 * text4data 旧图层的内容 (用户涂抹所有内容,即rgb不透明)获取所有像素的
 * newCanvas 创建新的图层
 * newcontext 新图层画笔
 * 1、新图层newcontext引入需要裁剪的图片
 * 2、图片加载完成,循环旧图层每个像素点,
 * 3、判断text4data旧图层像素的是否透明,如果透明则将新图层对应内容区域也修改透明
 */
function contrast() {
    const text4data = context4.getImageData(0, 0, width, height).data;
    // 创建一个新的canvas图层
    const newCanvas = document.createElement("canvas");
    newCanvas.id = "newCanvas";
    const newcontext = newCanvas.getContext('2d');
    // 设置Canvas的大小与图片一致
    newCanvas.width = width;
    newCanvas.height = height-30;
    const newimg  = new Image();
    newimg.src = "./21092414350ML0-0-lp-350.jpg";
    // 将图片绘制到Canvas上
    newcontext.drawImage(newimg, 0, 0,width,height);
    const imageData   = newcontext.getImageData(0, 0, width, height);
    const newdata = imageData.data;
    
    newimg.onload = function () {
        //循环每个像素点,如果是白色则  将其设为透明
        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
            const index = (y * width + x) * 4;
    
            const text4a = text4data[index + 3];
    
                // 判断像素是否为主体区域(透明度大于0)
                if (text4a > 0 ) {
                    // const r = newdata[index];
                    // const g = newdata[index + 1];
                    // const b = newdata[index + 2];
                    // const a = newdata[index + 3];
                }else{
                    newdata[index] = 0;   // 红色通道
                    newdata[index + 1] = 0; // 绿色通道
                    newdata[index + 2] = 0; // 蓝色通道
                    newdata[index + 3] = 0; // 透明度
                }
            }
        }
        // newdata = newcontext.getImageData(0, 0, width, height).putImageData(tempDate);
        newcontext.putImageData(imageData, 0, 0);

    }
    document.getElementById("sheartemp").appendChild(newCanvas);
  }

4 最后效果

相关推荐
wordbaby几秒前
搞不懂 px、dpi 和 dp?看这一篇就够了:图解 RN 屏幕适配逻辑
前端
程序员爱钓鱼2 分钟前
使用 Node.js 批量导入多语言标签到 Strapi
前端·node.js·trae
鱼樱前端3 分钟前
uni-app开发app之前提须知(IOS/安卓)
前端·uni-app
V***u4534 分钟前
【学术会议论文投稿】Spring Boot实战:零基础打造你的Web应用新纪元
前端·spring boot·后端
i听风逝夜42 分钟前
Web 3D地球实时统计访问来源
前端·后端
iMonster1 小时前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢1 小时前
antd渐变色边框按钮
前端
元直数字电路验证1 小时前
Jakarta EE Web 聊天室技术梳理
前端
wadesir1 小时前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛1 小时前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端