[深刻01] 执行上下文
[深刻02] 原型链
[深刻03] 继承
[深刻04] 事件循环
[深刻05] 柯里化 偏函数 函数记忆
[深刻06] 隐式转换 和 运算符
[深刻07] 浏览器缓存机制(http缓存机制)
[深刻08] 前端安全
[深刻09] 深浅拷贝
[深刻10] Debounce Throttle
[深刻11] 前端路由
[深刻12] 前端模块化
[深刻13] 观察者模式 发布订阅模式 双向数据绑定
[深刻14] canvas
[深刻15] webSocket
[深刻16] webpack
[深刻17] http 和 https
[深刻18] CSS-interview
[react] Hookscss
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CIhtml
[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程前端
canvas:画布
triangle:三角形
rectangle:矩形
arc:弧
anti:反对,反
clockwise:顺时针方向
anticlockwise:逆时针方向
curve:曲线
quadratic:平方的
复制代码
弧度 = 弧长 / 半径
圆的弧长 = 2PI * R //即周长
1°的弧长 = 2PI * R / 360 = PI * R / 180
1°的弧度 = PI / 180
复制代码
默认的width=300,height=150
<canvas
id="canvas" width="200" height="200"
style="border: 1px solid red;"
></canvas>
(1) 指定width 和 height的方式有三种
1. 标准方式:canvas 标签自带的 width 和 height 属性
2. css方式
3. js方式: domTarget.width 和 domTarget.height
复制代码
<canvas id="canvas" width="200" height="200">
替换的内容
// <img src="images/clock.png" width="150" height="150" alt=""/>
</canvas>
复制代码
2d
, 3d
var canvas = document.getElementById('canvas');
if (canvas.getContext) { // ------------------------ 经过判断 getContext 方法是否存在来判断
console.log('你的浏览器支持Canvas!');
} else {
console.log('你的浏览器不支持Canvas!');
}
复制代码
<canvas
id="canvas" width="200" height="200"
style="border: 1px solid red;"
>
替换的内容
</canvas>
<script>
window.addEventListener('load', draw, false)
// load事件:在页面加载完成时候触发,包括DOM,图片,视频等全部资源加载完毕时执行
// DOMContentLoaded:在DOM加载完成时触发
// 或者 <body onload="draw();">...</body>
function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) { // ------------------------ getCotext方法存在,说明浏览器支持canvas
console.log('你的浏览器支持Canvas!');
var ctx = canvas.getContext('2d');
// 实现绘画的逻辑...
} else {
console.log('你的浏览器不支持Canvas!');
}
}
复制代码
rectangle:矩形
x y 表示矩形左上角的坐标,原点是左上角 0 0 位置
// 矩形
ctx.fillRect(300, 100, 100, 100) // 填充矩形
ctx.clearRect(350, 150, 30, 30); // 清除矩形区域,使其清除部分彻底透明
ctx.strokeRect(400, 200, 50, 50) // 矩形边框
复制代码
function drawLineAndTriangle(ctx) {
// 三角形
ctx.beginPath() //------------------------------------------ 一个路径的开始
ctx.moveTo(100, 100) // ----- 起始点
ctx.lineTo(80, 120) // ------ 直线的第二个点
ctx.lineTo(120, 120) // ----- 直线的第三个点
ctx.closePath() // ----------------------------------------- 一个路径的结束
ctx.lineWidth = 4 // -------- 直线的宽度
ctx.strokeStyle = 'red' // ------ 直线的颜色,须要在绘画前设置
ctx.stroke() // --------------------------------------------- 描边 (绘制)
ctx.fillStyle= 'yellow' // ------ 填充的颜色,须要在绘画前设置
ctx.fill() // ----------------------------------------------- 填充 (绘制)
// 直线
ctx.beginPath()
ctx.moveTo(30, 30)
ctx.lineTo(100, 30)
ctx.closePath()
ctx.lineWidth = 2 !!!!!!!!!!!!!!!!!!!
ctx.strokeStyle = 'blue'
ctx.stroke()
}
复制代码
以x,y为圆心,radius为半径, startAngle和endAngle为角度,anticlockwise为方向的圆弧(圆)
默认的方向是顺时针
弧度=(Math.PI/180)*角度
根据给定的控制点和半径画一段圆弧,再以直线链接两个控制点。
// 圆弧
ctx.beginPath()
ctx.arc(200, 150, 40, 90 * Math.PI/180, 1.5 * Math.PI, false)
ctx.stroke()
// 圆
ctx.beginPath()
ctx.arc(200, 350, 40, 0, 2 * Math.PI, false)
ctx.fill()
复制代码
function drawLineAndTriangle(ctx) {
ctx.beginPath()
ctx.arc(300, 300, 200, 0, 2 * Math.PI) // --------------------- 大圆
ctx.stroke()
// ctx.closePath() 可要可不要
ctx.beginPath()
ctx.arc(250, 200, 6, 0, 2* Math.PI) // ------------------------ 左眼
ctx.stroke()
ctx.beginPath()
ctx.arc(350, 200, 6, 0, 2* Math.PI) // ------------------------ 右眼
ctx.stroke()
ctx.beginPath()
ctx.arc(300, 300, 150, 0, 1 * Math.PI) ------------------------- 嘴
ctx.stroke()
}
复制代码
0-1之间
ctx.beginPath();
ctx.moveTo(100, 100)
ctx.lineTo(100, 300)
ctx.strokeStyle = 'red'
ctx.lineWidth = 20
ctx.lineCap = 'round' // ------------------ 设置线段终点的样子为圆形
// 注意:全部的状态设置都必须在 stroke绘画前面
ctx.stroke()
复制代码
img:图片
x:在画布上放置图像的 x 坐标
y:在画布上放置图像的 y 坐标
witdh:图像的宽度
height:图像的高度
context.font = "" 设置或返回字体属性
image/png
new Image(width, height) 用于生成 HTMLImageElement 实例
new Image(width, heght)
mounted() {
const limg = require('../images/1.jpg');
const img = new Image(200, 200); ------------- 参数分别是 width 和 height
img.src = limg; ---------------- 除了src,还有currentSrc表示当前src,由于src能够动态指定
img.onload = function() {
console.log('加载完成');
document.body.appendChild(img); -------------- 插入文档
}
img.onerror = function() {
console.log('错误')
}
}
复制代码
------
canvas实现生成图片保存到本地
实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#imgContainer {
width: 400px;
height: 400px;
border: 1px solid black;
}
</style>
</head>
<body>
<div>
<img src="./002.jpg" alt="002.jpg" id="imgx">
<button id="button">生成图片</button>
<div id="imgContainer"></div>
</div>
<script>
window.onload = function() {
const imgx = document.getElementById('imgx')
const button = document.getElementById('button')
const imgContainer = document.getElementById('imgContainer')
button.addEventListener('click', clickButton, false)
function clickButton() {
combine()
}
function combine() {
const canvas = document.createElement('canvas') // -------- 建立canvas标签
canvas.width = 500
canvas.height = 500
canvas.style = "border: 1px solid red"
document.documentElement.appendChild(canvas) // ------------ 添加到HTML的DOM中
const context = canvas.getContext('2d') // ----------------- 获取渲染上下文和绘画功能
context.drawImage(imgx, 0, 0, 300, 300) // ----------------- drawImage() 生成图片
context.fillStyle = 'white';
context.font = '30px Georgia';
context.fillText('生成的图片', 60, 60) // ------------------- 填充文字
const currentUrl = canvas.toDataURL('image/png') // -------- toDataURL() 返回图片的 URI
imgContainer.innerHTML = `<img src=${currentUrl}>` // ------ 填充内容
}
}
</script>
</body>
</html>
复制代码
context.getImageData(x,y,width,height)
width
height
data
三个属性<!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>
<script>
class CanvasPartiular {
constructor() {
this.clientWidth = null // html宽度
this.clientHeight = null // html高度
this.sx = null // canvas的drawImage画图的x坐标,同时也是图片的宽度
this.sy = null // canvas的drawImage画图的y坐标,同时也是图片的高度
this.canvas = null // canvas实例
this.context = null // canvas的渲染上下文
this.img = null // new Image() 生成的图片
this.imageData = null // iamgeData对象,包括width,height,data数组
this.dotArr = [] // ------------------------------ 用来收集像素点,成员是一个包含x,y, cricle的对象
this.createCanvas() // 建立函数
this.createImage() // 建立函数
}
createCanvas = () => {
const HTML = document.documentElement
const clientWidth = HTML.clientWidth
const clientHeight = HTML.clientHeight
const canvas = this.canvas = document.createElement('canvas') // ------------- 建立canvas
canvas.width = this.clientWidth = clientWidth
canvas.height = this.clientHeight = clientHeight
canvas.style.border = '1px solid black';
this.context = canvas.getContext('2d') // ------------------------------------ 获取context
document.body.appendChild(canvas)
}
createImage = () => {
const img = this.img = new Image()
img.src = './5.jpg'
if (img.complete) { // -------------------------- if else保证了图片加载完成后再执行 init 方法
this.init()
}
else {
img.onload = this.init
}
}
init = () => {
const sx = this.sx = this.clientWidth/2 - this.img.width/2;
// 横坐标和宽,能够本身用两个正方形验证
const sy = this.sy = this.clientHeight/2 - this.img.height/2;
this.context.drawImage(this.img, sx, sy)
// 画图
const imageData = this.imageData = this.context.getImageData(sx, sy, this.img.width, this.img.height)
// 获取 imageData 对象
this.getDotList()
}
// 重点是该函数,获取dotList数组
getDotList = () => {
const dataArr = this.imageData.data
const imgWidth = this.imageData.width
for(let x = 0; x < imgWidth; x = x + 6) {
// x 表示横轴的点,每次增长6则每一个点之间有间隙
for(let y = 0; y < this.imageData.height; y = y + 6 ) {
// y 表示纵轴
const iDotPositionInArray = (y * imgWidth + x) * 4
// (1) y * imgWidth:表示该点的位置已是第y行了,即有 y * imgWidth个点
// (2) y * imgWidth + x:表示该点的具体位置,即前面有 y * imgWidth + x 个点
// (3) (y * imgWidth + x) * 4:表示再data数组中,该点的位置。由于每一个点占据data数组的4个成员
if(dataArr[iDotPositionInArray + 3] > 256/2 && dataArr[iDotPositionInArray] < 100) {
// iDotPositionInArray + 3:表示该点的 Alpha 透明度
// Alpha在0 - 256之间
// 256/2:表示该点可见,不是透明的
this.dotArr.push({x, y, radius: 2}) // x,y表示坐标,radius半径,半径随便设合适便可
}
}
}
this.draw()
}
draw = () => {
const context = this.context
// context.clearRect(0, 0, this.clientWidth, this.clientHeight);
// clearRect清除矩形的canvas,即清除drawImage的图片,下面从新画点图
context.fillStyle = 'black'
this.dotArr.forEach(({x,y,radius}) => {
context.save()
context.beginPath()
context.arc(x, y, radius, 0, 360 * Math.PI/180) // 画圆
context.fill()
context.restore()
})
}
}
new CanvasPartiular()
</script>
</body>
</html>
复制代码
cos(180° - a) = - cos(a)
sin(180° - a) = sin(a)
sin(2a) = 2 * sin(a) * cos(a)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
html {
height: 100%;
}
body {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#canvas {
border: 1px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width=600 height=600></canvas>
<script>
const canvas = document.getElementById('canvas')
const cWidth = canvas.width
const cHeight = canvas.height
const context = canvas.getContext('2d')
const center = context.translate(cWidth/2, cHeight/2)
const arcRadius = cWidth/2;
const rate = cWidth / 600; // -------- 比例,以当前大小600为基准,若是cWidth = 1200则rate为2,放大一倍
function clockBorder() { // 时钟外圆边框
context.beginPath()
context.arc(0, 0, (arcRadius - 20/2) * rate, 0, 360 * Math.PI/180) // 时钟大圆
context.strokeStyle='blue'
context.lineWidth = 20 * rate;
context.stroke()
context.closePath()
}
function clockNumber() { // 数字刻度
var numberArr = [3,4,5,6,7,8,9,10,11,12,1,2];
context.font = `${30 * rate}px Arail`;
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillStyle = 'black';
numberArr.forEach((item, index) => {
const rad = 2 * Math.PI / 12 * index;
// ----------------------------------------------------- 2 * Math.PI / 12 表示一个小时所占的弧度
// ----------------------------------------------------- rad表示当前点数的弧度
const x = Math.cos(rad) * (arcRadius - 60) * rate;
const y = Math.sin(rad) * (arcRadius - 60) * rate;
context.fillText(item, x, y) // ------------------------ 填充数字
})
}
function clockDot() { // 60份的刻度,和上面同样
for(let i = 0; i < 60; i++) {
const rad = 2 * Math.PI / 60 * i;
const x = Math.cos(rad) * ( arcRadius - 34) * rate;
const y = Math.sin(rad) * ( arcRadius - 34) * rate;
context.beginPath()
context.arc(x, y, 4 * rate, 0, 360 * Math.PI/180)
if (i % 5 === 0) {
context.fillStyle = '#000' // 整点的数字对应的刻度颜色高亮
} else {
context.fillStyle = '#ccc'
}
context.fill() // -------------------------------------- 画圆填充颜色
}
}
function clockPointer(hour, minute, second) { // ----------- 时针分针秒针
// 时针
const radHour = 2 * Math.PI / 12 * hour; // ----------------------------------------- 一小时的弧度
const radHourMinute = 2 * Math.PI / 12 / 60 * minute; // ---------------------------- 一分钟的弧度
const radHourtMinuteSecond = 2 * Math.PI / 12 / 60 / 60 * second; // ---------------- 一秒钟的弧度
context.save() // --------------------- 保存当前环境状态,由于画别的分针,秒针时不能用如今的旋转后的环境
context.rotate(radHour + radHourMinute + radHourtMinuteSecond) // -------------------- 旋转的总角度
context.beginPath()
context.lineWidth = 10 * rate;
context.lineCap = 'round';
context.strokeStyle='black'
context.moveTo(0, 10 * rate)
context.lineTo(0, (-arcRadius/2 + 20) * rate)
context.stroke()
context.restore() // ---------------------------------------------------- 旋转后,获取旋转以前的状态
// 分针
const radMinute = 2 * Math.PI / 60 * minute;
context.save()
context.beginPath()
context.rotate(radMinute)
context.lineCap = 'round'
context.strokeStyle='black'
context.lineWidth = 6 * rate;
context.moveTo(0, 10 * rate)
context.lineTo(0, (-arcRadius + 120) * rate)
context.stroke()
context.restore()
// 秒针
const radSecond = 2 * Math.PI / 60 * second;
context.save()
context.beginPath()
context.rotate(radSecond)
context.lineCap = 'round'
context.lineWidth = 5 * rate;
context.moveTo(-3 * rate, 14 * rate)
context.lineTo(3 * rate, 14 * rate)
context.lineTo(1* rate, (-arcRadius + 90) * rate)
context.lineTo(-1* rate, (-arcRadius + 90) * rate)
context.fillStyle = 'blue'
context.fill()
context.restore()
// 圆点
context.beginPath()
context.arc(0, 0, 4 * rate, 0, 2 * Math.PI)
context.fillStyle = '#fff'
context.fill()
}
clockDot()
clockBorder()
clockNumber()
clockPointer(4, 15, 60)
setInterval(() => {
context.clearRect(-300, -300, 600, 600) // ------------------------------ 清除后,重新绘制
clockBorder()
clockDot()
clockNumber()
const date = new Date()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
clockPointer(hour, minute, second)
}, 1000)
</script>
</body>
</html>
复制代码
<input type="range"> 滑动条
<input>
,<img>
,<link>
,<br>
,<hr>
,<meta>
<input type="range" min="1" max="10" step="1" value="3" />
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
<!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 style="background: #777;">
<canvas id="canvas" width="400" height="400"
style="border: 1px solid red; display: block; margin: 0 auto;"
></canvas>
<input
type="range"
id="range"
style="display: block;margin: 20px auto;width: 400px"
min="0.1"
max="3.0"
step="0.01"
value="1.0"
>
<script>
const canvas = document.getElementById('canvas')
const slider = document.getElementById('range')
const context = canvas.getContext('2d')
const img = new Image()
img.src = './002.jpg'
if (img.complete) {
init()
} else {
img.onload = init
}
function init() {
const scale = slider.value
drawImageByScale(scale)
}
function drawImageByScale(scale) {
const imgWidth = img.width * scale;
const imgHeight = img.height * scale;
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
const dx = canvasWidth/2 - imgWidth/2;
const dy = canvasHeight/2 - imgHeight/2;
context.clearRect(0, 0, canvasWidth, canvasWidth)
context.save()
context.beginPath()
context.drawImage(img, dx, dy, imgWidth, imgHeight)
context.restore()
}
slider.onmousemove = function() {
const scale = slider.value
drawImageByScale(scale)
}
</script>
</body>
</html>
复制代码
n
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#canvas { // 设置canvas的背景,能够用别的图片做为底层,将canvas移动到图片上重叠
background-image: url('./4.jpg');
background-position: center;
background-repeat: no-repeat;
background-size: cover;
border: 1px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width=400 height=330></canvas>
<script>
const canvas = document.getElementById('canvas')
const canvasWidth = canvas.width
const canvasHeight = canvas.height
if (canvas.getContext) { // ----------------------------------- 浏览器是否支持canvas
const context = canvas.getContext('2d')
const img = new Image()
img.src = './002.jpg'
if (img.complete) { // --------------------------------- if...else保证图标加载完成后执行drawImages()
drawImages()
} else {
img.onload = drawImages
}
let isEmit = false // 用于标志移动时时候能够画线条了,由于只有鼠标按下后或者touch后生效,结束后又设为false
let drawDots = 0 // 用来记录如今画的线段后,canvas源图像透明的点,>2/3则显示整个canvas背景
function drawImages() {
context.drawImage(img, 0, 0, canvas.width, canvas.height)
context.globalCompositeOperation = 'destination-out'
// ---------------------------------- context.globalCompositeOperation
// ---------------------------------- 'destination-out' 目标图被源图占据的部分将透明
// 监听鼠标和touch事件
canvas.addEventListener('mousedown', moveStart, false);
canvas.addEventListener('touchstart', moveStart, false);
canvas.addEventListener('mousemove', move, false);
canvas.addEventListener('touchmove', move, false);
canvas.addEventListener('mouseup', moveEnd, false);
canvas.addEventListener('touchend', moveEnd, false);
context.lineWidth = 30
context.lineCap = 'round'
context.lineJoin = 'round'
context.strokeStyle = 'white'
}
function moveStart(e) {
isEmit = true // 点击后才准许移动时画图
drawLineFn(e)
}
function move(e) {
if (!isEmit) return; // 不成立,则返回
drawLineFn(e)
}
function moveEnd(e) {
isEmit = false // 结束后移动不能再画图
drawLineFn(e)
paintAll() // 判断是否所有显示背景图,当画到必定程度,直接能够显示所有
}
function getDot(e) {
const dotx = e.type.match('mouse') ? e.clientX : e.changedTouches[0].clientX;
const doty = e.type.match('mouse') ? e.clientY : e.changedTouches[0].clientY;
return { dotx, doty }
}
function drawLineFn(e) {
const {dotx, doty} = getDot(e)
context.save()
context.beginPath()
context.moveTo(dotx, doty)
context.lineTo(dotx + 0.11, doty + 0.1) // 画线
context.stroke()
context.closePath()
context.restore()
}
function paintAll() {
const imageData = context.getImageData(0, 0, canvasWidth, canvasHeight)
const allDots = imageData.width * imageData.height; // ----------- 图片全部的点
for(let i = 0; i < allDots; i++) {
if(imageData.data[i*4 + 3] === 0) { // ------------------------- 统计透明的点
drawDots++ // 统计透明的点
}
}
if (drawDots > allDots * 2/3) { // 透明占总数点的比例
context.save()
context.beginPath()
context.fillRect(0, 0, canvasWidth, canvasHeight) // 用源图占满整个目标图,globalCompositeOperation的运用
context.closePath()
context.restore()
}
}
};
</script>
</body>
</html>
复制代码
canvas-api:www.w3school.com.cn/tags/html_r…
MDN:developer.mozilla.org/zh-CN/docs/…
ImageData对象:developer.mozilla.org/zh-CN/docs/…
canvas转成图片保存:segmentfault.com/a/119000001…
粒子动画3:juejin.im/post/57e7a7…
粒子动画1:juejin.im/post/57cda0…
时钟动画:www.imooc.com/video/11261
globalCompositeOperation1:www.w3school.com.cn/tags/canvas…
globalCompositeOperation2:www.w3school.com.cn/tiy/t.asp?f…
刮刮卡1:juejin.im/post/5ca18a…
刮刮卡2:juejin.im/post/5d8a3d…vue