JavaScript 俄罗斯方块 - Canvas基础:旋转和动画

本节内容

  1. 怎样将图片绘制在Canvas画布中?

  1. 怎样旋转图片?

  1. JavaScript动画我们该用什么? rAF或是setTimeout

一、怎样将图片绘制在Canvas画布中?

  1. 先创建一个页面模板,让canvas画布的宽度和高度和window的innerWidth和innerHeight一致, 整个画布的背景色设置为 background-color: aqua; 方便后续观察绘图结果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>旋转的图片</title>
    <style>
        canvas {
            position: absolute;
            background: black;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            background-color: aqua;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script>
        /** @type {HTMLCanvasElement} */
        const canvas = document.getElementById('canvas')
        const ctx = canvas.getContext('2d')
        canvas.width = window.innerWidth
        canvas.height = window.innerHeight
        
    </script>
</body>
</html>
  1. 使用 Image 创建一个图片对象,设置对象的src属性,src表示图片的路径,此处将图片和页面放置在同一目录下,所以只需图片名称, 图片如下:

/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight

//创建图片对象
const cherry = new Image()
cherry.src = 'cherry.png'
  1. 在canvas画布上画图片

  • 图片是异步加载,所以要监听图片加载完成事件load,加载完成后才能将图片画在canvas上

  • 使用translate方法平移当前绘图的原点到(100,100),也就是画图时以(100,100)作为新的原点(0,0)

  • 使用rotate方法旋转

  • 使用fillStyle设置之后的填充颜色

  • 再增加一个填充矩形,清楚图片位置,无其它作用

  • drawImage 画图,第1个参数为图片对象,第2,3个参数表示图片的坐标,第4,5个参数表示图片宽和高

//创建图片对象
const cherry = new Image()
cherry.src = 'cherry.png'

cherry.addEventListener('load', function(){
    ctx.translate(100,100)
    ctx.rotate(0 * Math.PI/360)
    ctx.fillStyle= 'black'
    ctx.fillRect(0,0,300,300)
    ctx.drawImage(cherry, 0, 0, 80, 80)
}) 

  1. 在不同位置再画同一个图片

  • 同样是平移到(100,100) ,但此时会发现,平移是相对于上次绘图的位置

//创建图片对象
const cherry = new Image()
cherry.src = 'cherry.png'

cherry.addEventListener('load', function(){
    //第一次画图
    ctx.translate(100,100)
    ctx.rotate(0 * Math.PI/360)
    ctx.fillStyle= 'black'
    ctx.fillRect(0,0,300,300)
    ctx.drawImage(cherry, 0, 0, 80, 80)

    //再画图
    ctx.translate(100,100)
    ctx.rotate(0 * Math.PI/360)
    ctx.fillStyle= 'orange'
    ctx.fillRect(0,0,300,300)
    ctx.drawImage(cherry, 0, 0, 80, 80)
}) 

  1. 怎样让第二张图画在第一张图的相同位置? 使用save()和restore()方法

  • 使用这两个方法后,恢复之前的状态,其实就是将save()之后的绘图操作入栈,调用restore()后出栈,也就是恢复到当前绘图之前的状态,后续画图会使用save()之前的状态

  • 使用save() restore() 后第二张图片画的位置将会是第一张的位置,第二张图片 认为 第一张图片不存在一样

  • 再旋转一下,看看上次绘图是不是还在

  • 桔色背景的绘图旋转了30度,平移的位置相对于最开始时的状态; 如果桔色背景下方还要画图,也可以加上save和restore,使得桔色图形的属性设置不会影响后续图形

//省略其它代码
//再画图
ctx.translate(100,100)
ctx.rotate(30 * Math.PI/360)
ctx.fillStyle= 'orange'
ctx.fillRect(0,0,300,300)
ctx.drawImage(cherry, 0, 0, 80, 80)

二、使用window.requestAnimationFrame函数 让图片旋转起来

  • 将绘图的代码封装到draw()函数中, 图片加载完成后调用draw()方法

/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight

//创建图片对象
const cherry = new Image()  
cherry.src = 'imgs/cherry.png'       

function draw() {
    ctx.save()
    ctx.translate(100,100)
    ctx.rotate(0 * Math.PI/360)
    ctx.fillStyle= 'black'
    ctx.fillRect(0,0,300,300)
    ctx.drawImage(cherry, 0, 0, 80, 80)
    ctx.restore()

    ctx.translate(100,100)
    ctx.rotate(30 * Math.PI/360)
    ctx.fillStyle= 'orange'
    ctx.fillRect(0,0,300,300)
    ctx.drawImage(cherry, 0, 0, 80, 80)
}

cherry.addEventListener('load', function(){
    draw()
})  
  • 使用window.requestAnimationFrame让绘制的图片动起来

  • 定义了两个角度,黑底图角度增量为1,橙底图角度增量为10, 旋转速度一个慢一个快

  • 也可以去掉绘图时填充的颜色

//创建图片对象
const cherry = new Image()  
cherry.src = 'imgs/cherry.png'     

let angle1 = 1 //第一张图片的旋转角度 初始值
let angle2 = 10 //第二张图片折旋转角度 初始值

function draw() {

    //使用save和restore包裹绘图过程,让其作旋转操作时不影响其它绘图
    ctx.save()
    ctx.translate(300,300)
    ctx.rotate(angle1 * Math.PI/360) //旋转
    ctx.fillStyle= 'black'
    ctx.fillRect(0,0,200,200)//可注释填充背景
    ctx.drawImage(cherry, 0, 0, 80, 80)
    ctx.restore()

    //使用save和restore包裹绘图过程,让其作旋转操作时不影响其它绘图
    ctx.save()
    ctx.translate(300,300)
    ctx.rotate(angle2 * Math.PI/360)
    ctx.fillStyle= 'orange'
    ctx.fillRect(0,0,200,200) //可注释填充背景
    ctx.drawImage(cherry, 0, 0, 80, 80)
    ctx.restore()
}

cherry.addEventListener('load', function(){
    draw()
})  

//动画函数 animate, 不断地调用画图函数,同时更改两个角度值
function animate() {
    //每次调用animate函数时清空画布内容
    ctx.clearRect(0,0,canvas.width, canvas.height)
    //重新绘图
    draw() 
    //更改角度
    angle1 += 1
    angle2 += 10
    //rAF不断调用animate
    window.requestAnimationFrame(animate)
}
animate() 

三、旋转的中心点改成以图片为中心

  • 仅需要根据图片的宽高除以2,并将x,y坐标向负方向移动,下面代码仅修改第一次绘图

ctx.drawImage(cherry, 0-80/2, 0-80/2, 80, 80)

四、小结

  • 主要介绍在canvas中如何绘图,实战了save()和restore()方法的具体用法,在后续的游戏设计中也会要使用到它们,理解它们的原理至关重要

  • 使用rAF制作动画,让图形旋转起来,后续将详细说明为什么要用rAF而不使用setTimeout

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值