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 最后效果

相关推荐
zqx_713 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己30 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2341 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai2 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端