目錄
專案簡介" >專案簡介
專案特點" >專案特點
sprite精靈實作" >sprite精靈實作
子類別" >子類別
scene場景的實作" >scene場景的實作
首頁 web前端 H5教程 HTML5 canvas實作畫圖程式(附程式碼)

HTML5 canvas實作畫圖程式(附程式碼)

Aug 01, 2018 pm 01:49 PM
canvas html5 javascript

這篇文章要跟大家介紹的內容是關於HTML5 canvas實作畫圖程式(附程式碼),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

專案簡介

整個專案分為兩個大部分

  1. #場景
    場景負責canvas控制,事件監聽,動畫處理

  2. 精靈
    精靈則指的是每一種可以繪製的canvas元素

Demo示範位址

專案特點

可擴展性強

sprite精靈實作

#父類別

1

2

3

4

5

6

7

8

9

10

11

12

class Element {

  constructor(options = {

    fillStyle: 'rgba(0,0,0,0)',

    lineWidth: 1,

    strokeStyle: 'rgba(0,0,0,255)'

  }) {

    this.options = options

  }

  setStyle(options){

    this.options =  Object.assign(this.options. options)

  }

}

登入後複製
  1. 屬性:

  • #options中儲存了所有的繪圖屬性

    • fillStyle:設定或傳回用於填滿繪畫的顏色、漸層或模式

    • strokeStyle:設定或傳回用於筆觸的顏色、漸層或模式

    • lineWidth:設定或傳回目前的線條寬度

    • 使用的都是getContext("2d")對象的原生屬性,此處只列出了這三種屬性,需要的話還可以繼續擴充。

  • 有需要可以繼續擴充

  1. #方法:

  • #setStyle方法用來重新設定目前精靈的屬性

  • #有需要可以繼續擴充

所有的精靈都繼承Element類別。

子類別

子類別就是每個精靈元素的具體實現,這裡我們介紹一遍Circle元素的實現

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

class Circle extends Element {

  // 定位点的坐标(这块就是圆心),半径,配置对象

  constructor(x, y, r = 0, options) {

    // 调用父类的构造函数

    super(options)

    this.x = x

    this.y = y

    this.r = r

  }

  // 改变元素大小

  resize(x, y) {

    this.r = Math.sqrt((this.x - x) ** 2 + (this.y - y) ** 2)

  }

  // 移动元素到新位置,接收两个参数,新的元素位置

  moveTo(x, y) {

    this.x = x

    this.y = y

  }

  // 判断点是否在元素中,接收两个参数,点的坐标

  choose(x, y) {

    return ((x - this.x) ** 2 + (y - this.y) ** 2) < (this.r ** 2)

  }

  // 偏移,计算点和元素定位点的相对偏移量(ofsetX, offsetY)

  getOffset(x, y) {

    return {

      x: x - this.x,

      y: y - this.y

    }

  }

  // 绘制元素实现,接收一个ctx对象,将当前元素绘制到指定画布上

  draw(ctx) {

    // 取到绘制所需属性

    let {

      fillStyle,

      strokeStyle,

      lineWidth

    } = this.options

    // 开始绘制beginPath() 方法开始一条路径,或重置当前的路径

    ctx.beginPath()

    // 设置属性

    ctx.fillStyle = fillStyle

    ctx.strokeStyle = strokeStyle

    ctx.lineWidth = lineWidth

    // 画圆

    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)

    // 填充颜色

    ctx.stroke()

    ctx.fill()

    // 绘制完成

  }

  // 验证函数,判断当前元素是否满足指定条件,此处用来检验是否将元素添加到场景中。

  validate() {

    return this.r >= 3

  }

}

登入後複製

arc()方法建立弧/曲線(用於建立圓或部分圓)

  • x    圓的中心的x 座標。

  • y    圓的中心的 y 座標。

  • r    圓的半徑。

  • sAngle    起始角,以弧度計。 (弧的圓形的三點鐘位置是 0 度)。

  • eAngle    結束角,以弧度計。

  • counterclockwise    可選。規定應該逆時針還是順時針繪圖。 False = 順時針,true = 逆時針。

注意事項:

  • 建構子的形參只有兩個是必須的,就是定位點的座標。

  • 其它的形參都必須有預設值。

所有方法的呼叫時機

  • 我們在畫布上繪製元素的時候回呼叫resize方法。

  • 移動元素的時候呼叫moveTo方法。

  • choose會在滑鼠按下時調用,判斷當前元素是否被選中。

  • getOffset選取元素時調用,判斷選取位置。

  • draw繪製函數,繪製元素到場景時呼叫。

scene場景的實作

  1. #屬性介紹

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

class Sence {

  constructor(id, options = {

    width: 600,

    height: 400

  }) {

    // 画布属性

    this.canvas = document.querySelector('#' + id)

    this.canvas.width = options.width

    this.canvas.height = options.height

    this.width = options.width

    this.height = options.height

    // 绘图的对象

    this.ctx = this.canvas.getContext('2d')

    // 离屏canvas

    this.outCanvas = document.createElement('canvas')

    this.outCanvas.width = this.width

    this.outCanvas.height = this.height

    this.outCtx = this.outCanvas.getContext('2d')

    // 画布状态

    this.stateList = {

      drawing: 'drawing',

      moving: 'moving'

    }

    this.state = this.stateList.drawing

    // 鼠标状态

    this.mouseState = {

    // 记录鼠标按下时的偏移量

      offsetX: 0,

      offsetY: 0,

      down: false, //记录鼠标当前状态是否按下

      target: null //当前操作的目标元素

    }

    // 当前选中的精灵构造器

    this.currentSpriteConstructor = null

    // 存储精灵

    let sprites = []

    this.sprites = sprites

    /* .... */

  }

}

登入後複製
  1. 事件邏輯

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

class Sence {

  constructor(id, options = {

    width: 600,

    height: 400

  }) {

  /* ... */

  // 监听事件

    this.canvas.addEventListener('contextmenu', (e) => {

      console.log(e)

    })

    // 鼠标按下时的处理逻辑

    this.canvas.addEventListener('mousedown', (e) => {

    // 只有左键按下时才会处理鼠标事件

      if (e.button === 0) {

      // 鼠标的位置

        let x = e.offsetX

        let y = e.offsetY

        // 记录鼠标是否按下

        this.mouseState.down = true

        // 创建一个临时target

        // 记录目标元素

        let target = null

        if (this.state === this.stateList.drawing) {

        // 判断当前有没有精灵构造器,有的话就构造一个对应的精灵元素

          if (this.currentSpriteConstructor) {

            target = new this.currentSpriteConstructor(x, y)

          }

        else if (this.state === this.stateList.moving) {

          let sprites = this.sprites

          // 遍历所有的精灵,调用他们的choose方法,判断有没有被选中

          for (let i = sprites.length - 1; i >= 0; i--) {

            if (sprites[i].choose(x, y)) {

              target = sprites[i]

              break;

            }

          }

           

          // 如果选中的话就调用target的getOffset方法,获取偏移量

          if (target) {

            let offset = target.getOffset(x, y)

            this.mouseState.offsetX = offset.x

            this.mouseState.offsetY = offset.y

          }

        }

        // 存储当前目标元素

        this.mouseState.target = target

        // 在离屏canvas保存除目标元素外的所有元素

        let ctx = this.outCtx

        // 清空离屏canvas

        ctx.clearRect(0, 0, this.width, this.height)

        // 将目标元素外的所有的元素绘制到离屏canvas中

        this.sprites.forEach(item => {

          if (item !== target) {

            item.draw(ctx)

          }

        })

        if(target){

            // 开始动画

            this.anmite()

        }

      }

    })

    this.canvas.addEventListener('mousemove', (e) => {

    //  如果鼠标按下且有目标元素,才执行下面的代码

      if (this.mouseState.down && this.mouseState.target) {

        let x = e.offsetX

        let y = e.offsetY

        if (this.state === this.stateList.drawing) {

        // 调用当前target的resize方法,改变大小

          this.mouseState.target.resize(x, y)

        else if (this.state === this.stateList.moving) {

        // 取到存储的偏移量

          let {

            offsetX, offsetY

          } = this.mouseState

          // 调用moveTo方法将target移动到新的位置

          this.mouseState.target.moveTo(x - offsetX, y - offsetY)

        }

      }

    })

    document.body.addEventListener('mouseup', (e) => {

      if (this.mouseState.down) {

      // 将鼠标按下状态记录为false

        this.mouseState.down = false

        if (this.state === this.stateList.drawing) {

        // 调用target的validate方法。判断他要不要被加到场景去呢

          if (this.mouseState.target.validate()) {

            this.sprites.push(this.mouseState.target)

          }

        else if (this.state === this.stateList.moving) {

          // 什么都不做

        }

      }

    })

  }

}

登入後複製
  1. 方法介紹

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

class Sence {

// 动画

  anmite() {

    requestAnimationFrame(() => {

      // 清除画布

      this.clear()

      // 将离屏canvas绘制到当前canvas上

      this.paint(this.outCanvas)

      // 绘制target

      this.mouseState.target.draw(this.ctx)

      // 鼠标是按下状态就继续执行下一帧动画

      if (this.mouseState.down) {

        this.anmite()

      }

    })

  }

  // 可以将手动的创建的精灵添加到画布中

  append(sprite) {

    this.sprites.push(sprite)

    sprite.draw(this.ctx)

  }

  // 根据ID值,从场景中删除对应元素

  remove(id) {

    this.sprites.splice(id, 1)

  }

  // clearRect清除指定区域的画布内容

  clear() {

    this.ctx.clearRect(0, 0, this.width, this.height)

  }

  // 重绘整个画布的内容

  reset() {

    this.clear()

    this.sprites.forEach(element => {

      element.draw(this.ctx)

    })

  }

  // 将离屏canvas绘制到页面的canvas画布上

  paint(canvas, x = 0, y = 0) {

    this.ctx.drawImage(canvas, x, y, this.width, this.height)

  }

  // 设置当前选中的精灵构造器

  setCurrentSprite(Element) {

    this.currentSpriteConstructor = Element

  }

登入後複製

相關文章推薦:

canvas如何實作二維碼與圖片合成的程式碼

HTML5 Canvas實作互動式地鐵線路圖

########

以上是HTML5 canvas實作畫圖程式(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1675
14
CakePHP 教程
1429
52
Laravel 教程
1333
25
PHP教程
1278
29
C# 教程
1257
24
HTML 中的表格邊框 HTML 中的表格邊框 Sep 04, 2024 pm 04:49 PM

HTML 表格邊框指南。在這裡,我們以 HTML 中的表格邊框為例,討論定義表格邊框的多種方法。

HTML 中的巢狀表 HTML 中的巢狀表 Sep 04, 2024 pm 04:49 PM

這是 HTML 中巢狀表的指南。這裡我們討論如何在表中建立表格以及對應的範例。

HTML 左邊距 HTML 左邊距 Sep 04, 2024 pm 04:48 PM

HTML 左邊距指南。在這裡,我們討論 HTML margin-left 的簡要概述及其範例及其程式碼實作。

HTML 表格佈局 HTML 表格佈局 Sep 04, 2024 pm 04:54 PM

HTML 表格佈局指南。在這裡,我們詳細討論 HTML 表格佈局的值以及範例和輸出。

HTML 輸入佔位符 HTML 輸入佔位符 Sep 04, 2024 pm 04:54 PM

HTML 輸入佔位符指南。在這裡,我們討論 HTML 輸入佔位符的範例以及程式碼和輸出。

HTML 有序列表 HTML 有序列表 Sep 04, 2024 pm 04:43 PM

HTML 有序列表指南。在這裡我們也分別討論了 HTML 有序列表和類型的介紹以及它們的範例

HTML onclick 按鈕 HTML onclick 按鈕 Sep 04, 2024 pm 04:49 PM

HTML onclick 按鈕指南。這裡我們分別討論它們的介紹、工作原理、範例以及各個事件中的onclick事件。

在 HTML 中移動文字 在 HTML 中移動文字 Sep 04, 2024 pm 04:45 PM

HTML 中的文字移動指南。在這裡我們討論一下marquee標籤如何使用語法和實作範例。

See all articles