Canvas实现PC端刮刮卡和生成四位随机验证码

一、先来实现刮刮卡

先来看效果图

在这里插入图片描述

思路:采用absolute定位用一个canvas元素完全覆盖住你想刮开卡后想显示的内容,采用画布的globalCompositeOperation:destination-out属性来实现在被鼠标划过的地方的画布部分变得透明,画布透明之后便可以看到被画布覆盖的内容。从而实现刮刮卡效果。
1.所需知识点

1.获得操作canvas的对象

getContext():获得用于在画布上绘图的对象。

2.getImageData(x,y,width,height)

定义:getImageData()方法返回ImageData对象,该对象拷贝了画布指定矩形的像素数据,该对象不是一张
图片,而是保存了画布中数据的对象。
参数:
  1.x、y分别表示开始复制的左上角的位置坐标。
  2.width、height分别表示要复制的矩形区域的宽度、高度。
  
对于ImageData对象中的每个像素,都存放着四个消息:
	R-红色(0-255)
	G-绿色(0-255)
	B-蓝色(0-255)
	A-alpha通道(0-255;0是透明,255是完全可见的)
注意:color/alpha信息以数组形式存在,存储在ImageData对象的data属性中。

3.ImageData对象的属性

1.width:返回ImageData对象的宽度。
2.height:返回ImageData对象的高度。
3.data:返回一个对象,其包含指定的ImageData对象的图像数据。

4.合成(重点)

1.globalCompositeOperation:设置或返回源(新的)图像如何绘制到目标(已有的)图像上。
	源图像:您打算放置到画布上的绘图。
	目标图像:您已经放置在画布上的绘图。
	destination-out:在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
	定义很抽象,看下面图很容易理解。

在这里插入图片描述
5.上代码,在angular实现

<div class="container">
    <p>HTML5实现刮刮卡,屋里藏娇</p>
    <div class="canvasDiv" id="canvasDiv">
        <img id="img" src="../../../assets/image/img.jpg" >
        <canvas id="myCanvas" width="200px" height="300px"></canvas>
    </div>
</div>


*{
    margin: 0px;
    padding: 0px;
}
.container{
    position: absolute;
    width:300px;
    height:400px;
    background-color: beige;
    box-sizing: content-box;
}
.canvasDiv{
    position: absolute;
    width:200px;
    height:300px;
    box-sizing: content-box;
}
img{
    position: absolute;
    width:100%;
    height:100%;
    
}
canvas{
    position: absolute;
}
.container,.canvasDiv{
    top:50%;
    left:50%;
    transform: translate(-50%,-50%);
    border:5px solid red;
}
.container p{
    text-align: center;
    padding-top:10px;
}



export class CanvasComponent implements OnInit {
  isMouseDown:boolean=false;//如果鼠标按下,再让mousemove起作用,如果没有这个变量,则鼠标未按下,鼠标滑过canvas也会刮图。
  myCanvas:any;
  myContext:any;
  setCanvas(){
    this.myContext.fillStyle="gray";
    this.myContext.fillRect(0,0,this.myCanvas.width,this.myCanvas.height);//目标层
    this.myContext.globalCompositeOperation="destination-out";//在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。
    this.myCanvas.addEventListener("mousedown",this.mouseDown,false);
    this.myCanvas.addEventListener("mousemove",this.mouseMove,false);
    this.myCanvas.addEventListener("mouseup",this.mouseUp,false);
  }
  mouseDown=function(e){
    e.preventDefault();
    this.isMouseDown=true;
  }.bind(this)//现在this指向全局

  mouseMove=function(e){
    if(this.isMouseDown){
        this.myContext.beginPath();
        this.myContext.arc(e.offsetX, e.offsetY, 20, 0, Math.PI * 2);//根据鼠标拖动生成圆形的刮卡路径
        this.myContext.fillStyle="yellow";//任意颜色,因为使用了destination-out,源图像是透明的,与颜色无关。
        this.myContext.fill();//填充源图像
        this.calculate();
    }
  }.bind(this)//现在this指向全局

  
  mouseUp=function(e){
    this.isMouseDown=false;
  }.bind(this)//现在this指向全局

  calculate(){
    let data=this.myContext.getImageData(0,0,this.myCanvas.width,this.myCanvas.height).data;//获得画布图像数据
    let length=data.length;//数据是以数组形式保存
    let transparentNumber=0;//用来计数,计算画布中已经透明的像素数据。
    for(let i=3;i<length;i+=4){//看看getImageData(),就知道每隔四个数据是记录着一个像素的alpha值
        if(data[i]==0){
          ++transparentNumber;
        }
    }
    if(transparentNumber>=(length/4/2)){//length除以4,得出画布的像素个数,再除以2即当用户刮开一半刮刮卡
      document.getElementById("img").style.zIndex="1";//<img>的标签的堆叠数更大,则会覆盖canvas,显示出显示屏
      this.myCanvas.removeEventListener("mousedown",function(e){
        return 0;
      },false);
      this.myCanvas.removeEventListener("mousemove",function(e){
        return 0;
      },false);
      this.myCanvas.removeEventListener("mouseup",function(e){
        return 0;
      },false);
    }
  }
  constructor() { }

  ngOnInit(): void {
    this.myCanvas=document.getElementById("myCanvas");
    this.myContext=this.myCanvas.getContext('2d');
    this.setCanvas();
  }
}

二、动态生成验证码

1.效果图

在这里插入图片描述

2.上代码
<div class="canvas" (click)="getCode()">
   <canvas id="canvas"></canvas>
</div>

canvas{
    height:90px;
    width:180px;
    background-color: gray;
    margin-left: 130px;
}

getCode(canvas:any){
    var environment=canvas.getContext('2d');
    canvas.setAttribute("height",90);//只要修改canvas任意属性,都会重置canvas,若不修改属性,则上一次生成的验证码一直显示在canvas上
    environment.font="35px normal";
    var str:Array<string>=['Q','W','E','R','T','Y','U','I','O','P','A','S','D','F','G','H','J','K','L','Z','X','C','V','B','N','M','1','2','3','4','5','6','7','8','9','0'];
    for(let i=0;i<4;i++){
      let index=Math.floor(Math.random()*str.length);//随机生成0~35的随机数
      this.ang[i]=str[index];
      let x=Math.floor(Math.random()*25+65*i);//canvas宽度为180px,因此每个字母应该在45px范围内活动,因此下一个字母应加上45px,才能保证它显示在下一个1/4的框中
      let y=Math.floor(Math.random()*30+50);//Math.random()*30+50因为每个字母在y轴上与上一个字母位置无关,因此与i无关
      let r=Math.floor(Math.random()*256);
      let g=Math.floor(Math.random()*256);
      let b=Math.floor(Math.random()*256);
      environment.fillStyle=`rgb(${r},${g},${b})`;
      let deg=Math.floor(Math.random()*6-2);//旋转度数,使字母有一定的角度;这个角度必须设小,因为角度会改变验证码的x,y;导致有些验证码看不到
      environment.rotate(deg*Math.PI/180);
      environment.fillText(str[index],x,y);
    }
    for(let i=0;i<15;i++){
      let start_x=Math.floor(Math.random()*200);
      let end_x=Math.floor(Math.random()*200+50);
      let start_y=Math.floor(Math.random()*20);
      let end_y=Math.floor(Math.random()*100);
      environment.moveTo(start_x,start_y);
      environment.lineTo(end_x,end_y);
      let r=Math.floor(Math.random()*256);
      let g=Math.floor(Math.random()*256);
      let b=Math.floor(Math.random()*256);
      let o=Math.floor(Math.random()*101);
      environment.strokeStyle=`rgb(${r},${g},${b},${o/100})`;
      environment.stroke();
    }
  }

三、canvas绘画的所有属性(这些是关于画布属性的学习)

线条样式:
1.lineWidth=number;用来设置画布内容线条的宽度或构造图形边框的宽度。
2.lineCap:设置或返回线条的结束端点样式。
3.lineJoin:设置或返回两条线相交时,拐角的样式类型。

矩形:
1.rect(x,y,width,height):创建矩形,只是一个空心的矩形
2.fillRect(x,y,width,height):绘制“被填充”的矩形,矩形内容,实心。
3.strokeRect():绘制矩形(无填充)
4.clearRect():在给定矩形内清空一个矩形。

颜色、样式和阴影:
1.fillStyle:设置填充绘画的颜色、渐变或模式。----->对应fillRect()
2.strokeStyle:设置笔触的颜色、渐变或模式。----->对应strikeRect()
3.shadowColor:设置用于阴影的颜色。必须配合shadowBlur来使用才有效果。
4.shadowBlur:设置阴影的模糊级别。
5.shadowOffsetX:设置阴影距形状的水平距离。
6.shadowOffsetY:设置阴影距形状的垂直距离。

重要方法:
1.createLinearGradient():创建线性渐变(用在画布内容上)
2.createPattern():创建模式,用于上面的style,在指定的方向上重复指定的元素。(元素可以是图片,视频、其它canvas元素)
3.createRadialGradient():创建放射状、环状的渐变(用在画布内容上).
4.addColorStop():规定第一、第三渐变对象中的颜色和停止位置。

路径:
1.fill():填充当前“绘画”(路径)
2.stroke():在画布绘制已定义的“路径”。
3.beginPath():起始一条路径,或重置当前路径。
4.moveTo():把路径移动到画布中的指定点,不创建线条
5.closePath():创建从当前点回到起始点的路径
6.lineTo():添加一个新点,然后在画布中创建该点到指定点的线条。
7.clip():从原始画布剪切任意形状和尺寸的区域。
8.quadraticCurveTo(cpx,cpy,x,y):创建二次贝塞尔曲线。二次贝塞尔曲线需要两个点。第一个点是用于二次贝塞尔计算中的控制点,第二个点是曲线的结束点。曲线的开始点是当前路径中最后一个点。如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点。
9.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y):创建三次贝塞尔曲线。
10.arc(x,y,r,sAngle,eAngle,counterclockwise):创建弧、曲线。counterclockwise规定是顺时针还是逆时针绘图。可选选项。
11.arcTo(x1,y1,x2,y2,r):在画布上创建介于两个切线之间的弧、曲线。
12.isPointInPath():如果指定的点位于当前的路径中,则返回true。

转换:
1.scale():缩放当前绘图至更大更小。
2.rotate():旋转当前绘图
3.translate():重新映射画布上的(0,0)位置。

文本:
1.fillText(text,x,y,maxWidth):在画布上绘制填色的文本。请使用 font 属性来定义字体和字号,并使用 fillStyle 属性以另一种颜色/渐变来渲染文本。
2.strokeText():在画布上绘制没有填色的文本。请使用 font 属性来定义字体和字号,并使用 strokeStyle 属性以另一种颜色/渐变来渲染文本。

像素操作:
1.width:返回ImageData对象的宽度。
2.height:返回ImageData对象的高度。
3.data:返回一个对象,其包含ImageData对象的图像数据。
4.createImageData(width,height):创建新的、空白的ImageDate对象。
5.getImageData(x,y,width,height):返回ImageData对象,该对象为画布上指定的矩形复制像素数据
6.putImageData(imgData,x,y):把图像数据(从指定的ImageData对象)放回画布上。
​
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var imgData=ctx.createImageData(100,100);
for (var i=0;i<imgData.data.length;i+=4)
  {
  imgData.data[i+0]=0;
  imgData.data[i+1]=255;
  imgData.data[i+2]=0;
  imgData.data[i+3]=255;
  }
ctx.putImageData(imgData,10,10);


对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:
R - 红色 (0-255)
G - 绿色 (0-255)
B - 蓝色 (0-255)
A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)
color/alpha 以数组形式存在,并存储于 ImageData 对象的 data 属性中。

以下代码可获得被返回的 ImageData 对象中第一个像素的 color/alpha 信息:
red=imgData.data[0];
green=imgData.data[1];
blue=imgData.data[2];
alpha=imgData.data[3];


其它方法:
1.save():保存当前环境

四、最后,动态验证码弄的线条不够好看,请勿喷,可以自己打造更有个性的画布噢

猜你喜欢

转载自blog.csdn.net/weixin_43334673/article/details/106190602