[深刻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