将图片资源放到 asset/img 下面。

    在项目里面我们可能用到 requestAnimationFrame ,关于兼容,大家请查看


    为了语义化,规范化,大家可以参考以下规范。

    写出一份优雅的代码,是每一位工程师的责任。


    • 首先修改我们的 index.html
    • 添加 main.css
    1. CSS书写顺序
    2. 1.位置属性(position, top, right, z-index, display, float等)
    3. 2.大小(width, height, padding, margin)
    4. 3.文字系列(font, line-height, letter-spacing, color- text-align等)
    5. 4.背景(background, border等)
    6. 5.其他(animation, transition等)
    7. */
    8. .wrapper{
    9. width: 800px;
    10. height: 600px;
    11. padding-top: 10px;
    12. margin: 0 auto;
    13. }
    14. .canvas-container{
    15. position: relative;
    16. width: 800px;
    17. height: 600px;
    18. margin: 0;
    19. }
    20. canvas.canvas-item{
    21. position: absolute;
    22. top: 0;
    23. right: 0;
    24. bottom: 0;
    25. left: 0;
    26. }
    27. canvas#one{
    28. z-index: 1;
    29. }
    30. canvas#two{
    31. z-index: 0;
    32. }

    首先我们封装获取一个获取 DOM 和 context 的函数

    1. function getCanvasAndContextById(id: string): [HTMLCanvasElement, CanvasRenderingContext2D] {
    2. const dom = <HTMLCanvasElement>document.querySelector('#' + id);
    3. return [dom, ctx];
    4. }

    为什么会知道是 HTMLCanvasElement ?

    而这个 CanvasRenderingContext2D ,直接查看 dom.getContext 的返回值类型即可知道。

    这里的返回值我们用了元组的解构。

    所以获取我们应该这样解构,并且放在了 window.onload 函数里面,报错 DOM 加载完成之后再执行代码。

    我们设置的 #one 的 层级比较高,所以我们会在 one 这一层上面绘制一些 UI、结构之类的逻辑,而 two 则是绘制背景。

    • 接下来我们要用到 requestAnimationFrame 来优化 Web 动画性能

    大家可以看一下这一篇文章 。

    我们知道游戏的运行,是需要浏览器不停的重新绘制的,所以说一定有一个循环,在不停的重绘。

    1. let lastTime: number = Date.now(), // 记录上一次绘制的时间
    2. deltaTime: number = 0; // requestAnimationFrame 执行完成所用的时间 = 当前时间 - 上一次绘制的世界
    3. function gameLoop() {
    4. const now = Date.now()
    5. deltaTime = now - lastTime;
    6. lastTime = now;
    7. console.log(deltaTime);
    8. requestAnimationFrame(gameLoop);
    9. }

    而我们的 DOM 实例和 Context 是需要在其他函数里面使用的,所以说,我们必须拿到外面来,但是又必须等 DOM 完成之后再获取。

    所有我们小小的修改一下之前的代码

    1. function getCanvasAndContextById(id: string): [HTMLCanvasElement, CanvasRenderingContext2D] {
    2. const dom = <HTMLCanvasElement>document.querySelector('#' + id);
    3. const ctx = dom.getContext('2d');
    4. return [dom, ctx];
    5. }
    6. let cvs_one: HTMLCanvasElement,
    7. cvs_two: HTMLCanvasElement,
    8. ctx_one: CanvasRenderingContext2D,
    9. ctx_two: CanvasRenderingContext2D;
    10. let lastTime: number = Date.now(), // 记录上一次绘制的时间
    11. deltaTime: number = 0; // requestAnimationFrame 执行完成所用的时间 = 当前时间 - 上一次绘制的世界
    12. window.onload = () => {
    13. init();
    14. gameLoop();
    15. }
    16. function init() {
    17. [cvs_one, ctx_one] = getCanvasAndContextById('one');
    18. [cvs_two, ctx_two] = getCanvasAndContextById('two');
    19. }
    20. function gameLoop() {
    21. const now = Date.now()
    22. deltaTime = now - lastTime;
    23. lastTime = now;
    24. console.log(deltaTime);
    25. requestAnimationFrame(gameLoop);
    26. }
    • 接下来我们添加绘制背景的函数

    因为背景是处于游戏中,所以我们要把它写到游戏循环里面。

    现在你应该可以看到一个背景图片了。

    这样的代码看起来非常的乱,现在我们该来想一想如何重构了。

    首先我们理清逻辑,初始化 Init -> 游戏循环 ,目前来说就这么2个逻辑,所以,我们把这 2 个函数分成 2 个文件。

    1. let cvs_one: HTMLCanvasElement,
    2. ctx_one: CanvasRenderingContext2D,
    3. ctx_two: CanvasRenderingContext2D;
    4. let cvs_width: number,
    5. cvs_height: number;
    6. const bgPic = new Image();
    7. function getCanvasAndContextById(id: string): [HTMLCanvasElement, CanvasRenderingContext2D] {
    8. const dom = <HTMLCanvasElement>document.querySelector('#' + id);
    9. const ctx = dom.getContext('2d');
    10. return [dom, ctx];
    11. }
    12. function init() {
    13. [cvs_one, ctx_one] = getCanvasAndContextById('one');
    14. [cvs_two, ctx_two] = getCanvasAndContextById('two');
    15. bgPic.src = 'assets/img/background.jpg';
    16. cvs_width = cvs_one.width;
    17. cvs_height = cvs_one.height;
    18. }
    19. export default init;
    20. export { bgPic, cvs_width , cvs_height, ctx_two };
    1. // game-loop.ts
    2. import { bgPic, cvs_width , cvs_height, ctx_two } from "./init";
    3. let lastTime: number = Date.now(), // 记录上一次绘制的时间
    4. deltaTime: number = 0; // requestAnimationFrame 执行完成所用的时间 = 当前时间 - 上一次绘制的世界
    5. function gameLoop() {
    6. const now = Date.now()
    7. deltaTime = now - lastTime;
    8. lastTime = now;
    9. console.log(deltaTime);
    10. drawBackbround()
    11. requestAnimationFrame(gameLoop);
    12. }
    13. function drawBackbround() {
    14. ctx_two.drawImage(bgPic, 0, 0, cvs_width, cvs_height)
    15. }

    重构搞定!