<canvas>是⼀个HTML元素,我们可以将它简单理解为⼀个画板,通过Canvas提供的绘制api我们就可以绘制出各种图形。
一、基础
1、渲染上下文
● getContext('2d')
● getContext('webgl')
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d') //画笔
</script>
</body>
2、绘制图形
坐标系:
a、线(线,三⻆形,矩形):
- 绘制: moveTo, lineTo,stroke
- 设置样式:lineWidth,strokeStyle
- 路径: beginPath,closePath
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d') //画笔
//绘制线段
ctx.beginPath() //新建路径,两条线不会互相干扰
ctx.moveTo(0,10) //线段起点
ctx.lineTo(200,10) //终点
ctx.strokeStyle = 'orange' //颜色
ctx.lineWidth = 10
ctx.stroke() //绘制
ctx.beginPath()
ctx.moveTo(0,50)
ctx.lineTo(200,50)
ctx.strokeStyle = 'green'
ctx.lineWidth = 10
ctx.stroke() //绘制
//绘制三角形
ctx.beginPath()
ctx.moveTo(10,70)
ctx.lineTo(200,70)
ctx.lineTo(50,200)
//ctx.lineTo(10,70) //图形是闭合的,所以最后一个坐标,等于起始坐标
ctx.closePath() //让路径闭合
ctx.fillStyle = 'yellow'
ctx.fill() //用黄色填充内部
ctx.lineWidth = 1
ctx.strokeStyle = 'red'
ctx.stroke()
//绘制矩形 rect()
ctx.beginPath()
ctx.strokeRect(0,220,200,100)
ctx.fillStyle ='red'
ctx.fillRect(0,220,200,100)
</script>
</body>
b、弧线(弧、圆弧/圆)
arc(x, y, radius, startAngle, endAngle, anticlockwise);
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
//arc 画弧
ctx.beginPath()
//圆心x坐标,圆心y坐标,半径,开始角度,结束角度,顺时针
ctx.arc(400,400,100,0,Math.PI / 3,false)
ctx.strokeStyle = '#9013FE'
ctx.stroke()
</script>
</body>
c、贝塞尔曲线:
- 二阶贝塞尔曲线
参数:控制点坐标 ,结束点坐标
⼆阶贝塞尔曲线:quadraticCurveTo(cpx, cpy, x, y);
二阶贝塞尔曲线调试工具:Canvas Quadratic Curve Example (site ointstatic.com)
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
//绘制贝塞尔曲线 二阶
ctx.beginPath()
// 起点
ctx.moveTo(100,100)
//控制点,结束点
ctx.quadraticCurveTo(200,500,400,400)
ctx.stroke()
//绘制辅助线
ctx.fillStyle = 'red'
ctx.fillRect(100,100,10,10)
ctx.fillRect(200,500,10,10)
ctx.fillRect(400,400,10,10)
</script>
</body>
- 三阶贝塞尔曲线
三阶贝塞尔曲线:bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
三阶比二阶多了一个控制点
三阶贝塞尔曲线调试工具:Canvas Bézier Curve Example (sitepointstatic.com)
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
//绘制贝塞尔曲线 二阶
ctx.beginPath()
// 起点
ctx.moveTo(100,100)
//控制点,结束点
ctx.quadraticCurveTo(200,500,400,400)
ctx.stroke()
//绘制辅助线
ctx.fillStyle = 'red'
ctx.fillRect(100,100,10,10)
ctx.fillRect(200,500,10,10)
ctx.fillRect(400,400,10,10)
</script>
</body>
效果:
3、绘图样式
a、线条样式:
- lineWidth(设置线段的宽度)
- lineCap(设置线段两端的形状)
- setLineDash(虚线)
b、渐变
沿着某个方向:
- 线性渐变 ctx.createLinearGradient(x0, y0, x1, y1);
放射状:
- 径向渐变 ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
c、纹理样式
- ctx.createPattern(image,repetition)
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
// ctx.lineWidth = 15 //宽度15
ctx.lineCap = "round" //两端形状为圆角
ctx.beginPath()
ctx.moveTo(50,50)
ctx.lineTo(300,50)
//参数是一个数组,表示实线和虚线的长度
ctx.setLineDash([20,40,50]) //绘制虚线
ctx.stroke()
//线性渐变
//前两个参数:起始点坐标,后两个参数:结束点坐标
var gradient = ctx.createLinearGradient(0,0,200,0)
//在起始位置是绿色,0代表起始
gradient.addColorStop(0,"green")
//在终止位置是蓝色,1代表终止
gradient.addColorStop(1,"blue")
ctx.fillStyle = gradient
ctx.fillRect(50,200,200,100)
//径向渐变
//圆1圆心坐标,圆1半径,圆2圆心坐标,圆2半径
var gradient2 = ctx.createRadialGradient(150,450,150,150,450,0)
gradient2.addColorStop(0,"white")
gradient2.addColorStop(1,"green")
ctx.fillStyle = gradient2
ctx.fillRect(50,350,200,200)
//纹理样式
let img = new Image()
img.src = './images/pattern.png'
img.onload = function(){
var pattern = ctx.createPattern(img,'repeat')
ctx.fillStyle = pattern
ctx.fillRect(50,600,300,100)
}
</script>
</body>
4、绘制文本
绘制方式:
- 描边:stokeText()
- 填充:fillText()
绘制样式:
font、textAlign、direction、textBaseline
阴影:shadowOffsetX和shadowOffsetY、shadowBlur、shadowColor
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
//88像素 罗马字体
ctx.font = "88px Times New Roman"
//设置阴影
ctx.shadowOffsetX = 2 //阴影往下偏移两个像素
ctx.shadowOffsetY = 2 //引用往右偏移两个像素
ctx.shadowBlur = 2 //模糊值
ctx.shadowColor = "rgba(255,0,0,0.5)" //颜色
//第一个参数使绘制的文本内容 第二个和第三个参数是绘制的位置坐标
// ctx.fillText("hello canvas",100,100)
//用纹理
let img = new Image()
img.src = './images/pattern.png'
img.onload = function(){
var pattern = ctx.createPattern(img,'repeat')
ctx.fillStyle = pattern
ctx.fillText("hello canvas",100,100)
}
</script>
</body>
5、绘制图片
drawImage用法
- drawImage(image,dx,dy);
- drawImage(image,dx,dy,dWidth,dHeight);
- drawImage(image,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight);
param1:要绘制的图选购对象
param2、param3:从原图像中开始裁剪的坐标
param4、param5:从原图像裁剪的宽度、高度
param6、param7:在目标canvas上绘制的坐标
param8、param9:在目标canvas上绘制的宽度、高度
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
let img = new Image()
img.src = './images/pattern.png'
//图片加载完成时触发
img.onload = function(){
//第一个参数是绘制的图片,
//第二个和第三个参数是绘制的位置
//第四个和第五个参数是绘制的大小
ctx.drawImage(img,0,0,400,200)
}
</script>
</body>
二、进阶
1、变形
a、平移(translate)、旋转(rotate)、缩放(scale) ——指的是坐标系的平移和旋转
b、状态的保存与恢复
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
ctx.fillStyle = "red"
ctx.fillRect(0,0,100,100)
//将当前的所有状态进行保存
ctx.save()
//translate 平移--给坐标系向下、向右平移了400像素
ctx.translate(400,400)
ctx.fillRect(0,0,100,100)
//恢复状态--相当于坐标系又回到(0,0)
ctx.restore()
ctx.fillStyle = "black"
ctx.fillRect(0,0,50,50)
//旋转
ctx.save()
ctx.fillStyle = "yellow"
ctx.rotate(Math.PI/6) //旋转30°
ctx.fillRect(300,0,100,100)
ctx.restore()
//缩放
ctx.save()
ctx.fillStyle = "blue"
ctx.scale(0.5,0.5) //缩小一半
ctx.fillRect(400,400,100,100)
ctx.restore()
</script>
</body>
c、transform、setTransform
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
//矩阵变换 默认值:ctx.transform(1,0,0,1,0,0)
//平移 只更改e、f的值
ctx.save()
ctx.transform(1,0,0,1,400,400)
ctx.fillStyle = 'pink'
ctx.fillRect(0,0,100,100)
ctx.restore()
//缩放 只更改a、d的值
ctx.save()
ctx.transform(0.5,0,0,0.5,0,0)
ctx.fillStyle = 'orange'
ctx.fillRect(0,0,100,100)
ctx.restore()
//倾斜 只更改b、c的值
ctx.save()
ctx.transform(1,-0.5,-0.5,1,0,0)
ctx.fillStyle = 'purple'
ctx.fillRect(100,100,100,100)
ctx.restore()
</script>
</body>
2、合成(重要)
- destination-over: 先绘制的图形盖在后绘制的图形上
- destination-out :后绘制的图形会把先绘制的图形进行挖空,形成一个镂空效果
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = 'blue'
ctx.fillRect(10,10,100,100)
ctx.fillStyle = 'red'
ctx.fillRect(50,50,100,100)
</script>
</body>
3、裁剪——clip():
- 需要配合路径去使用(例如:context.arc()、context.rect()),路径所绘制出的图形就是裁剪出的范围,任何后续的绘图操作只会在裁剪范围内显示
- 在裁剪之前要保存一下当前状态,在裁剪之后恢复状态,以免影响其他绘制
<body>
<canvas id="canvas" width="800" height="800" style="background-color: aliceblue;margin: 20px;"></canvas>
<script>
const c = document.getElementById('canvas')
const ctx = c.getContext('2d')
//裁剪
ctx.rect(0,0,225,100)
ctx.clip()
//绘制文字
ctx.fillStyle = "green"
ctx.font = "44px Times New Roman"
ctx.fillText('hello canvas',50,50)
</script>
</body>
三、实战
1、放大镜或缩小镜(探照灯/望远镜)
技术点:离屏渲染
将一个canvas对象绘制到另一个canvas对象上
实现思路:
- 准备两个canvas,将两个图片分别绘制在这两个canvas上,一大一小,大的canvas对用户不可见;
- 在小图点击鼠标时,计算出在大图上相应的点击坐标,这个坐标就是是圆心;
- 根据圆心的坐标,减去放大镜的半径,能计算出大图左上角的坐标;
- 在小图中,根据小图的圆心,和放大镜的半径,裁剪出一个圆;
- 把大图上的图片进行裁剪,绘制到小图上;
<body>
<!-- 展示的canvas -->
<canvas id="canvas" style="display:block;margin:0 auto;border:1px solid #aaa;">
您的浏览器尚不支持canvas
</canvas>
<!-- 不可见的canvas -->
<canvas id="offCanvas" style="display: none">
</canvas>
<script>
//准备两个画布
var canvas = document.getElementById("canvas")
var context = canvas.getContext("2d")
var offCanvas = document.getElementById("offCanvas")
var offContext = offCanvas.getContext("2d")
//设置两个canvas的宽高,大图是小图的2倍(相当于放大2倍)
canvas.width = 920 / 2
canvas.height = 1598 / 2
offCanvas.width = 920
offCanvas.height = 1598
//在每个canvas中绘制一张图片
var image = new Image()
image.src = "./images/3.jpg"
image.onload = function () {
context.drawImage(image, 0, 0, canvas.width, canvas.height)
offContext.drawImage(image, 0, 0, offCanvas.width, offCanvas.height)
}
//缩放比
var scale = 2
var isMouseDown = false //鼠标是否按下
//鼠标点下去触发事件
canvas.onmousedown = function (e) {
e.preventDefault() //阻止默认的鼠标按下行为
isMouseDown = true
//记录鼠标点击位置的坐标
point = {
x: e.offsetX,
y: e.offsetY
}
drawCanvasWithMagnifier(true, point)
}
//鼠标抬起触发事件
canvas.onmouseup = function (e) {
e.preventDefault()
isMouseDown = false
drawCanvasWithMagnifier(false)
}
//鼠标移出
canvas.onmouseout = function (e) {
e.preventDefault()
isMouseDown = false
drawCanvasWithMagnifier(false)
}
//鼠标移动的时候
canvas.onmousemove = function (e) {
e.preventDefault()
if (isMouseDown == true) {
point = {
x: e.offsetX,
y: e.offsetY
}
console.log(point.x, point.y)
drawCanvasWithMagnifier(true, point)
}
}
//画放大镜
function drawCanvasWithMagnifier(isShowMagnifier, point) {
// 清空画布上的所有内容
context.clearRect(0, 0, canvas.width, canvas.height)
// 在画布上绘制图像
context.drawImage(image, 0, 0, canvas.width, canvas.height)
//是否绘制放大镜
if (isShowMagnifier == true) {
drawMagnifier(point)
}
}
//画放大镜
function drawMagnifier(point) {
// 放大镜的半径
var mr = 200
//点击的坐标 * 缩放值 = 大的canvas点的坐标
var imageLG_cx = point.x * scale
var imageLG_cy = point.y * scale
//(sx,sy)是大的canvas左上角的坐标
var sx = imageLG_cx - mr
var sy = imageLG_cy - mr
//(dx,dy)是小的canvas的左上角坐标
var dx = point.x - mr
var dy = point.y - mr
context.save()
context.lineWidth = 10.0
context.strokeStyle = "#ccc"
//定义一个圆形的裁剪区域,
context.beginPath()
context.arc(point.x, point.y, mr, 0, Math.PI * 2, false)
context.stroke()
context.clip()
//绘制图形,但是只在裁剪区域中可见
context.drawImage(offCanvas, sx, sy, 2 * mr, 2 * mr, dx, dy, 2 * mr, 2 * mr)
context.restore()
}
</script>
</body>
效果:
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="zYLxLoDU-1723599309241" src="https://live.csdn.net/v/embed/417263"></iframe>1
2、彩票刮刮乐
技术点:图像合成
利用图像合成让绘制的内容与与原矩形重合部分清空
<body>
<div id="ggl"
style=" background: url(../images/ggl.png);background-position: center center;background-size: cover;">
<div class="wrapper">
<div class="text">特等奖</div>
<canvas id="myCanvas" width="400px" height="200px"></canvas>
</div>
</div>
<script type="text/javascript">
let canvas = document.getElementById('myCanvas');
let ctx = canvas.getContext('2d');
let isClick = false;
// 绘制灰色涂层
ctx.fillStyle = '#909399';
ctx.fillRect(0, 0, 400, 200);
//定义画笔是圆角
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
ctx.lineWidth = 20
// 鼠标点击按下事件
canvas.addEventListener('mousedown', (e) => {
let x = e.offsetX;
let y = e.offsetY;
ctx.beginPath();
// 设置绘制的起点为当前点击的位置
ctx.moveTo(x, y)
isClick = true
})
// 鼠标移动事件
canvas.addEventListener('mousemove', (e) => {
if (!isClick) {
return
}
let x = e.offsetX;
let y = e.offsetY;
// 绘制的内容与原矩形重合部分清空
ctx.globalCompositeOperation = 'destination-out'
ctx.lineTo(x, y)
ctx.stroke()
})
canvas.addEventListener('mouseup', (e) => {
isClick = false
})
</script>
</body>
<style>
* {
margin: 0;
padding: 0;
}
#ggl {
position: relative;
width: 729px;
height: 1073px;
background-color: yellow;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper {
background-color: white;
position: relative;
width: 400px;
height: 200px;
border-radius: 50px;
overflow: hidden;
}
.text {
text-align: center;
line-height: 150px;
font-size: 28px;
font-weight: bold;
color: brown;
}
#myCanvas {
position: absolute;
top: 0;
left: 0;
width: 400px;
height: 200px;
margin: 0 auto;
}
</style>
效果:
<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="kKj3sUAa-1723599335813" src="https://live.csdn.net/v/embed/417264"></iframe>2
标签:Canvas,const,fillRect,ctx,canvas,特性,HTML5,100,绘制 From: https://blog.csdn.net/lxy19980718/article/details/140955713