canvas三要素
- id:作为唯一的标识
- width:画布内同宽度的像素大小(与stylr的宽高是有区别的)
- height:画布内同高度的像素大小
颜色、样式和阴影
属性 |
描述 |
fillStyle |
设置或返回用于填充绘画的颜色,渐变或模式 |
strokeStyle |
设置或返回用于笔触的颜色,渐变或模式 |
shadowColr |
设置或返回用于阴影的颜色 |
shadowBlur |
设置或返回用于阴影的模糊级别 |
shadowOffsetX |
设置或返回阴影与形状的水平距离 |
shadowOffsetY |
设置或返回阴影与形状的垂直距离 |
方法 |
描述 |
createLinearGradient() |
创建线性渐变(用于画布内容上) |
createPattern() |
在指定的方向上重复指定的元素 |
createRadialGradient() |
创建放射状/环形的渐变(用在画布内容上) |
addColorStop() |
规定渐变对象种的颜色和停止位置 |
线条样式
属性 |
样式 |
lineCap |
设置或返回线条的结束端点样式 |
lineJoin |
设置或返回两条线相交时,所创建的拐角类型 |
lineWidth |
设置或返回当前的线条的宽度 |
miterLimit |
设置或返回最大斜接长度(值只有1,2,3;三个等级) |
miterLimit:其实就是例如折线交点处,如果太尖锐时候需要截掉尖锐头的一部分,可以设置该值
miterLimit说明
矩形
方法 |
属性 |
rect() |
创建矩形 |
fillRect() |
绘制"被填充"的矩形 |
strokeRect() |
绘制矩形(无填充) |
clearRect() |
再给定的矩形内清除指定的像素 |
路径
方法 |
描述 |
fill() |
填充当前绘图(路径) |
stroke() |
绘制已定义的路径 |
beginPath() |
起始一条路径,或重置当前路线 |
moveTo() |
把路径移动到画布的指定点,不创建线条 |
closePath() |
创建从当前点回到起始点的路径 |
linTo() |
添加一个新点,然后再画布种创建从该点到最后指定点的线条 |
clip() |
从原始画布裁剪任意形状和尺寸的区域 |
quadraticCurveTo() |
创建二次贝塞尔曲线 |
bezierCurveTo() |
创建三次贝塞尔曲线 |
arc() |
创建弧/曲线(用于创建圆形或者部分圆) |
isPointLnPath() |
如果指定的点位于当前路径种,则返回true否则返回false |
转换
方法 |
描述 |
scale() |
缩放当前绘图至更大或更小 |
rotate() |
旋转当前绘图 |
translate() |
重新映射画布上的(0,0)位置 |
transform() |
替换绘图的当前转换矩阵 |
setTransform() |
将当前转换重置为单元矩阵,然后运行transform() |
文本
属性 |
描述 |
font |
设置或返回文本内容的当前字体属性 |
textAlign |
设置或返回文本内容的当前对齐方式 |
textBaseLine |
设置或返回在绘制文本时使用的当前文本基线 |
方法 |
描述 |
fillText() |
在画布上绘制"被填充的"文本 |
strokeText() |
在画布上绘制文本(无填充) |
measureText() |
返回包含指定文本宽度的对象 |
图像绘制
方法 |
描述 |
drawImage() |
向画布上绘制图像、画布或视频 |
像素操作
属性 |
描述 |
width |
返回ImageData对象的宽度 |
height |
返回ImageData对象的高度 |
data |
返回一个对象,其包含指定的ImageData对象的图像数据 |
方法 |
描述 |
createImageData() |
创建新的、空白的ImageData对象 |
getImageData() |
返回ImageData对象,该对象为画布上指定的矩形复制像素数据 |
putImageData() |
把图像数据(从指定的ImageData对象)放回画布上 |
getImageData(x0,y0,width,height);起始就是获取一个矩形图像,数据是数组,数组里面数据是四个一条,就是rgba的数据
合成
属性 |
描述 |
globalAlpha |
设置或返回绘图的当前alpha或透明值 |
globalCompositeOperation |
设置或返回新图像如何绘制到已有的图像上 |
定义和用法
globalCompositeOperation属性设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上。
- 源图想=打算放置到画布上的绘图
- 目标图像=已经放置再画布上的绘图
默认值 |
source-over |
js语法 |
context.globalCompositeOperation="source-in" |
属性值
值 |
描述 |
source-over |
源图像叠加在目标图像上 |
source-atop |
在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 |
source-in |
在目标图像中显示源图像。只有目标图像之内的源图像部分会显示,目标图像是透明的。 |
source-out |
在目标图像之外显示源图像。只有目标图像之外的源图像部分会显示,目标图像是透明的 |
destination-over |
在源图像上显示目标图像。 |
destination-atop |
在源图像顶部显示目标图像。目标图像位于源图像之外的部分是不可见的。 |
destination-in |
在源图像中显示目标图像。只有源图像之内的目标图像部分会被显示,源图像是透明的。 |
destination-out |
在源图像之外显示目标图像。只有源图像之外的目标图像部分会被显示,源图像是透明的。 |
lighter |
显示源图像 + 目标图像
|
copy |
显示源图像。忽略目标图像。 |
xor |
等同于lighter,使用异或操作对源图像与目标图像进行组合。 |
其他
方法 |
描述 |
save() |
保存当前环境的状态 |
restore() |
返回之前保存过的路径状态和属性 |
createEvent() |
创建一个事件对象。该事件可以是任何合法事件类型,必须在使用前初始化 |
getContext() |
返回一个用于在画布上绘图的环境。参数("2d") |
toDataURL() |
返回一个包含图片展示的 data URI |
var x = document.createEvent("MouseEvent");
x.initMouseEvent("mouseover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
document.getElementById("myDiv").dispatchEvent(x);
var btn = document.createEvent("BUTTON"); // 创建一个<button>元素
btn.innerHTML = "CLICK ME"; // 插入文本
document.body.appendChild(btn);
downloadBtn.onclick = function () {
var url = canvas.toDataURL()
//console.log(url)
/* var img = new Image()
img.src = url; */
/* lcAlert({
title:"请点击图片另存!",
content:"<img style='width:auto;height:230px;' src='"+url+"'/>"
}) */
var aDom = document.querySelector(".download2 a")
aDom.setAttribute("href", url);
//自动触发点击事件
aDom.click()
}
基本使用demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="draw" width="600" height="500" style="border: 1px solid black;">
</canvas>
</body>
</html>
<script>
/** @type {HTMLCanvasElement} */
let draw=document.getElementById('draw');
// console.log(draw);
let cvs=draw.getContext("2d");
// console.log(cvs);
// cvs.fillStyle;//填充的样式
// cvs.strokeStyle;//笔触的样式(边框的颜色)
// cvs.lineWidth;//边框宽度
//绘制图形有两种方式:一个填充一个绘制边框,两个可以合并起来用
// cvs.fill();//填充
// cvs.stroke();//边框
// 坐标已画布为基准,左上角(0,0)
// cvs.moveTo();//起始点的坐标
// cvs.lineTo();//结束点的坐标
//如果没有moveto就把上一个挨着的lineTo作为起始坐标
// cvs.beginPath();//开始一个新的路径
// cvs.closePath();//关闭当前的路径,而且会自动闭合
function draw1() {
//直线
cvs.beginPath();
cvs.moveTo(50,50);
cvs.lineTo(150,50);
cvs.closePath();
cvs.strokeStyle="red";
cvs.lineWidth=20;
cvs.stroke();//以边框的形式显示
}
// draw1();
function draw2() {
//三角形
cvs.beginPath();
cvs.moveTo(50,50);
cvs.lineTo(150,50);
cvs.lineTo(80,80);
cvs.closePath();
// cvs.fillStyle="red";
// cvs.lineWidth=2;
cvs.stroke();
}
// draw2();
function draw3() {
//圆角
// cvs.lineJoin 三个属性:尖角(默认 miter),斜角(bevel),圆角(round)
//设置一条线段,两端点的样式
// cvs.lineCap 三个属性:butt(默认的 平) round(圆角) square(方角)
cvs.beginPath();
cvs.moveTo(50,50);
cvs.lineTo(250,50);
// cvs.closePath(); 加上这句话没有端点的效果,例如圆角消失
cvs.lineWidth=20;
cvs.strokeStyle="red";
cvs.lineCap="round";
cvs.stroke();
}
// draw3();
function draw4() {
cvs.beginPath();
cvs.moveTo(200,100);
cvs.lineTo(100,250);
cvs.lineTo(300,250);
cvs.closePath();
cvs.lineWidth=20;
cvs.strokeStyle="red";
cvs.lineJoin="bevel";
cvs.stroke();
}
// draw4();
function draw5() {
//圆形
//cvs.arc(x,y,radius,star,end,n)
//x,y 圆心的坐标
//r半径
//star:起始角,以弧度计算(三点钟方向是0度)
//end 结束角
//n: 是否逆时针 默认false(顺时针)
//一、画线条圆形-线-不填充
// cvs.strokeStyle="red";
// cvs.beginPath(); 划线的时候一般都需要开始路径结束路径,但是画形状等时候不需要
// cvs.arc(150,150,100,0,2*Math.PI);
// cvs.closePath();
// cvs.stroke();
//二,画形状-填充形式
cvs.arc(150,150,100,0,2*Math.PI);
cvs.fillStyle='blue';
cvs.fill();
//重点:虽然开启路径和关闭路径在此种形势下可省略,但是实际开发中,可能上下都有逻辑,所以建议也加上
}
// draw5();
function draw6() {
//线性渐变
//渐变也可以设置给边框 strokeStyle
// let CLG=cvs.createLinearGradient(x0,y0,x1,y1);//创建线性渐变
// CLG.addColorStop(n,m)
//x0,y0:渐变开始的坐标
//x1,y1:渐变结束的坐标
//n:设置颜色偏移量
//m:颜色
// let CLG=cvs.createLinearGradient(0,0,200,200);//创建线性渐变-斜着渐变
let CLG=cvs.createLinearGradient(0,0,200,0);//创建线性渐变-这就是只改变了x,横向渐变
CLG.addColorStop(0,"red");
CLG.addColorStop(0.25,"yellow");
CLG.addColorStop(0.5,"#ccc");
CLG.addColorStop(0.75,"yellow");
CLG.addColorStop(1,"red");
cvs.fillStyle=CLG;
cvs.fillRect(0,0,200,200); //起始坐标0,0 宽高 200
}
// draw6();
function draw7() {
//图1-原理图
//径向渐变,发散型渐变
// cvs.createRadialGradient(x0,y0,r0,x1,y1,r1);
//x0,y0:渐变开始圆心的坐标
//x1,y1:渐变结束圆心的坐标
//r0:渐变开始的半径
//r1:渐变结束的半径
let CRG= cvs.createRadialGradient(200,200,200,200,200,10);
CRG.addColorStop(0,"#000");
CRG.addColorStop(0.5,"orange");
CRG.addColorStop(1,"red");
cvs.fillStyle=CRG;
cvs.fillRect(100,100,200,200);
//图2
//如果r0小r1大,则就是从里到位渐变
}
draw7();
</script>
1.png
2.png
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="draw" width="109" height="195" style="border: 1px solid black;">
</canvas>
</body>
</html>
<script>
/** @type {HTMLCanvasElement} */
let draw = document.getElementById('draw');
let cvs = draw.getContext("2d");
function draw1() {
//阴影
// cvs.shadowOffsetX;//阴影横向偏移量,默认0
// cvs.shadowOffsetY;//阴影纵向偏移量,默认0
// cvs.shadowColor="";//阴影颜色
// cvs.shadowBlur="";//阴影模糊范围(值越大越模糊)
cvs.shadowOffsetX = 30;
cvs.shadowOffsetY = 20;
cvs.shadowColor = "#0000ff";
cvs.shadowBlur = 20;
cvs.fillStyle = "#449FDB";
cvs.fillRect(50, 50, 100, 100);
//图3
}
// draw1();
function draw2() {
//设置字体样式 cvs.font="字符大小font-szie font-family"
//水平对齐方式 cvs.textAlign=属性值 "start,end,right,center";
//垂直对齐fanghsi cvs.textBaseline=属性值 "top,middle,hanging,bottom,alphabetic,ideographic"
//计算文本宽度 cvs.measureText(str)
//填充文字 cvs.fillText(text,x,y,maxWidth) 文本内容 文字起始点x y 最大宽度
//绘制文字轮廓 cvs.strokeText(text,x,y,maxWidth) 文本内容 文字起始点x y 最大宽度
let text = "Hello World";
cvs.fillStyle = "#ffE470";
cvs.font = "40px verdana";
cvs.textAlign = "start";
cvs.textBaseline = "top";
cvs.fillText(text, 0, 0, cvs.measureText(text).width);
}
// draw2();
function draw3() {
let CLG = cvs.createLinearGradient(0, 150, 450, 250);
CLG.addColorStop(0, "red");
CLG.addColorStop(0.25, "yellow");
CLG.addColorStop(0.5, "green");
CLG.addColorStop(0.75, "yellow");
CLG.addColorStop(1, "red");
let text = "Hello World";
cvs.fillStyle = CLG;
cvs.font = "40px verdana";
cvs.textAlign = "start";
cvs.textBaseline = "top";
cvs.shadowOffsetX = 5;
cvs.shadowColor = "#0000ff";
cvs.shadowOffsetY = 4;
cvs.shadowBlur = 5;
cvs.fillText(text, 0, 0, cvs.measureText(text).width);
}
function draw4() {
//绘图
// cvs.drawImage(Image,x,y,w,h);
//Image 真实图片,可以动态创建,也可以获取页面上的
//x,y 图片左上角坐标
//w,h 宽高
// cvs.drawImage(Image,sx,sy,sw,sh,dx,dy,dw,dh);
//也是绘图但是可以把图部分显示出来
//sx,sy 图片左上角的坐标
//sw,sh 矩形区域的宽高,用来截图图片
//dx,dy 截取出来需要画在canvas上的坐标
//dw,dh 画在canvas上面的宽高
}
function draw5() {
//以下平移,缩放,旋转,其实都是针对画布原点操作的
//平移:cvs.translate(x,y);
//x: 坐标原点向x轴平移
//y: 坐标原点向y轴平移
//缩放 cvs.scale(x0,y0)
//x0:x轴按照x0的比例缩放
//y0:x轴按照y0的比例缩放
//旋转 cvs.rotate(angle); angle:弧度 坐标轴转的角度
cvs.fillStyle = "#0000FF";
cvs.fillRect(0, 0, 200, 100);
//上面一个矩形已经画完了,所以这个移动对上面的矩形无效
cvs.translate(50, 0);
cvs.fillStyle = "#800080";
cvs.fillRect(0, 0, 100, 50);
//图3
}
function draw6() {
//图形组合
// cvs.globalCompositeOperation=type;
// type的值
//0. source-cover: 默认值 覆盖 在原来图形上绘制新图
//1. destination-over 在原来图形下绘制新图
//2. sourse-in:显示图形和新图的交集,新图再上,颜色是新图的颜色
//3. source-out:显示和新图的非交集部分,图5
//4. destination-in: 显示图形和新图的交集,旧图在上,颜色是旧图的颜色
//5. destination-out:显示和旧图的非交集部分
//6. source-atop: 显示是旧图和交集的部分,交集是新图的颜色
//7. destination-atop:显示是新图和交集的部分,交集是旧图的颜色
//8. lighter:全部显示,交集是二者颜色的叠加\
//9. xor:显示新旧图的非交集部分
//10. copy:显示新图
cvs.fillStyle = "gold";
cvs.fillRect(10, 10, 100, 100);
//注意位置:最好放在这里,才有新旧的设置区分
cvs.globalCompositeOperation = "copy";
cvs.fillStyle = "#0000FF";
cvs.fillRect(50, 50, 100, 100);
//图四
}
function draw7() {
//僵尸动画
let img = new Image;
img.src = "./haha.png";
img.onload = function () {
let width = this.width / 10;
let height = this.height;
let i = 0;
window.setInterval(() => {
//每一次都清空下画布
cvs.clearRect(0,0,draw.width,draw.height);
cvs.drawImage(this, i * width, 0, width, height, 0, 0, width, height);
if (i == 9) {
i = 0;
} else {
i++;
}
}, 200);
}
}
draw7();
</script>
3.png
4.png
5.png
haha.png
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="draw" width="600" height="500" style="border: 1px solid black;">
</canvas>
<video width="600" height="500" src="./movie.ogv" controls="controls"></video>
</body>
</html>
<script>
/** @type {HTMLCanvasElement} */
let draw = document.getElementById('draw');
let cvs = draw.getContext("2d");
let video = document.querySelector('video');
let interId;
video.onplay=function(){
setInterval(function() {
// cvs.cl
interId= cvs.drawImage(video,0,0,500,500);
cvs.font="50px 微软雅黑";
cvs.strokeStyle="#999";
cvs.strokeText("老陈打吗",50,50);
},16);
}
video.onpause=function(){
clearInterval(interId);
}
</script>
canvas的width,height
canvas的标签上面可以添加height,widht;同时其style也可以添加height,widht;但是二者不完全相同,下面说明下。
<canvas id="canvas" width="800" height="600" style="width:400px;height: 300px;"></canvas>
<video width="800" height="" src="img/mov_bbb.mp4" controls="controls"></video>
2.png
<canvas id="canvas" width="800" height="600" style="width:800px;height: 400px;"></canvas>
<video width="800" height="" src="img/mov_bbb.mp4" controls="controls"></video>
1.png
这两个案例只有style是不同的,对比图可知,style里面的宽高才是canvas真的宽高,至于canvas标签上面直接的宽高属性,其实指的是绘制的内容;例如截取的视频的某一帧设置获取得是500200;那么此时800600就会空出来一部分canvas没占用;如果截取的一帧设置的很大,那么此时canvas只能显示其中一部分
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<canvas id="canvas" width="800" height="600"></canvas>
<script type="text/javascript">
var canvas = document.querySelector("#canvas")
var cxt = canvas.getContext('2d')
function renderClock(){
cxt.clearRect(0,0,800,600)
cxt.save()
//将坐标移动到画布的中央
cxt.translate(400,300)
cxt.rotate(-2*Math.PI/4)
cxt.save()
//绘制表盘
cxt.beginPath()
cxt.arc(0,0,200,0,2*Math.PI)
cxt.strokeStyle = "darkgrey"
cxt.lineWidth = 10
cxt.stroke()
cxt.closePath()
//绘制分钟刻度
for(var j=0;j<60;j++){
cxt.rotate(Math.PI/30)
cxt.beginPath()
cxt.moveTo(180,0)
cxt.lineTo(190,0)
cxt.lineWidth = 2;
cxt.strokeStyle = "orangered"
cxt.stroke()
cxt.closePath()
}
cxt.restore()
cxt.save()
//绘制时钟刻度
for(var i = 0;i<12;i++){
cxt.rotate(Math.PI/6);//通过旋转坐标尺,这样就不需要纠结刻度的变化了
cxt.beginPath()
cxt.moveTo(180,0)
cxt.lineTo(200,0)
cxt.lineWidth = 10;
cxt.strokeStyle = "darkgrey"
cxt.stroke()
cxt.closePath()
}
cxt.restore()
cxt.save()
var time = new Date()
var hour = time.getHours()
var min = time.getMinutes()
var sec = time.getSeconds()
//如果时间是大于12的话,就直接减去12
hour = hour>12?hour-12:hour
console.log(hour+":"+min+":"+sec)
//绘制秒针
cxt.beginPath()
//根据秒针的时间进行旋转
cxt.rotate(2*Math.PI/60*sec)
cxt.moveTo(-30,0)
cxt.lineTo(170,0)
cxt.lineWidth = 2;
cxt.strokeStyle = "red"
cxt.stroke()
cxt.closePath()
cxt.restore()
cxt.save()
//绘制分针
cxt.beginPath()
//根据分针的时间进行旋转
cxt.rotate(2*Math.PI/60*min+2*Math.PI/3600*sec)
cxt.moveTo(-20,0)
cxt.lineTo(150,0)
cxt.lineWidth = 4;
cxt.strokeStyle = "darkblue"
cxt.stroke()
cxt.closePath()
cxt.restore()
cxt.save()
//绘制时针
cxt.beginPath()
//根据时针的时间进行旋转
cxt.rotate(2*Math.PI/12*hour+2*Math.PI/60/12*min + 2*Math.PI/12/60/60*sec)
cxt.moveTo(-20,0)
cxt.lineTo(140,0)
cxt.lineWidth = 6;
cxt.strokeStyle = "darkslategray"
cxt.stroke()
cxt.closePath()
//画中间的表芯
cxt.beginPath()
cxt.arc(0,0,10,0,2*Math.PI)
cxt.fillStyle = "deepskyblue";
cxt.fill()
cxt.closePath()
cxt.restore()
cxt.restore()
}
setInterval(function(){
renderClock()
},1000)
</script>
</body>
</html>
其实主要就是globalCompositeOperation,通过显示层级的覆盖关系去实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
#ggk{
width: 600px;
height: 200px;
position: relative;
}
#ggk .jp{
width: 600px;
height: 200px;
position: absolute;
left: 0;
top: 0;
text-align: center;
color: deeppink;
font-size: 50px;
line-height: 200px;
}
#ggk #canvas{
width: 600px;
height: 200px;
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div id="ggk">
<div class="jp">谢谢惠顾</div>
<canvas id="canvas" width="600" height="200"></canvas>
</div>
<script type="text/javascript">
var canvas = document.querySelector("#canvas")
var ggkDom = document.querySelector("#ggk")
var jpDom = document.querySelector(".jp")
var ctx = canvas.getContext('2d');
ctx.fillStyle= "darkgray"
ctx.fillRect(0,0,600,200)
ctx.font = "20px 微软雅黑"
ctx.fillStyle = '#fff'
ctx.fillText("刮刮卡",260,100)
var isDraw = false;
//设置isDraw = true,即为允许绘制
canvas.onmousedown = function(){
isDraw = true
console.log(isDraw)
}
//移动的时候绘制圆形,将源图像内的目标的内容给清除掉
canvas.onmousemove = function(e){
//console.log(e)
if(isDraw){
var x = e.pageX - ggkDom.offsetLeft;
var y = e.pageY - ggkDom.offsetTop
ctx.globalCompositeOperation = "destination-out"
ctx.arc(x,y,20,0,2*Math.PI)
ctx.fill()
}
}
canvas.onmouseup = function(){
isDraw = false
console.log(isDraw)
}
jpDom.onselectstart = function(){
return false
}
var arr = [{content:"一等奖:IphoneXs",p:0.1},{content:"二等奖:娃娃1个",p:0.2},{content:"三等奖:Ipad",p:0.3}]
var randomNum = Math.random()
if(randomNum<arr[0].p){
jpDom.innerHTML = arr[0].content
}else if(randomNum<arr[1].p+arr[0].p){
jpDom.innerHTML = arr[1].content
}else if(randomNum<arr[2].p+arr[1].p+arr[0].p){
jpDom.innerHTML = arr[2].content
}
</script>
</body>
</html>
/*
args:
{
title:'温馨提示',
content:"是否在页面添加1个蓝色的div?",
confirmFn:function(){
var blueDiv = document.createElement("div")
blueDiv.style.backgroundColor = "blue"
blueDiv.style.width = "300px"
blueDiv.style.height = "300px"
body.appendChild(blueDiv)
},
cancelFn:function(){
}
}
*/
function lcAlert(args){
var zhezhao = document.createElement('div')
zhezhao.className = "zhezhao";
zhezhao.innerHTML = `
<div class="alert">
<div class="header"><span class="title">`+args.title+`</span><span class="close">x</span></div>
<div class="main">
`+args.content+`
</div>
<div class="btnList">
<div class="btn comfirm">确定</div>
<div class="btn cancel">取消</div>
</div>
</div>
`
var body = document.querySelector('body');
body.appendChild(zhezhao)
//获取close元素
var closeDiv = document.querySelector(".close")
closeDiv.onclick = function(){
body.removeChild(zhezhao);
}
var confirmDiv = document.querySelector(".btn.comfirm");
confirmDiv.onclick = function(){
args.confirmFn()
if(typeof args.confirmFn == 'function'){
args.confirmFn()
}else{
console.error("传入的参数,没有设置确认函数!")
}
body.removeChild(zhezhao);
}
var cancelDiv = document.querySelector(".btn.cancel")
cancelDiv.onclick = function(){
if(typeof args.cancelFn == 'function'){
args.cancelFn()
}else{
console.error("传入的参数,没有设置取消函数!")
}
body.removeChild(zhezhao);
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.zhezhao {
position: fixed;
left: 0;
top: 0;
height: 100vh;
width: 100vw;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.alert {
width: 600px;
height: 400px;
background: #fff;
display: flex;
flex-direction: column;
}
.alert>.header {
height: 80px;
line-height: 80px;
border-bottom: 1px solid #ccc;
display: flex;
justify-content: space-between;
padding: 0px 30px;
}
.alert>.header>.title {
font-size: 30px;
font-weight: 900;
}
.alert>.header>.close {
font-size: 30px;
font-weight: 100;
color: #ccc;
}
.alert>.header>.close:hover {
color: orangered;
}
.alert>.main {
flex: 1;
border-bottom: 1px solid #ccc;
}
.alert>.btnList {
height: 80px;
display: flex;
justify-content: center;
align-items: center;
padding: 0 30px;
}
.alert>.btnList>.btn {
height: 40px;
width: 120px;
text-align: center;
line-height: 40px;
color: #fff;
margin: 10px;
}
.alert>.btnList>.btn:nth-child(1) {
background: coral;
}
.alert>.btnList>.btn:nth-child(2) {
background: #999;
}
body {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.caidan {
height: 100px;
width: 100vw;
display: flex;
border-bottom: 3px solid #ccc;
justify-content: space-around;
align-items: center;
}
#canvas {
flex: 1;
width: 100vw;
}
.btn,
.btn2 {
width: 150px;
height: 50px;
border: 1px solid #ccc;
border-radius: 20px;
text-align: center;
line-height: 50px;
color: #999;
background-size: auto 100%;
background-position: center;
background-repeat: no-repeat;
}
.btn1 {
width: 150px;
height: 50px;
border: 1px solid #ccc;
border-radius: 20px;
text-align: center;
line-height: 50px;
color: #999;
background-size: auto 100%;
background-position: center;
background-repeat: no-repeat;
}
.btn.active {
box-shadow: 0 0 20px deepskyblue;
border: 1px solid deepskyblue;
}
.btn1.active {
box-shadow: 0 0 20px deepskyblue;
border: 1px solid deepskyblue;
}
.line {
display: flex;
justify-content: center;
align-items: center;
}
.xi:after {
content: "";
background: #333;
width: 6px;
height: 6px;
display: block;
border-radius: 3px;
}
.normal:after {
content: "";
background: #333;
width: 16px;
height: 16px;
display: block;
border-radius: 8px;
}
.cu:after {
content: "";
background: #333;
width: 32px;
height: 32px;
display: block;
border-radius: 16px;
}
</style>
</head>
<body>
<!--
将画笔功能:能够拖动鼠标在页面内绘图,能够设置画笔的粗细,设置画笔的颜色
能够在任意位置绘制圆形:拖动鼠标即可随意在任意位置绘制圆形,并且可以随意定制大小
能够在任意位置绘制出矩形:拖动鼠标即可随意在任意位置绘制矩形,并且可以随意定制大小
-->
<div class="caidan">
<div class="btn active" id="huabi" style="background-image: url(img/huabi.png);"></div>
<div class="btn" id="rect">矩形</div>
<div class="btn">圆形</div>
<div class="btn">橡皮擦</div>
<div class="btn2 download">下载图片</div>
<div class="btn2 download2" style="display: none;">
<a href="" download="download">下载</a>
</div>
<div class="btn1 line xi active"></div>
<div class="btn1 line normal"></div>
<div class="btn1 line cu"></div>
<div class="btn2"><input type="color" name="color" id="color" value="#000000" /></div>
</div>
<canvas id="canvas"></canvas>
<link rel="stylesheet" type="text/css" href="css/alert.css" />
<script src="js/alert.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var allBtn = document.querySelectorAll(".btn")
//获取canvas
var canvas = document.querySelector("#canvas")
console.log([canvas])
var ctx = canvas.getContext('2d')
console.log(ctx)
//设置canvas的宽度和高度
canvas.setAttribute("width", canvas.offsetWidth);
canvas.setAttribute("height", canvas.offsetHeight)
var huaban = {
type: "huabi",
isDraw: false,
beginX: 0,
beginY: 0,
lineWidth: 6,
imageData: null,
color: "#000",
huabiFn: function (e) {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop
ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight)
if (huaban.imageData != null) {
ctx.putImageData(huaban.imageData, 0, 0, 0, 0, canvas.offsetWidth, canvas.offsetHeight)
}
ctx.lineTo(x, y)
ctx.strokeStyle = huaban.color;
ctx.lineWidth = huaban.lineWidth;
ctx.lineCap = "round"
ctx.lineJoin = "round"
ctx.stroke()
},
rectFn: function (e) {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop
ctx.clearRect(0, 0, canvas.offsetWidth, canvas.offsetHeight)
if (huaban.imageData != null) {
ctx.putImageData(huaban.imageData, 0, 0, 0, 0, canvas.offsetWidth, canvas.offsetHeight)
}
ctx.beginPath()
ctx.rect(huaban.beginX, huaban.beginY, x - huaban.beginX, y - huaban.beginY);
ctx.strokeStyle = huaban.color;
ctx.stroke()
ctx.closePath()
}
}
var huabiBtn = document.querySelector("#huabi")
huabiBtn.onclick = function () {
allBtn.forEach(function (item, i) {
item.classList.remove("active")
})
huabiBtn.classList.add("active")
huaban.type = "huabi"
}
var rectBtn = document.querySelector("#rect")
rectBtn.onclick = function () {
allBtn.forEach(function (item, i) {
item.classList.remove("active")
})
rectBtn.classList.add("active")
huaban.type = "rect"
}
//设置粗细的按钮
var lineDivs = document.querySelectorAll(".line")
lineDivs.forEach(function (item, i) {
item.onclick = function () {
lineDivs.forEach(function (a, b) {
a.classList.remove("active")
})
item.classList.add('active')
if (i == 0) {
huaban.lineWidth = 6;
} else if (i == 1) {
huaban.lineWidth = 16;
} else {
huaban.lineWidth = 32;
}
}
})
//监听颜色设置改变事件
var colorInput = document.querySelector("#color")
colorInput.onchange = function (e) {
/* console.log(e)
console.log(colorInput.value) */
huaban.color = colorInput.value;
}
//找到下载按钮
var downloadBtn = document.querySelector(".download")
downloadBtn.onclick = function () {
var url = canvas.toDataURL()
//console.log(url)
/* var img = new Image()
img.src = url; */
/* lcAlert({
title:"请点击图片另存!",
content:"<img style='width:auto;height:230px;' src='"+url+"'/>"
}) */
var aDom = document.querySelector(".download2 a")
aDom.setAttribute("href", url);
//自动触发点击事件
aDom.click()
}
//监听鼠标按下事件
canvas.onmousedown = function (e) {
huaban.isDraw = true;
if (huaban.type == "rect") {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
huaban.beginX = x;
huaban.beginY = y;
}
if (huaban.type == "huabi") {
var x = e.pageX - canvas.offsetLeft;
var y = e.pageY - canvas.offsetTop;
huaban.beginX = x;
huaban.beginY = y;
ctx.beginPath()
ctx.moveTo(x, y)
}
}
//监听鼠标抬起事件
canvas.onmouseup = function () {
huaban.imageData = ctx.getImageData(0, 0, canvas.offsetWidth, canvas.offsetHeight)
huaban.isDraw = false;
if (huaban.type == "huabi") {
ctx.closePath()
}
}
canvas.onmousemove = function (e) {
if (huaban.isDraw) {
var strFn = huaban.type + 'Fn'
/* console.log(huaban) */
huaban[strFn](e)
}
}
</script>
</body>
</html>
image.png
网友评论