Bài viết này ghi lại quá trình phát triển một trình chỉnh sửa bản đồ phẳng bằng Flask theo phương pháp Phát triển Tăng dần. Phương pháp tiếp cận là xây dựng từng chức năng nhỏ một cách có hệ thống.
Bước 1: Khởi tạo cấu trúc dự án
1. Tạo thư mục cơ bản
F:/deepseek/xiaoshuo/
├── app.py # Điểm vào chính của ứng dụng
└── static/ # Tài nguyên tĩnh
└── css/
└── style.css
└── templates/ # Các mẫu HTML
└── index.html
2. Viết ứng dụng Flask tối giản
Tệp:app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
Tệp: templates/index.html
<html>
<head>
<title>Trình chỉnh sửa bản đồ đơn giản</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<h1>Chào mừng đến với trình chỉnh sửa bản đồ</h1>
</body>
</html>
Chạy thử:
python app.py
Truy cập http://127.0.0.1:5000 để xác nhận trang hiển thị bình thường.
Bước 2: Thêm chức năng cơ bản cho vùng vẽ
1. Cập nhật HTML để thêm vùng vẽ
Tệp:templates/index.html
<body>
<div class="canvas-container">
<canvas id="main-canvas" width="800" height="600"></canvas>
</div>
</body>
- Phần tử Canvas: Tạo một vùng vẽ có kích thước 800x600 pixel để làm khu vực vẽ.
2. Thêm CSS cơ bản
Tệp:static/css/style.css
.canvas-container {
margin: 20px;
border: 2px solid #ddd;
}
#main-canvas {
background: #f8f8f8;
}
3. Thêm tương tác JavaScript cho vùng vẽ
Tạo tệp static/js/script.js:
class SimpleEditor {
constructor() {
this.canvas = document.getElementById('main-canvas');
this.ctx = this.canvas.getContext('2d');
this.initEventListeners();
}
initEventListeners() {
this.canvas.addEventListener('mousedown', (e) => {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.drawDot(x, y);
});
}
drawDot(x, y) {
this.ctx.fillStyle = '#ff0000';
this.ctx.beginPath();
this.ctx.arc(x, y, 5, 0, Math.PI * 2);
this.ctx.fill();
}
}
const editor = new SimpleEditor();
- Định nghĩa lớp SimpleEditor: Đóng gói chức năng vẽ Canvas, giúp quản lý và mở rộng dễ dàng.
- Tham số constructor canvas: Truyền thể hiện của phần tử Canvas.
- Lấy ngữ cảnh Canvas ctx: Sử dụng phương thức getContext('2d') để lấy môi trường vẽ, dùng để thực thi các thao tác vẽ khác nhau.
Cập nhật HTML để nhúng JS:
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
Kiểm tra: Nhấp vào vùng vẽ sẽ xuất hiện một dấu chấm đỏ.
Bước 3: Từng bước thêm chức năng cốt lõi
1. Chức năng 1 - Vẽ đường thẳng
Cập nhật script.js:
class SimpleEditor {
constructor() {
this.isDrawing = false;
this.lastX = 0;
this.lastY = 0;
// ... mã gốc
}
initEventListeners() {
this.canvas.addEventListener('mousedown', (e) => this.startDrawing(e));
this.canvas.addEventListener('mousemove', (e) => this.draw(e));
this.canvas.addEventListener('mouseup', () => this.stopDrawing());
}
startDrawing(e) {
this.isDrawing = true;
const rect = this.canvas.getBoundingClientRect();
[this.lastX, this.lastY] = [e.clientX - rect.left, e.clientY - rect.top];
}
draw(e) {
if (!this.isDrawing) return;
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.ctx.beginPath();
this.ctx.moveTo(this.lastX, this.lastY);
this.ctx.lineTo(x, y);
this.ctx.stroke();
[this.lastX, this.lastY] = [x, y];
}
stopDrawing() {
this.isDrawing = false;
}
}
- Thực hiện chức năng vẽ: Phương thức startDrawing.
- Mục đích: Khởi tạo trạng thái vẽ khi người dùng nhấp chuột vào vùng vẽ.
- Lấy vị trí Canvas: Phương thức getBoundingClientRect() trả về thông tin biên của Canvas, bao gồm thuộc tính left và top, biểu thị vị trí thực tế của Canvas trên trang.
Kiểm tra: Giữ chuột và kéo để vẽ đường thẳng.
2. Chức năng 2 - Thay đổi màu bút vẽ
Cập nhật HTML (thêm nút chọn màu):
<div class="toolbar">
<button onclick="editor.setColor('#ff0000')">Đỏ</button>
<button onclick="editor.setColor('#00ff00')">Xanh lá</button>
</div>
- Nút màu: Cung cấp hai nút để đặt màu bút vẽ thành đỏ và xanh lá cây. Nhấp vào nút sẽ gọi phương thức JavaScript
editor.setColor(color).
Cập nhật script.js:
class SimpleEditor {
constructor() {
this.currentColor = '#ff0000';
// ... mã gốc
}
setColor(color) {
this.currentColor = color;
}
draw(e) {
this.ctx.strokeStyle = this.currentColor;
// ... mã gốc
}
}
Kiểm tra: Nhấp vào nút để thay đổi màu, vẽ các đường thẳng có màu khác nhau.
Bước 4: Mở rộng bằng cách module hóa (giới thiệu Blueprint)
1. Tạo thư mục Blueprint
xiaoshuo/
└── map_app/
├── __init__.py # Tệp trống
└── routes.py
2. Viết các route của Blueprint
Tệp:map_app/routes.py
from flask import Blueprint
map_bp = Blueprint('map', __name__)
@map_bp.route('/test')
def test():
return "Kiểm tra route Blueprint thành công!"
3. Đăng ký Blueprint vào ứng dụng chính
Cập nhật app.py:
from flask import Flask, render_template
from map_app.routes import map_bp # Import tuyệt đối
app = Flask(__name__)
app.register_blueprint(map_bp, url_prefix='/map')
# ... các route gốc
Kiểm tra: Truy cập http://127.0.0.1:5000/map/test sẽ hiển thị văn bản kiểm tra.
Bước 5: Kế hoạch phát triển theo giai đoạn
| Giai đoạn | Mục tiêu chức năng | Điểm kỹ thuật chính |
|---|---|---|
| Giai đoạn 1 | Vẽ cơ bản (điểm, đường thẳng) | Canvas API, lắng nghe sự kiện |
| Giai đoạn 2 | Chọn màu/độ dày | Quản lý trạng thái, tương tác UI |
| Giai đoạn 3 | Chức năng hoàn tác/làm lại | Ngăn xếp lịch sử thao tác, lưu trữ dữ liệu |
| Giai đoạn 4 | Vẽ vùng (đa giác) | Thuật toán đóng đường dẫn, lưu trữ tọa độ |
| Giai đoạn 5 | Lưu và tải dữ liệu | API Flask, đọc/ghi tệp |
| Giai đoạn 6 | Hệ thống lưới và chức năng hút | Tính toán toán học, tối ưu hóa vẽ Canvas |
Các nguyên tắc chính
- Đơn nhiệm: Mỗi giai đoạn chỉ thực hiện một chức năng cốt lõi.
- Kiểm tra tức thì: Xác minh kết quả ngay sau khi thêm một đoạn mã nhỏ.
- Sao lưu phiên bản: Sử dụng Git hoặc sao lưu thủ công để dễ dàng quay lại.
- Ghi lại tài liệu: Thêm chú thích cho mỗi chức năng, ghi lại ý tưởng triển khai.