Django学习笔记-创建菜单界面

  1. 1. 项目总体设计
  2. 2. 全局设置与项目结构创建
  3. 3. 创建HTML
  4. 4. 创建Views与更新URL
  5. 5. 创建菜单

本节内容是项目结构的划分与游戏菜单界面的设计。

1. 项目总体设计

(1)系统设计

  • menu:菜单页面;
  • playground:游戏界面;
  • settings:设置界面。

(2)文件结构

  • templates:管理 HTML 文件;
  • urls:管理路由,即链接与函数的对应关系;
  • views:管理 HTTP 函数;
  • models:管理数据库数据;
  • static:管理静态文件,比如:
    • css:对象的格式,比如位置、长宽、颜色、背景、字体大小等;
    • js:对象的逻辑,比如对象的创建与销毁、事件函数、移动、变色等;
    • image:图片;
    • audio:声音;
    • ……
  • consumers:管理 WebSocket 函数。

(3)素材地址

  • 背景图片:
    • 下载方式:wget --output-document=自定义图片名称 图片地址
    • 本地上传:scp -P 20000 .\xxx.jpeg asanosaki@<公网IP>:
  • jQuery 库:
    • <link rel="stylesheet" href="http://<公网IP>:8000/static/css/jquery-ui.min.css">
    • <script src="http://<公网IP>:8000/static/js/jquery-3.6.1.min.js"></script>

2. 全局设置与项目结构创建

为了后期方便项目的维护管理,首先先将 game 中的 urls.pymodels.pyviews.py 删除,将其创建为一个目录,并在这三个目录下创建好 __init__.py 文件,注释掉上一节中在总 URL 文件中的配置信息,然后创建好 static 目录。

接着进行一些项目的全局设置,首先设置一下项目的时区,打开 ~/djangoapp/djangoapp/settings.py,修改 TIME_ZONEUSE_TZ

1
2
3
TIME_ZONE = 'Asia/Shanghai'

USE_TZ = False

如果 USE_TZ 设置为 True 时,Django 会使用系统默认设置的时区,即 America/Chicago,此时的 TIME_ZONE 不管有没有设置都不起作用。

注意:由于我们是在云服务器上开发的,如果是 Windows 则设置 TIME_ZONE 是无效的,Django 会使用本机的时间。

然后将自己创建的 App 加载进来,找到 INSTALLED_APPS,将 game/apps.py 添加进来:

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
'game.apps.GameConfig', # 添加此行
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

找到 STATIC_URL = 'static/',在其附近添加几行:

1
2
3
4
5
6
import os  # 在文件首部导入包

STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 表示要将静态文件放到static目录下
STATIC_URL = 'static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = 'media/'

我们在 game/static/ 中创建 image/menu/ 目录用来存放菜单界面的背景,将图片 background.png 放入该目录中即可在自己的网址上访问这张图片:http://<公网IP>:8000/static/image/menu/background.png

一般 CSS 文件只需要一个就行,因此只需要在 game/static/css/ 中创建一个 game.css 文件即可。JS 文件最后一般会有很多,因此在 game/static/js/ 中创建两个目录:distsrc,分别表示最终合并在一起生成的 js 文件以及许多 js 源文件,我们可以写一个脚本完成合并操作,在 ~/djangoapp/ 目录下创建一个 scripts 目录,然后在该目录中编写一个 compress_game_js.sh 文件:

1
2
3
4
5
6
7
#! /bin/bash

JS_PATH=/home/asanosaki/djangoapp/game/static/js/
JS_PATH_DIST=${JS_PATH}dist/
JS_PATH_SRC=${JS_PATH}src/

find ${JS_PATH_SRC} -type f -name '*.js' | sort | xargs cat > ${JS_PATH_DIST}game.js

添加可执行权限:

1
chmod +x compress_game_js.sh

我们执行一下该脚本:./compress_game_js.sh,可以看到 ~/djangoapp/game/static/js/dist/ 中多了一个 game.js 文件。

此时我们先将代码上传至 Git:

1
2
3
4
cd ~/djangoapp/
git add .
git commit -m 'create project structure'
git push

3. 创建HTML

我们先在 templates 目录下创建三个目录:menuplaygroundsettings。由于项目是前后端分离的,最后可以放在多种不同的终端上运行,因此我们再建一个 multiends 目录。

static/src 目录下也创建三个目录:menuplaygroundsettings,然后再创建一个文件 zbase.js,表示总文件,由于打包工具是按照字典序打包的,该文件中为总的 Class,会调用前面三个目录中的 Class,JS 在调用之前必须定义好,因此在打包时需要保证前面三个目录中的文件在 zbase.js 之前被打包,因此加一个字典序最大的字母在首部。

zbase.js 中定义好总类:

1
2
3
4
class AcGame {
constructor(id) {
}
}

templates/multiends/ 中创建 web.html,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% load static %}

<head>
<link rel="stylesheet" href="http://<公网IP>:8000/static/css/jquery-ui.min.css">
<script src="http://<公网IP>:8000/static/js/jquery-3.6.1.min.js"></script>
<link rel="stylesheet" href="{% static 'css/game.css' %}">
<script src="{% static 'js/dist/game.js' %}"></script>
</head>

<body style="margin: 0">
<div id="ac_game_1"></div>
<script>
$(document).ready(function() {
let ac_game = new AcGame("ac_game_1");
});
</script>
</body>

4. 创建Views与更新URL

我们先在 views 目录下创建三个目录:menuplaygroundsettings,这三个目录都是存放 Python 文件的,因此每个目录下都需要一个 __init__.py 文件。

然后再在 views 目录下创建一个 index.py 作为总函数,该函数只会在 Web 端被调用,主要作用是用来返回上一节中的 HTML 文件的:

1
2
3
4
from django.shortcuts import render

def index(request):
return render(request, 'multiends/web.html') # 从templates目录之后开始写路径,因为Django默认会从templates开始找HTML

现在我们开始写路由,先在 urls 目录下创建三个目录:menuplaygroundsettings,在每个目录下都创建一个 __init__.py 文件。接着同样创建一个 index.py,用来将所有该路径下其它目录中的路径 Include 进来,可以参考总 URL 文件编写。

先在三个目录中也创建好 index.py,内容如下:

1
2
3
from django.urls import path

urlpatterns = []

此前在 views 目录中实现的 index.py 为总函数,即整个项目只有一个主链接,因此 urls 目录中的 index.py 也要将其 Include 进来:

1
2
3
4
5
6
7
8
9
from django.urls import path, include
from game.views.index import index

urlpatterns = [
path('', index, name='index'),
path('menu/', include('game.urls.menu.index')),
path('playground/', include('game.urls.playground.index')),
path('settings/', include('game.urls.settings.index')),
]

最后修改一下总 URL 文件(~/djangoapp/djangoapp/ 中的 urls.py):

1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('', include('game.urls.index')),
path('admin/', admin.site.urls),
]

此时梳理一下路由顺序:首先会进入到 ~/djangoapp/djangoapp/ 中的 urls.py 中,然后发现浏览器链接中没有带后缀(例如 admin/),所以会进到 game.urls.index 中,由于没有后缀因此又调用第一个路由,也就是直接调用 game.views.index 中的 index 函数,该函数会渲染 multiends/web.html 里面的内容。

Tips:浏览器中按 F12 打开控制台后如果看到报错:The Cross-Origin-Opener-Policy header has been ignored, because the URL's origin was untrustworthy. It was defined either in the final response or a redirect.,表示出现了跨域问题,在 settings.py 中添加如下代码即可:

1
SECURE_CROSS_ORIGIN_OPENER_POLICY = 'None'

最后将代码上传至 Git:

1
2
3
git add .
git commit -m 'modify html & js & views & urls'
git push

5. 创建菜单

我们需要创建一个菜单对象,首先在 static/js/src/menu/ 目录中创建一个 zbase.js 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
class AcGameMenu {
constructor(root) { // root用来传AcGame对象
this.root = root;
// 如果是HTML对象通常会加一个'$'符号
this.$menu = $(`
<div class='ac_game_menu'>
</div>
`)
this.root.$ac_game.append(this.$menu); // 将this.$menu添加到主类的div中
}
}

然后在 game.css 中定义相应的样式:

1
2
3
4
5
6
.ac_game_menu {
width: 100%;
height: 100%;
background-image: url('/static/image/menu/background.png'); /* 注意不用带公网IP */
background-size: 100% 100%;
}

最后需要更新 static/js/src/ 中的 zbase.js 文件:

1
2
3
4
5
6
7
class AcGame {
constructor(id) {
this.id = id;
this.$ac_game = $('#' + id); // jQuery通过id找对象的方式
this.menu = new AcGameMenu(this);
}
}

此时即可在页面中看到自己的背景图片(注意更新完 JS 文件后都需要执行一下打包脚本 compress_game_js.sh)。

Tips:如果修改完 CSS 文件后网页没有变化说明页面把 CSS 文件缓存下来了,可以按 F12 打开控制台,在 Network 选项卡中勾选上 Disable cache

现在我们在菜单界面添加几个按钮,首先修改菜单目录中的 zbase.js 文件(修改完记得运行打包脚本):

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
class AcGameMenu {
constructor(root) { // root用来传AcGame对象
this.root = root;
// 如果是HTML对象通常会加一个'$'符号
this.$menu = $(`
<div class='ac_game_menu'>
<div class='ac_game_menu_btgroup'>
<div class='ac_game_menu_btgroup_bt ac_game_menu_btgroup_bt_single'>
单人模式
</div>
<br>
<div class='ac_game_menu_btgroup_bt ac_game_menu_btgroup_bt_multi'>
多人模式
</div>
<br>
<div class='ac_game_menu_btgroup_bt ac_game_menu_btgroup_bt_settings'>
设置
</div>
</div>
</div>
`);
this.root.$ac_game.append(this.$menu); // 将this.$menu添加到主类的div中
this.$single = this.$menu.find('.ac_game_menu_btgroup_bt_single'); // class名前面要加'.'
this.$multi = this.$menu.find('.ac_game_menu_btgroup_bt_multi');
this.$settings = this.$menu.find('.ac_game_menu_btgroup_bt_settings');
}
}

然后修改 game.css 设置一下按钮的样式:

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
.ac_game_menu {
width: 100%;
height: 100%;
background-image: url('/static/image/menu/background.png'); /* 注意不用带公网IP */
background-size: 100% 100%;
user-select: none;
}

.ac_game_menu_btgroup {
width: 20vw;
position: relative;
top: 35vh;
left: 19vw;

}

.ac_game_menu_btgroup_bt {
height: 7vh;
width: 18vw;
color: white;
font-size: 6vh;
line-height: 7vh;
font-style: italic;
padding: 2vh;
cursor: pointer;
text-align: center;
background-color: rgba(39, 21, 28, 0.6);
border-radius: 10px;
letter-spacing: 0.5vw;
}

.ac_game_menu_btgroup_bt:hover {
transform: scale(1.2);
transition: 100ms;
}

现在我们实现点击按钮切换页面的功能,需要给每个按钮绑定一个函数,我们可以定义一个 start 函数放在构造函数中,表示对象被创建出来时需要执行的一些初始化操作,即在 start 中可以绑定一些事件。

首先我们先把游戏界面的简易版本写出来,在 static/js/src/playground/ 目录中创建一个 zbase.js 文件,内容如下:

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
class AcGamePlayground {
constructor(root) {
this.root = root;
this.$playground = $(`
<div>
游戏界面
</div>
`);
this.root.$ac_game.append(this.$playground);

this.start();
}

start() {
this.hide(); // 初始化时需要先关闭playground界面
}

// 显示playground界面
show() {
this.$playground.show();
}

// 关闭playground界面
hide() {
this.$playground.hide();
}
}

然后更新一下总的 zbase.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
class AcGame {
constructor(id) {
this.id = id;
this.$ac_game = $('#' + id); // jQuery通过id找对象的方式
this.menu = new AcGameMenu(this);
this.playground = new AcGamePlayground(this);

this.start();
}

start() {
}
}

最后在菜单界面中设置一下按钮点击的响应操作,点击单人模式按钮即可跳转到 AcGamePlayground 界面:

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
class AcGameMenu {
constructor(root) { // root用来传AcGame对象
this.root = root;
// 如果是HTML对象通常会加一个'$'符号
this.$menu = $(`
<div class='ac_game_menu'>
<div class='ac_game_menu_btgroup'>
<div class='ac_game_menu_btgroup_bt ac_game_menu_btgroup_bt_single'>
单人模式
</div>
<br>
<div class='ac_game_menu_btgroup_bt ac_game_menu_btgroup_bt_multi'>
多人模式
</div>
<br>
<div class='ac_game_menu_btgroup_bt ac_game_menu_btgroup_bt_settings'>
设置
</div>
</div>
</div>
`);
this.root.$ac_game.append(this.$menu); // 将this.$menu添加到主类的div中
this.$single = this.$menu.find('.ac_game_menu_btgroup_bt_single'); // class名前面要加'.'
this.$multi = this.$menu.find('.ac_game_menu_btgroup_bt_multi');
this.$settings = this.$menu.find('.ac_game_menu_btgroup_bt_settings');

this.start();
}

start() {
this.add_listening_events();
}

// 给按钮绑定监听函数
add_listening_events() {
let outer = this;
// 注意在function中调用this指的是function本身,因此需要先将外面的this存起来
this.$single.click(function() {
outer.hide(); // 关闭menu界面
outer.root.playground.show(); // 显示playground界面
});
this.$multi.click(function() {
});
this.$settings.click(function() {
});
}

// 显示menu界面
show() {
this.$menu.show();
}

// 关闭menu界面
hide() {
this.$menu.hide();
}
}

最后将代码上传至 Git:

1
2
3
git add .
git commit -m 'modify html & css & menu & playground'
git push

上一章:Django学习笔记-概述与项目环境配置

下一章:Django学习笔记-创建游戏界面