🌟 所属专栏:献给榕榕
🐔 作者简介:rchjr——五带信管菜只因一枚
😮 前言:该专栏系为女友准备的,里面会不定时发一些讨好她的技术作品,感兴趣的小伙伴可以关注一下~
👉 文章简介:献给女友的简单网站源代码
😃0 前言与开发环境
前言
这篇文章把自己今年寒假花了20天做的一个网站放出来。这个网站就是用来练练手感的,接下来会重新做一个属于我的个人网站,主题还没有想好,但是应该是类似于一个个人博客吧。后面做网站时会一直更新进度,感兴趣的小伙伴可以关注我了解后续,这个送给女朋友的就不详解了,直接放置源码,可以直接运行,当做一个参考。
开发环境
开发语言:python 3.8
开发框架:Flask
开发工具:PyCharm专业版
虚拟环境工具:Anaconda
浏览器:谷歌浏览器
😃1 运行结果图
![](https://i-blog.csdnimg.cn/blog_migrate/ef35e0facf0d88ef3874070ee677bcfb.png)
主页
![](https://i-blog.csdnimg.cn/blog_migrate/5893908df2fc1e7ca7bd63cd73f8da44.png)
登录界面
![](https://i-blog.csdnimg.cn/blog_migrate/e71b1f2b4e0461bbff5020bc123b92ce.png)
注册界面
![](https://i-blog.csdnimg.cn/blog_migrate/37cd9f6eea7f6507919d847fd25aa289.png)
照片墙
![](https://i-blog.csdnimg.cn/blog_migrate/2b1156de94ea8da10f2b9965f88a5428.png)
想去的地方(挪威)
![](https://i-blog.csdnimg.cn/blog_migrate/ee60e24bc171314188e264a8dad69dd4.png)
留言(无法提交)
![](https://i-blog.csdnimg.cn/blog_migrate/32c669983434365a97b3ed4d0c907017.png)
浪漫烟花(带背景音乐)
![](https://i-blog.csdnimg.cn/blog_migrate/109e84b925f84dc5a78f3b00d3df843c.png)
注销
😃2 代码结构
一个app文件夹用来存放各个功能(忘记术语叫啥了),对应上面展示的几个功能。static文件夹存放静态文件,具体包括css文件,js文件和html文件以及图片和背景音乐。当然还有模型文件models.py存放数据库模型。
然后是config.py存放一些要用的配置类,manager.py作为主文件用于启动整个项目。
![](https://i-blog.csdnimg.cn/blog_migrate/ddf911d885f42a1d1c5ff171c7665f87.png)
😃3 config.py配置文件
先从配置信息开始讲起吧。这个网站我用到的配置信息只有连接MySQL要用的一些相关信息,比较简单。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/3 15:08
"""各种配置类"""
class BaseConfig(object):
"""所有配置类的基类"""
SECRET_KEY = "LOVE"
DEBUG = True
TESTING = False
VISIT_TIME = 0 # 网站访问次数
# 数据库配置
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:这里换成自己的MySQL密码@localhost/personalwebsite"
SQLALCHEMY_TRACK_MODIFICATIONS = False # 是否追踪数据库的修改
class ProductionConfig(BaseConfig):
"""生产环境下的配置类"""
DEBUG = False
class DevelopmentConfig(BaseConfig):
"""开发模式下的配置类"""
DEBUG = True
TESTING = True
然后我是使用的Navicat这个工具用来可视化操作MySQL的。就是下面这个家伙啦,可以在网上找到破解版的,如果想和我用一样的话,这里也有百度网盘的下载链接:
链接: https://pan.baidu.com/s/1WqS8H2fHAwkPm76O-7OClg?pwd=9rlv
提取码:9rlv
--来自百度网盘超级会员V3的分享
![](https://i-blog.csdnimg.cn/blog_migrate/69797a74d02f9e2a7bc6825c7d25c4f3.png)
打开Navicat可以看到我创建了一个名为personalwebsite的数据库,双击它可以看到
![](https://i-blog.csdnimg.cn/blog_migrate/eb6d5afa298123eb69f4662cea005d66.png)
我创建了两个表,一个是存放用户登录信息的,另一个message本来打算存放留言的,但是后面没用到大家可以不用管。
![](https://i-blog.csdnimg.cn/blog_migrate/17cb90befbaa34da9e42a4b3d744a2db.png)
😃4 app包详解
这个包里面包含了整个网页代码,因此是本次项目的大头,我将按照登录注册功能、照片墙功能、我说功能、我想去的地方功能和浪漫烟花功能的顺序进行讲解。另外项目采用蓝图的方式开发,每次搞定完一个功能,就会在app包下面的__init__.py文件中添加相应的信息。这样方便管理整个项目结构,使之整洁、易于修改,可扩展性也很好。
📜4.1 登录注册功能
登录注册功能在app_login包中,里面一共两个文件,分别是__init__.py和view.py视图文件。先看__init__.py文件。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/8 23:19
"""登录视图的蓝图"""
from flask import Blueprint
login = Blueprint("login", __name__)
from . import view
在app_login/__init__.py文件中我创建了一个登录视图的蓝图。接下来看登录注册的视图文件
首先导入了实现登录要用的一些Flask提供的模块,比如login_required, login_user, logout_user。具体功能我就不细讲了。然后导入了跳转网页要用的request, render_template, redirect, url_for, flash,还导入了数据库模型User和刚刚创建的蓝图login,最后从app包下的__init__.py中导入了管理登录用的login_manager,这个等会展示app/__init__.py的时候会看得到。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/6 11:39
"""登录的视图函数"""
from flask import request, render_template, redirect, url_for, flash
from flask_login import login_required, login_user, logout_user
from . import login
from app.models import User
from app import login_manager
@login_manager.user_loader
def load_user(user_id): # 创建用户加载回调函数,接受用户 ID 作为参数
user = User.query.get(int(user_id)) # 用 ID 作为 User 模型的主键查询对应的用户
return user # 返回用户对象
@login.route('/index', methods=['GET', 'POST'])
@login_required
def index():
return render_template('index.html')
@login.route('/register', methods=['GET', 'POST'])
def register():
# 如果请求为post
if request.method == 'POST':
count = request.form.get('count')
password = request.form.get('password')
repassword = request.form.get('repassword')
from app import db
if password == repassword:
user = User(count, password)
user.set_password(password)
db.session.add(user)
db.session.commit()
return redirect(url_for("login.login_"))
else:
flash("两次密码不一致!")
return render_template("register.html")
return render_template('register.html')
@login.route('/', methods=['GET', 'POST'])
@login.route('/login', methods=['GET', 'POST'])
def login_():
if request.method == 'POST':
count = request.form['count']
password = request.form['password']
if not count or not password:
flash('请输入账号和密码!')
return redirect(url_for('login.login_'))
user = User.query.filter_by(count=count).first()
if not user:
flash("用户不存在")
return redirect(url_for('login.login_'))
if count == user.count and user.check_password(password):
login_user(user) # 登入用户
return redirect(url_for('login.index')) # 重定向到主页
flash('账号或密码不正确, 请重新登录!') # 如果验证失败,显示错误消息
return redirect(url_for('login.login_')) # 重定向回登录页面
return render_template('login.html')
@login.route('/logout', methods=['GET','POST'])
@login_required
def logout():
logout_user()
return redirect(url_for('login.login_'))
导入需要的模块和函数后,用login_manager作为修饰器管理用户登录,具体就是从数据库中查询信息判断是否存在用户,可以看其他博客了解详情。然后是分别用对应的修饰器修饰主页、注册页面、登录页面和注销页面。
下面给出要用到的前端文件
index.html
{% extends 'base.html' %}
{% block title %}
主页
{% endblock %}
{% block div %}
<div class="wenzi_a">
<img src="../static/images/background_images/照片墙.png" style="height: 50px; width: 50px">
<span><font size="4" color=#000000><b> 我们的相册</b></font></span>
<div class="wenzi_font">
<font size="3"><b>
举目望向天空,有说不尽的美好。<br/>
夏天来临之前,温暖洒满了一地。<br/>
轻轻翻阅发黄的书卷,斑驳往事。<br/>
抓住午后跳跃的阳光,锁住流年。<br/>
到底是怎样一种繁华创造了永恒?<br/>
夏日的夕阳将天空染成了橘色。<br/>
我们走过的路上,落满一地的幸福。<br/>
</b></font>
</div>
</div>
<div class="wenzi_b">
<img src="../static/images/background_images/爱心.png">
<span><font size="4" color=#000000><b>我与你</b></font></span>
<div class="wenzi_font">
<font size="3"><b>
时光的洪流把每个人的生命印刻成一<br/>
枚枚底片。记忆沿着掌心的脉络聚成<br/>
一幅幅永恒的画面。这些光怪陆离的<br/>
记忆中最令我怀念的就是,最美的你<br/>
陪我走过最绚烂的年华。<br/>
</b></font>
</div>
</div>
<div class="wenzi_c">
<img src="../static/images/background_images/消息.png">
<span><font size="4" color=#000000><b>我想说</b></font></span>
<div class="wenzi_font">
<font size="3"><b>
呯呯的心跳却总能代表我的情意。<br/>
想说爱你其实真的很遥远,傍山 <br/>
涉水地追求,可能才是我的目的。<br/>
想说爱你只是想真心对待你,<br/>
想说爱你只是想真实地表达自己。<br/>
</b></font>
</div>
</div>
<div class="wenzi_d">
<img src="../static/images/background_images/走.png">
<span><font size="4" color=#000000><b>  想去的地方</b></font></span>
<div class="wenzi_font">
<font size="3">
<b>
前面以上的文字均来自百度, <br/>
原谅我即时在这种特殊的日子 <br/>
也说不出来一句“像样的”言语。<br/>
我平时已经说的够多了,为了 <br/>
让记忆永存,我自学了网页语 <br/>
言编写了这个网站。制作过程 <br/>
过程费尽心血和精力,但一切 <br/>
都是得的。<br/>
</b>
</font>
</div>
</div>
{% endblock %}
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" type="text/css" href="../static/css/login.css"/>
<script src="../static/js/register.js" type="text/javascript"></script>
</head>
<body>
<div class="login-box">
<h2>注册</h2>
<p class="tip">
{% for item in get_flashed_messages() %}
{{ item }}
{% endfor %}
</p>
<form action="/register" method="post">
<div class="login-field">
<span style="color: aqua; size: 12px">账号</span>
<input type="text" name="count" required=""/>
</div>
<div class="login-field">
<spqn style="color: aqua; size: 12px">密码</spqn>
<input type="password" name="password" required=""/>
</div>
<div class="login-field">
<spqn style="color: aqua; size: 12px">确认密码</spqn>
<input type="password" name="repassword" required=""/>
</div>
<input type="submit" value="提交" id="myloginlabel">
<p style="color: rgba(194,108,108,0.58)">已有账号?<a href="./login" style="color: rgba(22,180,166,0.58)"
onclick="topggleForm();">登录</a></p>
</form>
</div>
</body>
</html>
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>榕城与佳人</title>
<link rel="stylesheet" type="text/css" href="../static/css/login.css"/>
<script src="../static/js/register.js" type="text/javascript"></script>
<link rel="shortcut icon" href="../static/images/ico_images/wjr.ico">
</head>
<body>
<div class="login-box">
<h2 style="color: #ce8483; font-family: 华文彩云">榕城佳人</h2>
<p class="tip">
{% for item in get_flashed_messages() %}
{{ item }}
{% endfor %}
</p>
<form action="/login" method="post">
<div class="login-field">
<span style="color: aqua; size: 12px">账号</span>
<input type="text" name="count" required=""/>
</div>
<div class="login-field">
<spqn style="color: aqua; size: 12px">密码</spqn>
<input type="password" name="password" required=""/>
</div>
<input type="submit" value="点击进入" id="myloginlabel">
<p style="color: rgba(222,112,112,0.58)">没有账号?<a href="./register" style="color: rgba(22,180,166,0.58)"
onclick="topggleForm();">注册</a></p>
</form>
</div>
</body>
</html>
当然其他的css文件,图片等后面会一并给出。
📟4.2 照片墙功能
照片墙功能在app_photo_wall包中,里面一共两个文件,分别是__init__.py和view.py视图文件。先看__init__.py文件。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/13 16:10
"""照片墙的蓝图"""
from flask import Blueprint
photo_wall = Blueprint("photo_wall", __name__)
from . import view
在app_photo_wall/__init__.py文件中我创建了一个照片墙视图的蓝图。接下来看照片墙的视图文件
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/13 16:20
"""照片墙的视图函数"""
from flask import render_template
from flask_login import login_required
from . import photo_wall
@photo_wall.route('/photo_wall', methods=['GET', 'POST'])
@login_required
def index():
return render_template('photo_wall.html')
照片墙后端很简单,主要是靠前端实现的,下面给出前端代码
photo_wall.html
当然大家可以换成自己想换的照片。
{% extends "base.html" %}
{% block title %}
照片墙
{% endblock %}
{% block div %}
<div id="div2">
<div id="photo_box">
<img src="../static/images/photo_wall_images/wjr%20(1).jpg">
<img src="../static/images/photo_wall_images/wjr%20(2).jpg">
<img src="../static/images/photo_wall_images/wjr%20(3).jpg">
<img src="../static/images/photo_wall_images/wjr%20(4).jpg">
<img src="../static/images/photo_wall_images/wjr%20(5).jpg">
<img src="../static/images/photo_wall_images/wjr%20(6).jpg">
<img src="../static/images/photo_wall_images/wjr%20(7).jpg">
<img src="../static/images/photo_wall_images/wjr%20(8).jpg">
<img src="../static/images/photo_wall_images/wjr%20(9).jpg">
<img src="../static/images/photo_wall_images/wjr%20(10).jpg">
<img src="../static/images/photo_wall_images/wjr%20(11).jpg">
<img src="../static/images/photo_wall_images/wjr%20(12).jpg">
<img src="../static/images/photo_wall_images/wjr%20(13).jpg">
<img src="../static/images/photo_wall_images/wjr_%20(1).jpg">
<img src="../static/images/photo_wall_images/wjr_%20(2).jpg">
<img src="../static/images/photo_wall_images/wjr_%20(3).jpg">
<img src="../static/images/photo_wall_images/wjr_%20(4).jpg">
<img src="../static/images/photo_wall_images/wjr_%20(5).jpg">
<img src="../static/images/photo_wall_images/wjr_%20(6).jpg">
<img src="../static/images/photo_wall_images/wjr_%20(7).jpg">
</div>
</div>
{% endblock %}
😛4.3 我说功能
我说功能在app_isay包中,里面一共两个文件,分别是__init__.py和view.py视图文件。先看__init__.py文件。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/22 8:52
""""""
from flask import Blueprint
isay = Blueprint("isay", __name__)
from . import view
在app_isay/__init__.py文件中我创建了一个我说视图的蓝图。接下来看我说功能的视图文件
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/22 8:52
"""我说的视图函数"""
import time
from . import isay
from flask import request, render_template, session
from flask_login import login_required
from app.models import User, Message
@isay.route('/isay', methods=['GET', 'POST'])
@login_required
def isay():
from app import db
if request.method == 'POST':
return render_template('isay.html')
if request.method == 'POST':
title = request.form.get("title")
text = request.form.get("text")
count = session.get('count')
print(count)
# 获取当前系统时间
created_time = time.strftime("%Y-%m-%d %H:%M:%S")
user = User.query.filter_by(count=count).first()
message = Message(title=title, text=text, created_time=created_time, user_id=user.id)
db.session.add(message)
db.session.commit()
blog = Message.query.filter(Message.created_time == created_time).first()
return render_template('blogSuccess.html', title=title, id=message.id)
这个功能其实没开发好,大家可以不用管它或者自行完善也行。
✈4.4 想去的地方功能
想去的地方功能在app_togo包中,里面一共两个文件,分别是__init__.py和view.py视图文件。先看__init__.py文件。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/23 11:34
""""""
from flask import Blueprint
togo = Blueprint("togo", __name__)
from . import view
在app_togo/__init__.py文件中我创建了一个想去的地方视图的蓝图。接下来看看想去的地方功能的视图文件
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/23 11:35
"""想去的地方"""
from . import togo
from flask import render_template
@togo.route("/togo", methods=['POST', 'GET'])
def togo():
return render_template("togo.html")
这个功能主要是在前端导入了谷歌地图的API,下面给出前端页面
togo.html
{% extends "base.html" %}
{% block div %}
<iframe src="https://www.google.com/maps/embed?pb=!1m14!1m12!1m3!1d11329513.820172088!2d14.194477459720666!3d61.318977221270224!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!5e0!3m2!1szh-CN!2s!4v1674447168409!5m2!1szh-CN!2s"
width="800" height="600" class="map" allowfullscreen="" loading="lazy"
referrerpolicy="no-referrer-when-downgrade"></iframe>
{% endblock %}
💥4.5 浪漫烟花功能
浪漫烟花功能在app_fireflower包中,里面一共两个文件,分别是__init__.py和view.py视图文件。先看__init__.py文件。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/2/11 20:48
""""""
from flask import Blueprint
fireflower = Blueprint("fireflower", __name__)
from . import view
在app_fireflower/__init__.py文件中我创建了一个浪漫烟花视图的蓝图。接下来看看浪漫烟花功能的视图文件
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/2/11 20:48
""""""
from flask import render_template
from flask_login import login_required
from . import fireflower
@fireflower.route('/fireflower', methods=['GET', 'POST'])
@login_required
def index():
return render_template('romanticFireFlower.html')
这个功能主要是在前端实现的,下面给出前端页面
romanticFireFlower.html
{% extends "base.html" %}
{% block title %}
浪漫烟花
{% endblock %}
{% block div %}
<style>
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: rgba(15, 23, 22, 0.8);
}
</style>
{# <form action="/fireflower" method="post"></form>#}
<audio autoplay="autoplay" loop="loop" preload="auto"
src="../static/music/Because%20of%20You.mp3"></audio>
<canvas id="canvas" style="position:relative;width:1500px;height:800px;z-index:9999;top: 100px"></canvas>
<canvas style="position:absolute;width:100%;height:100%;z-index:9999" class="canvas"></canvas>
<div class="overlay">
<div class="tabs">
<div class="tabs-labels"><span class="tabs-label">Commands</span><span class="tabs-label">Info</span><span
class="tabs-label">Share</span></div>
<div class="tabs-panels">
<ul class="tabs-panel commands">
</ul>
</div>
</div>
</div>
<script>
function initVars() {
pi = Math.PI;
ctx = canvas.getContext("2d");
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
cx = canvas.width / 2;
cy = canvas.height / 2;
playerZ = -25;
playerX = playerY = playerVX = playerVY = playerVZ = pitch = yaw = pitchV = yawV = 0;
scale = 600;
seedTimer = 0;
seedInterval = 5, seedLife = 100;
gravity = .02;
seeds = new Array();
sparkPics = new Array();
s = "https://cantelope.org/NYE/";
for (i = 1; i <= 10; ++i) {
sparkPic = new Image();
sparkPic.src = s + "spark" + i + ".png";
sparkPics.push(sparkPic);
}
sparks = new Array();
pow1 = new Audio(s + "pow1.ogg");
pow2 = new Audio(s + "pow2.ogg");
pow3 = new Audio(s + "pow3.ogg");
pow4 = new Audio(s + "pow4.ogg");
frames = 0;
}
function rasterizePoint(x, y, z) {
var p, d;
x -= playerX;
y -= playerY;
z -= playerZ;
p = Math.atan2(x, z);
d = Math.sqrt(x * x + z * z);
x = Math.sin(p - yaw) * d;
z = Math.cos(p - yaw) * d;
p = Math.atan2(y, z);
d = Math.sqrt(y * y + z * z);
y = Math.sin(p - pitch) * d;
z = Math.cos(p - pitch) * d;
var rx1 = -1000, ry1 = 1, rx2 = 1000, ry2 = 1, rx3 = 0, ry3 = 0, rx4 = x, ry4 = z,
uc = (ry4 - ry3) * (rx2 - rx1) - (rx4 - rx3) * (ry2 - ry1);
if (!uc) return {x: 0, y: 0, d: -1};
var ua = ((rx4 - rx3) * (ry1 - ry3) - (ry4 - ry3) * (rx1 - rx3)) / uc;
var ub = ((rx2 - rx1) * (ry1 - ry3) - (ry2 - ry1) * (rx1 - rx3)) / uc;
if (!z) z = .000000001;
if (ua > 0 && ua < 1 && ub > 0 && ub < 1) {
return {
x: cx + (rx1 + ua * (rx2 - rx1)) * scale,
y: cy + y / z * scale,
d: Math.sqrt(x * x + y * y + z * z)
};
} else {
return {
x: cx + (rx1 + ua * (rx2 - rx1)) * scale,
y: cy + y / z * scale,
d: -1
};
}
}
function spawnSeed() {
seed = new Object();
seed.x = -50 + Math.random() * 100;
seed.y = 25;
seed.z = -50 + Math.random() * 100;
seed.vx = .1 - Math.random() * .2;
seed.vy = -1.5;//*(1+Math.random()/2);
seed.vz = .1 - Math.random() * .2;
seed.born = frames;
seeds.push(seed);
}
function splode(x, y, z) {
t = 5 + parseInt(Math.random() * 150);
sparkV = 1 + Math.random() * 2.5;
type = parseInt(Math.random() * 3);
switch (type) {
case 0:
pic1 = parseInt(Math.random() * 10);
break;
case 1:
pic1 = parseInt(Math.random() * 10);
do {
pic2 = parseInt(Math.random() * 10);
} while (pic2 == pic1);
break;
case 2:
pic1 = parseInt(Math.random() * 10);
do {
pic2 = parseInt(Math.random() * 10);
} while (pic2 == pic1);
do {
pic3 = parseInt(Math.random() * 10);
} while (pic3 == pic1 || pic3 == pic2);
break;
}
for (m = 1; m < t; ++m) {
spark = new Object();
spark.x = x;
spark.y = y;
spark.z = z;
p1 = pi * 2 * Math.random();
p2 = pi * Math.random();
v = sparkV * (1 + Math.random() / 6)
spark.vx = Math.sin(p1) * Math.sin(p2) * v;
spark.vz = Math.cos(p1) * Math.sin(p2) * v;
spark.vy = Math.cos(p2) * v;
switch (type) {
case 0:
spark.img = sparkPics[pic1];
break;
case 1:
spark.img = sparkPics[parseInt(Math.random() * 2) ? pic1 : pic2];
break;
case 2:
switch (parseInt(Math.random() * 3)) {
case 0:
spark.img = sparkPics[pic1];
break;
case 1:
spark.img = sparkPics[pic2];
break;
case 2:
spark.img = sparkPics[pic3];
break;
}
break;
}
spark.radius = 25 + Math.random() * 50;
spark.alpha = 1;
spark.trail = new Array();
sparks.push(spark);
}
switch (parseInt(Math.random() * 4)) {
case 0:
pow = new Audio(s + "pow1.ogg");
break;
case 1:
pow = new Audio(s + "pow2.ogg");
break;
case 2:
pow = new Audio(s + "pow3.ogg");
break;
case 3:
pow = new Audio(s + "pow4.ogg");
break;
}
d = Math.sqrt((x - playerX) * (x - playerX) + (y - playerY) * (y - playerY) + (z - playerZ) * (z - playerZ));
pow.volume = 1.5 / (1 + d / 10);
pow.play();
}
function doLogic() {
if (seedTimer < frames) {
seedTimer = frames + seedInterval * Math.random() * 10;
spawnSeed();
}
for (i = 0; i < seeds.length; ++i) {
seeds[i].vy += gravity;
seeds[i].x += seeds[i].vx;
seeds[i].y += seeds[i].vy;
seeds[i].z += seeds[i].vz;
if (frames - seeds[i].born > seedLife) {
splode(seeds[i].x, seeds[i].y, seeds[i].z);
seeds.splice(i, 1);
}
}
for (i = 0; i < sparks.length; ++i) {
if (sparks[i].alpha > 0 && sparks[i].radius > 5) {
sparks[i].alpha -= .01;
sparks[i].radius /= 1.02;
sparks[i].vy += gravity;
point = new Object();
point.x = sparks[i].x;
point.y = sparks[i].y;
point.z = sparks[i].z;
if (sparks[i].trail.length) {
x = sparks[i].trail[sparks[i].trail.length - 1].x;
y = sparks[i].trail[sparks[i].trail.length - 1].y;
z = sparks[i].trail[sparks[i].trail.length - 1].z;
d = ((point.x - x) * (point.x - x) + (point.y - y) * (point.y - y) + (point.z - z) * (point.z - z));
if (d > 9) {
sparks[i].trail.push(point);
}
} else {
sparks[i].trail.push(point);
}
if (sparks[i].trail.length > 5) sparks[i].trail.splice(0, 1);
sparks[i].x += sparks[i].vx;
sparks[i].y += sparks[i].vy;
sparks[i].z += sparks[i].vz;
sparks[i].vx /= 1.075;
sparks[i].vy /= 1.075;
sparks[i].vz /= 1.075;
} else {
sparks.splice(i, 1);
}
}
p = Math.atan2(playerX, playerZ);
d = Math.sqrt(playerX * playerX + playerZ * playerZ);
d += Math.sin(frames / 80) / 1.25;
t = Math.sin(frames / 200) / 40;
playerX = Math.sin(p + t) * d;
playerZ = Math.cos(p + t) * d;
yaw = pi + p + t;
}
function rgb(col) {
var r = parseInt((.5 + Math.sin(col) * .5) * 16);
var g = parseInt((.5 + Math.cos(col) * .5) * 16);
var b = parseInt((.5 - Math.sin(col) * .5) * 16);
return "#" + r.toString(16) + g.toString(16) + b.toString(16);
}
function draw() {
ctx.clearRect(0, 0, cx * 2, cy * 2);
ctx.fillStyle = "#ff8";
for (i = -100; i < 100; i += 3) {
for (j = -100; j < 100; j += 4) {
x = i;
z = j;
y = 25;
point = rasterizePoint(x, y, z);
if (point.d != -1) {
size = 250 / (1 + point.d);
d = Math.sqrt(x * x + z * z);
a = 0.75 - Math.pow(d / 100, 6) * 0.75;
if (a > 0) {
ctx.globalAlpha = a;
ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
}
}
}
}
ctx.globalAlpha = 1;
for (i = 0; i < seeds.length; ++i) {
point = rasterizePoint(seeds[i].x, seeds[i].y, seeds[i].z);
if (point.d != -1) {
size = 200 / (1 + point.d);
ctx.fillRect(point.x - size / 2, point.y - size / 2, size, size);
}
}
point1 = new Object();
for (i = 0; i < sparks.length; ++i) {
point = rasterizePoint(sparks[i].x, sparks[i].y, sparks[i].z);
if (point.d != -1) {
size = sparks[i].radius * 200 / (1 + point.d);
if (sparks[i].alpha < 0) sparks[i].alpha = 0;
if (sparks[i].trail.length) {
point1.x = point.x;
point1.y = point.y;
switch (sparks[i].img) {
case sparkPics[0]:
ctx.strokeStyle = "#f84";
break;
case sparkPics[1]:
ctx.strokeStyle = "#84f";
break;
case sparkPics[2]:
ctx.strokeStyle = "#8ff";
break;
case sparkPics[3]:
ctx.strokeStyle = "#fff";
break;
case sparkPics[4]:
ctx.strokeStyle = "#4f8";
break;
case sparkPics[5]:
ctx.strokeStyle = "#f44";
break;
case sparkPics[6]:
ctx.strokeStyle = "#f84";
break;
case sparkPics[7]:
ctx.strokeStyle = "#84f";
break;
case sparkPics[8]:
ctx.strokeStyle = "#fff";
break;
case sparkPics[9]:
ctx.strokeStyle = "#44f";
break;
}
for (j = sparks[i].trail.length - 1; j >= 0; --j) {
point2 = rasterizePoint(sparks[i].trail[j].x, sparks[i].trail[j].y, sparks[i].trail[j].z);
if (point2.d != -1) {
ctx.globalAlpha = j / sparks[i].trail.length * sparks[i].alpha / 2;
ctx.beginPath();
ctx.moveTo(point1.x, point1.y);
ctx.lineWidth = 1 + sparks[i].radius * 10 / (sparks[i].trail.length - j) / (1 + point2.d);
ctx.lineTo(point2.x, point2.y);
ctx.stroke();
point1.x = point2.x;
point1.y = point2.y;
}
}
}
ctx.globalAlpha = sparks[i].alpha;
ctx.drawImage(sparks[i].img, point.x - size / 2, point.y - size / 2, size, size);
}
}
}
function frame() {
if (frames > 100000) {
seedTimer = 0;
frames = 0;
}
frames++;
draw();
doLogic();
requestAnimationFrame(frame);
}
window.addEventListener("resize", () => {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
cx = canvas.width / 2;
cy = canvas.height / 2;
});
initVars();
frame();
</script>
{% endblock %}
💻4.6 models.py模型文件
这里创建了User和Message数据库模型,也就是对应数据库里的两张表。其中第一个模型和登录也是有关联的,都是Flask自带的模块。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/4 20:00
"""登录需要的数据库模型(用户表)"""
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from app import db
class User(UserMixin, db.Model):
# 第一个参数指定字段类型,后面设置属性
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
count = db.Column(db.String(128), nullable=False, unique=True)
password = db.Column(db.String(128), nullable=False)
def __init__(self, count, password):
self.count = count
self.password = password
def set_password(self, password):
self.password = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password, password)
class Message(db.Model):
__tablename__ = 'messages'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(128))
text = db.Column(db.TEXT)
created_time = db.Column(db.String(64))
# 关联用户id
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
user = db.relationship('User', backref='user')
if __name__ == '__main__':
user = User(count='0101', password=2021)
db.session.add(user)
db.session.commit()
🔧4.7 __init__.py文件
这里完成对整个项目的蓝图的注册,配置信息和初始化数据库。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/3 1:54
"""构建app,注册蓝图"""
from flask import Flask
from config import BaseConfig
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'login'
db = SQLAlchemy()
def register_bp(app):
"""
注册蓝图
:param app:
:return:
"""
from .app_login import login as login_blueprint
from .app_photo_wall import photo_wall as photo_wall_blueprint
from .app_isay import isay as isay_blueprint
from .app_togo import togo as togo_blueprint
from .app_fireflower import fireflower as fireflower_blueprint
app.register_blueprint(login_blueprint)
app.register_blueprint(photo_wall_blueprint)
app.register_blueprint(isay_blueprint)
app.register_blueprint(togo_blueprint)
app.register_blueprint(fireflower_blueprint)
def database(app, db):
"""
初始化数据库
:param app:
:return:
"""
db.init_app(app)
db.create_all()
db.session.commit()
def create_app():
my_app = Flask(__name__)
with my_app.app_context():
# app注册蓝图
register_bp(my_app)
# app加载配置
my_app.config.from_object(BaseConfig)
# 数据库管理对象
database(my_app, db)
# 用于登录验证
login_manager.init_app(my_app)
login_manager.login_view = 'login.login'
return my_app
😃5 manager.py 启动项目
从app/__init__.py导入create()函数用于启动,同时设置了错误页面。
# coding: utf-8
# 作者(@Author): Messimeimei
# 创建时间(@Created_time): 2023/1/3 13:50
"""启动文件"""
from app import create_app
from flask import render_template, session
app = create_app()
# 404页面
@app.errorhandler(404)
def page_not_found(e):
return render_template('error.html'), 404
# 500页面
@app.errorhandler(500)
def internal_server_error(e):
return render_template('error.html'), 500
if __name__ == '__main__':
app.run(debug=True)
💳6 一些静态文件
下面是要用到的所有静态文件,篇幅原因这里就不全部放出来了,有需要的朋友在评论区扣1我会及时给的。背景图片和照片墙大家可以自己选。
![](https://i-blog.csdnimg.cn/blog_migrate/f43d6f536977b26da4103976122be735.png)