前端之學(xué)習(xí) canvas 的基礎(chǔ)語法

前端之學(xué)習(xí) canvas 的基礎(chǔ)語法

前言

今天坞生,學(xué)習(xí)了 canvas 的一些語法知識和使用计济,在這里分享給大家一起學(xué)習(xí)交流
首先粱年,我們應(yīng)該了解 canvas 是什么钦睡,有什么用逆皮,文章最后振诬,我們將使用 canvas 繪制一個(gè)刮刮卡的中獎特效蘸吓!

什么是 canvas

定義:是 HTML5 提供的一種新標(biāo)簽, ie9 才開始支持的群扶,Canvas 是一個(gè)矩形區(qū)域的畫布透葛,可以用 JS 控制每一個(gè)像素在上面繪畫笨使。canvas 標(biāo)簽使用 JavaScript 在網(wǎng)頁上繪制圖像,本身不具備繪圖功能僚害。canvas 擁有多種繪制路徑硫椰、矩形、圓形萨蚕、字符以及添加圖像的方法靶草。

vscode 代碼提示

在了解 canvas 之后,大家一定迫不及待地想加入 canvas 的學(xué)習(xí)之路吧岳遥。

  • 首先奕翔,我們使用 vscode 新建一個(gè) html 文件,
    并在body標(biāo)簽中使用 canvas 標(biāo)簽浩蓉,給定一定的大小派继,這樣,canvas 畫布就建好了
<canvas class="canvas" width="600" height="400"></canvas>
  • script標(biāo)簽中捻艳,開始使用我們神奇的畫筆驾窟,工欲善其事必先利其器,開始之前认轨,我們使用 /** @type {HTMLCanvasElement} */ ,vscode 會給出 canvas 相關(guān) api 的智能提示绅络,讓我們更順暢的完成我們的代碼
  // 獲取畫布
  /** @type {HTMLCanvasElement} */
  let canvas = document.querySelector('.canvas')
  // 獲取畫布上下文對象
  let ctx = canvas.getContext('2d')
繪制二次貝塞爾曲線

首先,我們使用貝塞爾曲線繪制一個(gè)聊天氣泡
在 canvas 中,一般相鄰兩個(gè)值為一組對象恩急,代表 x 坐標(biāo)和 y 坐標(biāo)杉畜,而瀏覽器左上角即為原點(diǎn)位置
比如:
ctx.moveTo(200, 300)中 200 和 300 分別代表 x 軸和 y 軸坐標(biāo),即表示一個(gè)點(diǎn)
ctx.quadraticCurveTo(150, 300, 150, 200) 中兩兩為一組衷恭,即代表了兩個(gè)點(diǎn)的坐標(biāo)
二次貝塞爾曲線通過和起始點(diǎn)的三個(gè)點(diǎn)既可以畫出一條圓滑的曲線,如下圖:

   // 畫筆移動到指定位置
  ctx.moveTo(200, 300)
  // 開始畫筆
  ctx.beginPath()
  // 繪制多條貝塞爾曲線
  ctx.quadraticCurveTo(150, 300, 150, 200)
  ctx.quadraticCurveTo(150, 100, 300, 100)
  ctx.quadraticCurveTo(450, 100, 450, 200)
  ctx.quadraticCurveTo(450, 300, 250, 300)
  ctx.quadraticCurveTo(250, 350, 150, 350)
  ctx.quadraticCurveTo(200, 350, 200, 300)
  // 填充顏色
  ctx.stroke()
  // 結(jié)束畫筆
  ctx.closePath()
聊天氣泡

這樣此叠,一個(gè)聊天氣泡框就畫好了,后面我們還會學(xué)習(xí)如何修改顏色或填充顏色

繪制三次貝塞爾曲線

三次貝塞爾曲線和二次貝塞爾曲線類似匾荆,不同的是每次使用四個(gè)點(diǎn)完成一條曲線的繪畫拌蜘,使用的方法名也不同
接下來,我們使用三次貝塞爾曲線繪畫一個(gè)愛心

一段三次貝塞爾曲線如下:

  //開始畫筆
  ctx.beginPath()
  //三次貝塞爾曲線畫一個(gè)愛心
  ctx.moveTo(300, 200)
  ctx.bezierCurveTo(350, 150, 500, 200, 300, 350)
  //開始畫筆
  ctx.beginPath()
  //三次貝塞爾曲線畫一個(gè)愛心
  ctx.moveTo(300, 200)
  ctx.bezierCurveTo(350, 150, 500, 200, 300, 350)
  ctx.bezierCurveTo(100, 200, 250, 150, 300, 200)
  ctx.stroke()
  // 結(jié)束畫筆
  ctx.closePath()
繪制文字
  • font 設(shè)置文字大小和字體

     //設(shè)置文字大小和字體
      ctx.font = "30px Microsoft Yahei"
    
  • strokeStyle 設(shè)置文字顏色

      // 設(shè)置文字顏色
       ctx.strokeStyle = "#ffcc2a"
    
  • fillText 設(shè)置文字內(nèi)容和位置

      // 設(shè)置文字內(nèi)容和位置
       ctx.fillText("Hello World", 100, 100)
    
  • textAlign 設(shè)置文字對齊方式

      //設(shè)置文字對齊方式 start left center right end
      ctx.textAlign = "start"
    
  • textBaseline 文本基線對齊

      //文本基線對齊 start top middle bottom end
      ctx.textBaseline = "middle"
    
  • direction 文字方向

       //文字方向 rtl ltr
       ctx.direction = "rtl"
    
  • strokeText 設(shè)置文字輪廓

      //設(shè)置文字輪廓
      ctx.strokeText("Hello World", 300, 100)
    
  • 預(yù)測量文字寬度

/*
    預(yù)測量文字寬度
    actualBoundingBoxAscent 實(shí)際文字上邊距
    actualBoundingBoxDescent 實(shí)際文字下邊距
    actualBoundingBoxLeft 實(shí)際文字左邊距
    actualBoundingBoxRight 實(shí)際文字右邊距
    fontBoundingBoxAscent 字體上邊距
    fontBoundingBoxDescent 字體下邊距
    width 文字寬度
 */
  let result = ctx.measureText("Hello World")
  console.log(result)
繪制圖片

繪制圖片有三種方式:

  • 第一種:默認(rèn)繪制圖片的長寬
    let img = new Image()
    img.src = "./imgs/girl.webp"
    img.onload = () => {
      // 第一種繪制圖片方式 (默認(rèn)繪制圖片的長寬)
      ctx.drawImage(img, 0, 0)
    }
    
  • 第二種繪制圖片方式: 第一組坐標(biāo)參數(shù):繪制起點(diǎn)位置牙丽,第二組坐標(biāo)參數(shù):繪制長寬
  let img = new Image()
  img.src = "./imgs/girl.webp"
  img.onload = () => {
    //第二種繪制圖片方式 (第一組:繪制起點(diǎn)位置简卧,第二組:繪制長寬)
    ctx.drawImage(img, 0, 0, 200, 400)
  }
  • 第三種繪制圖片方式 第一組坐標(biāo)參數(shù):裁剪起點(diǎn)位置,第二組坐標(biāo)參數(shù):裁剪長寬烤芦,第三組坐標(biāo)參數(shù):繪制起點(diǎn)位置举娩,第四組坐標(biāo)參數(shù):繪制長寬
  let img = new Image()
  img.src = "./imgs/girl.webp"
  img.onload = () => {
    //第三種繪制圖片方式(第一組:裁剪起點(diǎn)位置,第二組:裁剪長寬构罗,第三組:繪制起點(diǎn)位置铜涉,第四組:繪制長寬)
    ctx.drawImage(img, 50, 50, 300, 150, 0, 0, 200, 200)
  }
繪制視頻

body 標(biāo)簽中添加 video 標(biāo)簽和 button 按鈕控制視頻播放

<video class="video" src="./video/video.mp4" controls></video>
<button class="btn">播放/暫停</button>
  //獲取視頻對象
  let video = document.querySelector('.video')
  //獲取按鈕
  let btn = document.querySelector('.btn')
  btn.onclick = () => {
    //播放或暫停視頻
    video.paused ? video.play() : video.pause()
    render()
  }
  function render() {
    //畫出視頻圖像
    ctx.drawImage(video, 0, 0, 600, 400)

    //requestAnimationFrame 會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成遂唧,并且重繪或回流的時(shí)間間隔緊緊跟隨瀏覽器的刷新頻率芙代,一般來說,這個(gè)頻率為每秒60幀盖彭。循環(huán)調(diào)用 render纹烹,就可以實(shí)現(xiàn)播放視頻的效果
    requestAnimationFrame(render)
  }
位移變換

首先,給 canvas 添加 border 樣式召边,并且繪制一個(gè)矩形

<style>
  .canvas {
    border: 1px solid #000;
  }
</style>
<script lang="js">
  /** @type {HTMLCanvasElement} */
  let canvas = document.querySelector('.canvas')
  //獲取畫筆上下文對象
  let ctx = canvas.getContext('2d')
  ctx.fillRect(0, 0, 100, 50)
</script>

canvas 的位移變化都是坐標(biāo)系的改變铺呵,即瀏覽器左上角原點(diǎn)形成的坐標(biāo)系的變化

  //坐標(biāo)系位移
  ctx.translate(100, 100)
 // 縮放
  ctx.scale(2, 2)
  // 旋轉(zhuǎn)
  ctx.rotate(Math.PI / 4)

通過矩陣的方式位移變換

  //平移 1,0,0,1分別代表x軸和y軸的縮放比例,200,200分別代表x軸和y軸的位移
  ctx.transform(1, 0, 0, 1, 200, 200)
  //旋轉(zhuǎn) transform(Math.cos(Math.PI / 4), Math.sin(Math.PI / 4), -Math.sin(Math.PI / 4), Math.cos(Math.PI / 4), 0, 0)
  ctx.transform(1, 1, -1, 1, 0, 0)
  //縮放  2,2分別代表x軸和y軸的縮放比例
  ctx.transform(2, 0, 0, 2, 0, 0)
線段和虛線樣式設(shè)置

繪制線段

    ctx.moveTo(200, 200)
    // 繪制直線
    ctx.lineTo(350, 250)
    // 繪制直線
    ctx.lineTo(440, 200)

繪制虛線

繪制虛線先繪制線段隧熙,再設(shè)置讓線段分割成為虛線

    ctx.moveTo(200, 200)
    // 繪制直線
    ctx.lineTo(350, 250)
    // 繪制直線
    ctx.lineTo(440, 200)
    //虛線樣式
    ctx.setLineDash([20, 20])
    //虛線偏移量
    ctx.lineDashOffset = index

使用 render 方法讓虛線移動

  let index = 0
  //線段移動
  function render() {
    //清除上次繪畫的矩形
    ctx.clearRect(0, 0, 600, 400)

    index++

    if (index > 400) {
      index = 0
    }

    ctx.moveTo(200, 200)
    // 繪制直線
    ctx.lineTo(350, 250)
    // 繪制直線
    ctx.lineTo(440, 200)

    // 設(shè)置線條樣式
    ctx.lineWidth = 1

    // 設(shè)置線條端點(diǎn)樣式片挂,round為圓形,square為方形贞盯,butt為直角
    ctx.lineCap = 'butt'

    //設(shè)置線段連接處樣式音念,round為圓形,bevel為斜角躏敢,mitter為直角
    ctx.linejoin = 'mitter'
    // 設(shè)置線段連接處最大長度
    ctx.miterLimit = 6
    //虛線樣式
    ctx.setLineDash([20, 20])
    //虛線偏移量
    ctx.lineDashOffset = index

    ctx.stroke()
    //循環(huán)調(diào)用闷愤,實(shí)現(xiàn)動畫效果
    requestAnimationFrame(render)
  }
  render()
陰影

陰影屬性和 css 的陰影類似

  • shadowOffsetX: 陰影的水平偏移量
  • shadowOffsetY: 陰影的垂直偏移量
  • shadowBlur: 陰影的模糊程度
  • shadowColor: 陰影的顏色
  // path2D 對象 用于存儲路徑
  let heartPath = new Path2D()
  //二次貝塞曲線畫一個(gè)愛心
  heartPath.moveTo(300, 200)
  heartPath.bezierCurveTo(350, 150, 500, 200, 300, 350)
  heartPath.bezierCurveTo(100, 200, 250, 150, 300, 200)

  // shadowOffsetX: 陰影的水平偏移量 shadowOffsetY: 陰影的垂直偏移量 shadowBlur: 陰影的模糊程度 shadowColor: 陰影的顏色
  ctx.shadowOffsetX = 10
  ctx.shadowOffsetY = 10
  ctx.shadowBlur = 5
  ctx.shadowColor = 'rgba(255, 200, 200, 1)'
  ctx.stroke(heartPath)
裁剪圖片

繪制圖片后,使用 ctx.clip()則可對圖片進(jìn)行裁剪
如下:原本圖片被愛心形狀裁剪

  // path2D對象 用于創(chuàng)建路徑
  let heart = new Path2D()
  //二次貝塞曲線畫一個(gè)愛心
  heart.moveTo(300, 200)
  heart.bezierCurveTo(350, 150, 500, 200, 300, 350)
  heart.bezierCurveTo(100, 200, 250, 150, 300, 200)
  //clip() 方法從原始畫布中剪切任意形狀和尺寸的區(qū)域
  ctx.clip(heart)

  let img = new Image()
  img.src = "./imgs/girl.webp"
  img.onload = () => {
    //第三種繪制圖片方式(第一組:裁剪起點(diǎn)位置父丰,第二組:裁剪長寬,第三組:繪制起點(diǎn)位置,第四組:繪制長寬)
    ctx.drawImage(img, 80, 70, 250, 250, 200, 180, 200, 300)
    ctx.lineWidth = 5
    ctx.strokeStyle = "red"
    ctx.stroke(heart)
  }
  ctx.stroke(heart)
合成圖像

使用合成圖像蛾扇,做一個(gè)刮刮卡抽獎效果

  • 首先畫布 canvas 樣式和刮刮卡都定位到左上角攘烛,使用 z-index 使畫布居于抽獎文字上層
  • 繪制刮刮卡圖片,遮蓋文字
  • 鼠標(biāo)按下時(shí)镀首,開啟繪畫坟漱,globalCompositeOperation 屬性設(shè)置或返回如何將一個(gè)源(新的)圖像繪制到目標(biāo)(已有)的圖像上,由于屬性很多更哄,具體參考canvas 中的合成------globalCompositeOperation 一文 ,這里使用destination-out 從畫布中刪除刮刮卡圖像芋齿,畫筆繪畫時(shí)繪畫弧形模仿橡皮差效果
  • 鼠標(biāo)抬起時(shí),停止繪畫
<style>
  * {
    margin: 0;
    padding: 0;
  }

  .ggk {
    width: 600px;
    height: 400px;
    line-height: 400px;
    text-align: center;
    font-weight: 900;
    overflow: hidden;
    font-size: 30px;
    position: absolute;
    left: 0;
    top: 0;
    z-index: -1;
  }

  .canvas {
    border: 1px solid #000;
    position: absolute;
    left: 0;
    top: 0;
    z-index: 1;
  }
</style>

<body>
  <div class="ggk">謝謝惠顧</div>
  <canvas class="canvas" width="600" height="400"></canvas>
</body>
  //繪制刮刮卡圖片
  let img = new Image()
  img.src = './imgs/ggk.webp'
  img.onload = () => {
    ctx.drawImage(img, 0, 0, 600, 400)
  }

  // isDraw 用來判斷是否在畫布上繪制
  let isDraw = false
  canvas.onmousedown = (e) => {
    isDraw = true
  }
  canvas.onmouseup = (e) => {
    isDraw = false
  }
  // onmousemove 在鼠標(biāo)指針移動時(shí),不斷繪畫
  canvas.onmousemove = (e) => {
    if (isDraw) {
      // 獲取鼠標(biāo)指針的坐標(biāo)
      let x = e.pageX
      let y = e.pageY
      // globalCompositeOperation 屬性設(shè)置或返回如何將一個(gè)源(新的)圖像繪制到目標(biāo)(已有)的圖像上
      // destination-out 從目標(biāo)圖像中刪除源圖像
      ctx.globalCompositeOperation = 'destination-out'
      // arc() 方法使用一個(gè)中心點(diǎn)和半徑成翩,為畫布的當(dāng)前子路徑添加一條弧觅捆。
      ctx.arc(x, y, 20, 0, Math.PI * 2)
      ctx.fill()
    }
  }
  let random = Math.random()
  if (random < 0.5) {
    let ggk = document.querySelector('.ggk')
    ggk.innerHTML = '恭喜你中獎了'
  }

刮刮卡刮獎前

刮刮卡刮獎后

原創(chuàng)不易,覺得文章還不錯(cuò)或者感興趣可以點(diǎn)贊或者收藏一下哦麻敌!歡迎留言評論和關(guān)注一下栅炒!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市术羔,隨后出現(xiàn)的幾起案子赢赊,更是在濱河造成了極大的恐慌,老刑警劉巖级历,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件释移,死亡現(xiàn)場離奇詭異,居然都是意外死亡寥殖,警方通過查閱死者的電腦和手機(jī)玩讳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扛禽,“玉大人锋边,你說我怎么就攤上這事”嗦” “怎么了豆巨?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掐场。 經(jīng)常有香客問我往扔,道長,這世上最難降的妖魔是什么熊户? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任萍膛,我火速辦了婚禮,結(jié)果婚禮上嚷堡,老公的妹妹穿的比我還像新娘蝗罗。我一直安慰自己艇棕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布串塑。 她就那樣靜靜地躺著沼琉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桩匪。 梳的紋絲不亂的頭發(fā)上打瘪,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音傻昙,去河邊找鬼闺骚。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妆档,可吹牛的內(nèi)容都是我干的僻爽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼过吻,長吁一口氣:“原來是場噩夢啊……” “哼进泼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起纤虽,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤乳绕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后逼纸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洋措,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年杰刽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菠发。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贺嫂,死狀恐怖滓鸠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情第喳,我是刑警寧澤糜俗,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站曲饱,受9級特大地震影響悠抹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扩淀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一楔敌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驻谆,春花似錦卵凑、人聲如沸庆聘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掏觉。三九已至,卻和暖如春值漫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背织盼。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工杨何, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沥邻。 一個(gè)月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓危虱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唐全。 傳聞我的和親對象是個(gè)殘疾皇子埃跷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容