Mirrored from https://github.com/LeeRoySheep/MovieWebApp.git
Synced Nov 2, 18:15 UTC
added js and css
Lee Roy Stevenson committed
May 22, 2025 at 00:58 UTC
9bdb43f07668d77ffe0b6f350306bd917fc2c25a
9 files changed
+534
-13
app.py
+1
-1
@@ -211,5 +211,5 @@ def recommendation(user_id):
211
212
if __name__ == "__main__":
213
214
- app.run()
214
+ app.run(debug=True,host="127.0.0.1",port=5000)
215
static/action.js
new
+158
@@ -0,0 +1,158 @@
1
+// Wrap all DOM-dependent code inside a DOMContentLoaded event listener
2
+document.addEventListener('DOMContentLoaded', function() {
3
+ // Get the canvas element and its 2D rendering context
4
+ const canvas = document.getElementById('pixelCanvas');
5
+ const ctx = canvas.getContext('2d');
6
+
7
+ // Text content and properties for the canvas-drawn text
8
+ const mainText = "Dynamic Pixelmap Background";
9
+ const subText = "Watch the colors change!";
10
+ const thirdText = "This text has pixel-level contrast!";
11
+ let fontSizeH1 = 48; // px - will be dynamically adjusted
12
+ let fontSizeP = 24; // px - will be dynamically adjusted
13
+ const fontName = "Inter, sans-serif"; // Ensure font is available or fallback
14
+
15
+ /**
16
+ * Generates a random hexadecimal color code, biased towards darker shades (slightly brighter than before).
17
+ *
18
+ * @returns {string} A random hexadecimal color code (e.g., '#RRGGBB').
19
+ */
20
+ function generateRandomColor() {
21
+ // Limit random byte generation to a darker range (0-150) - adjusted for brighter background
22
+ const getRandomDarkByte = () => Math.floor(Math.random() * 151); // Max value 150
23
+
24
+ const r = getRandomDarkByte();
25
+ const g = getRandomDarkByte();
26
+ const b = getRandomDarkByte();
27
+
28
+ // Helper to convert a number to a two-digit hexadecimal string
29
+ const toHex = (c) => {
30
+ const hex = c.toString(16);
31
+ return hex.length === 1 ? '0' + hex : hex; // Ensure two digits (e.g., 'f' becomes '0f')
32
+ };
33
+
34
+ // Combine RGB components into a full hexadecimal color string
35
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
36
+ }
37
+
38
+ // Function to set up canvas dimensions and handle window resizing
39
+ function resizeCanvas() {
40
+ canvas.width = window.innerWidth;
41
+ canvas.height = window.innerHeight;
42
+
43
+ // Dynamically adjust font sizes based on screen width for responsiveness
44
+ if (canvas.width < 600) {
45
+ fontSizeH1 = 32; // Smaller font for small screens
46
+ fontSizeP = 18;
47
+ } else if (canvas.width < 900) {
48
+ fontSizeH1 = 40; // Medium font for medium screens
49
+ fontSizeP = 22;
50
+ } else {
51
+ fontSizeH1 = 48; // Original font size for large screens
52
+ fontSizeP = 24;
53
+ }
54
+ // No need to clear here, animate will clear and redraw
55
+ }
56
+
57
+ // Define parameters for drawing background pixels
58
+ const pixelSize = 1; // The size (width and height) of each square pixel in pixels (remains 1)
59
+ const pixelsPerFrame = 500; // The number of random background pixels to draw in each animation frame (increased for quicker change)
60
+
61
+ /**
62
+ * Draws a single randomly colored pixel on the canvas.
63
+ * The pixel's position is snapped to a grid defined by `pixelSize`.
64
+ */
65
+ function drawRandomBackgroundPixel() {
66
+ const x = Math.floor(Math.random() * (canvas.width / pixelSize)) * pixelSize;
67
+ const y = Math.floor(Math.random() * (canvas.height / pixelSize)) * pixelSize;
68
+ const color = generateRandomColor();
69
+ ctx.fillStyle = color;
70
+ ctx.fillRect(x, y, pixelSize, pixelSize);
71
+ }
72
+
73
+ /**
74
+ * Draws text on the canvas with pixel-level contrast to the background.
75
+ * This function uses an off-screen canvas to get the text mask.
76
+ */
77
+ function drawContrastingText() {
78
+ // Create an off-screen canvas for text rendering
79
+ const tempCanvas = document.createElement('canvas');
80
+ const tempCtx = tempCanvas.getContext('2d');
81
+ tempCanvas.width = canvas.width;
82
+ tempCanvas.height = canvas.height;
83
+
84
+ // Set text properties for the off-screen canvas
85
+ tempCtx.font = `${fontSizeH1}px ${fontName}`;
86
+ tempCtx.textAlign = 'center';
87
+ tempCtx.textBaseline = 'middle';
88
+ tempCtx.fillStyle = 'black'; // Draw text in black to get a clear mask
89
+
90
+ // Calculate text positions (centered)
91
+ const centerX = canvas.width / 2;
92
+ const centerY = canvas.height / 2;
93
+ // Adjust lineHeight based on current fontSizeH1
94
+ const lineHeight = fontSizeH1 * 1.2; // Approximate line height
95
+
96
+ // Draw text on the off-screen canvas
97
+ tempCtx.fillText(mainText, centerX, centerY - lineHeight);
98
+ tempCtx.font = `${fontSizeP}px ${fontName}`;
99
+ tempCtx.fillText(subText, centerX, centerY);
100
+ tempCtx.fillText(thirdText, centerX, centerY + lineHeight);
101
+
102
+ // Get pixel data from the main canvas (current background)
103
+ const mainImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
104
+ const mainPixels = mainImageData.data;
105
+
106
+ // Get pixel data from the off-screen text canvas (text mask)
107
+ const tempImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
108
+ const tempPixels = tempImageData.data;
109
+
110
+ // Loop through all pixels
111
+ for (let i = 0; i < mainPixels.length; i += 4) {
112
+ // Check if the pixel on the temporary canvas is part of the text (opaque black)
113
+ if (tempPixels[i + 3] > 0) { // If this pixel is part of the text
114
+ // Get the background color at this pixel position from main canvas
115
+ const bgR = mainPixels[i];
116
+ const bgG = mainPixels[i + 1];
117
+ const bgB = mainPixels[i + 2];
118
+
119
+ // Calculate the inverse color for contrast.
120
+ mainPixels[i] = 255 - bgR;
121
+ mainPixels[i + 1] = 255 - bgG;
122
+ mainPixels[i + 2] = 255 - bgB;
123
+ }
124
+ }
125
+
126
+ // Put the modified pixel data back onto the main canvas
127
+ ctx.putImageData(mainImageData, 0, 0);
128
+ }
129
+
130
+ /**
131
+ * The main animation loop.
132
+ * It draws a set number of random background pixels per frame and then redraws the contrasting text.
133
+ */
134
+ function animate() {
135
+ // Draw multiple background pixels in each frame (accumulating effect)
136
+ for (let i = 0; i < pixelsPerFrame; i++) {
137
+ drawRandomBackgroundPixel();
138
+ }
139
+
140
+ // Draw the contrasting text on top of the background
141
+ drawContrastingText();
142
+
143
+ // Request the next animation frame, creating a smooth loop
144
+ requestAnimationFrame(animate);
145
+ }
146
+
147
+ // Initial setup and animation start
148
+ resizeCanvas(); // Set the initial canvas size and font sizes
149
+ animate(); // Start the pixel drawing animation
150
+
151
+ // Add an event listener to resize the canvas whenever the window is resized
152
+ window.addEventListener('resize', () => {
153
+ resizeCanvas(); // Recalculate canvas size and font sizes
154
+ // On resize, you might want to clear the canvas and immediately redraw text
155
+ // to avoid temporary distortions before the pixels fill in.
156
+ // ctx.clearRect(0, 0, canvas.width, canvas.height); // Optional: clear on resize
157
+ });
158
+});
static/homepic.jpg
Binary files /dev/null and b/static/homepic.jpg differ
static/style.css
new
+317
@@ -0,0 +1,317 @@
1
+body {
2
+ font-family: "Inter", sans-serif;
3
+ margin: 0;
4
+ overflow-x: hidden; /* Prevent horizontal scrollbar on body */
5
+ overflow-y: auto; /* Enable vertical scrolling for the entire page */
6
+ display: flex; /* Use flexbox to center canvas content conceptually */
7
+ justify-content: center;
8
+ align-items: center;
9
+ min-height: 100vh;
10
+ background-color: #000; /* Fallback for body background */
11
+}
12
+
13
+canvas {
14
+ display: block; /* Remove extra space below canvas */
15
+ position: fixed; /* Use fixed to keep it in viewport regardless of scroll */
16
+ top: 0;
17
+ left: 0;
18
+ z-index: -1; /* Place the canvas behind other content */
19
+ width: 100%; /* Ensure canvas covers full width */
20
+ height: 100%; /* Ensure canvas covers full height */
21
+}
22
+
23
+/* Styles for the new main content container */
24
+.main-content-container {
25
+ position: relative; /* Ensure content is positioned above the canvas */
26
+ z-index: 1; /* Bring content to the front */
27
+ display: flex;
28
+ flex-direction: column;
29
+ justify-content: center;
30
+ align-items: center;
31
+ background-color: rgba(0, 0, 0, 0.2); /* Semi-transparent dark background for contrast */
32
+ border-radius: 12px; /* Rounded corners */
33
+ padding: 30px; /* Padding inside the container */
34
+ margin: 20px auto; /* Changed margin to auto for horizontal centering */
35
+ width: 90%; /* Make it more fluid on smaller screens */
36
+ max-width: 700px; /* Limit width for readability on larger screens */
37
+ box-shadow: 0 8px 16px rgba(0,0,0,0.4); /* More prominent shadow */
38
+ color: white; /* Default text color for content */
39
+ text-align: center;
40
+ backdrop-filter: blur(3px); /* Optional: subtle blur effect for content behind */
41
+ box-sizing: border-box; /* Include padding in the element's total width */
42
+}
43
+
44
+.content-title {
45
+ font-size: 2.5rem; /* text-4xl equivalent */
46
+ font-weight: 700; /* font-bold equivalent */
47
+ margin-bottom: 1rem;
48
+ color: #E0E0E0; /* Slightly off-white for title */
49
+}
50
+
51
+.content-paragraph {
52
+ font-size: 1.1rem; /* text-lg equivalent */
53
+ line-height: 1.6;
54
+ margin-bottom: 1rem;
55
+ color: #F0F0F0; /* Slightly off-white for paragraph */
56
+}
57
+
58
+.content-image {
59
+ max-width: 200px; /* Ensure image is responsive */
60
+ height: auto;
61
+ border-radius: 8px;
62
+ margin-top: 1.5rem;
63
+ margin-bottom: 1.5rem;
64
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
65
+}
66
+
67
+/* Navigation */
68
+.topnav {
69
+ background-color: black;
70
+ display: flex;
71
+ gap: 15px;
72
+ padding: 12px 20px;
73
+ border-bottom: 2px solid darkred;
74
+ justify-content: center;
75
+}
76
+
77
+.topnav a {
78
+ color: darkgreen;
79
+ text-decoration: none;
80
+ padding: 8px 14px;
81
+ border-radius: 8px;
82
+ transition: background-color 0.3s ease, color 0.3s ease;
83
+}
84
+
85
+.topnav a.active,
86
+.topnav a:hover {
87
+ background-color: darkgreen;
88
+ color: darkred;
89
+}
90
+
91
+/* Title styling */
92
+h1.title {
93
+ text-align: center;
94
+ margin: 20px 0;
95
+ color: darkgreen;
96
+}
97
+
98
+/* Status message */
99
+.status-message p {
100
+ text-align: center;
101
+ font-weight: bold;
102
+ color: darkgreen;
103
+ margin-bottom: 20px;
104
+}
105
+
106
+/* Carousel container with horizontal scroll */
107
+.css-carousel {
108
+ display: flex;
109
+ overflow-x: auto; /* Enable horizontal scrolling */
110
+ scroll-snap-type: x mandatory;
111
+ -webkit-overflow-scrolling: touch;
112
+ padding-bottom: 12px; /* Keep bottom padding for scrollbar */
113
+ scrollbar-width: thin;
114
+ scrollbar-color: darkred black;
115
+ border: 2px solid darkred; /* Full border for clearer limits */
116
+ scroll-behavior: smooth;
117
+ color: darkgreen;
118
+ width: 100%; /* Ensure it takes full width of its parent */
119
+ box-sizing: border-box; /* Ensure padding is included in width */
120
+ gap: 20px; /* Space between carousel items */
121
+
122
+ /* New: Dynamic scroll-padding for consistent edge snapping */
123
+ /* This ensures the item snaps to the start, but we add padding to the scroll container itself */
124
+ /* The padding is calculated to center the item based on its flex-basis */
125
+ scroll-padding-inline: calc(50% - (250px / 2)); /* 50% of carousel width - half of default item width */
126
+}
127
+
128
+/* Scrollbar styling */
129
+.css-carousel::-webkit-scrollbar {
130
+ height: 8px;
131
+}
132
+
133
+.css-carousel::-webkit-scrollbar-track {
134
+ background: black;
135
+}
136
+
137
+.css-carousel::-webkit-scrollbar-thumb {
138
+ background-color: darkred;
139
+ border-radius: 10px;
140
+ border: 2px solid black;
141
+}
142
+
143
+/* Scroll instruction text */
144
+.scroll-instruction {
145
+ color: darkgreen;
146
+ font-size: 0.9rem;
147
+ text-align: center;
148
+ margin-top: 6px;
149
+ font-style: italic;
150
+ user-select: none;
151
+}
152
+
153
+/* Individual movie card */
154
+.carousel-item {
155
+ flex: 0 0 250px; /* Fixed width for items, no shrinking/growing by default */
156
+ scroll-snap-align: center; /* Center the item when snapped */
157
+ background-color: #111;
158
+ border: 1px solid darkgreen;
159
+ border-radius: 12px;
160
+ padding: 15px;
161
+ color: darkgreen;
162
+ box-sizing: border-box;
163
+ display: flex;
164
+ flex-direction: column;
165
+ align-items: center;
166
+ transition: transform 0.3s ease;
167
+}
168
+
169
+/* Slight scale on hover */
170
+.carousel-item:hover {
171
+ transform: scale(1.05);
172
+ border-color: darkred;
173
+}
174
+
175
+/* Movie image */
176
+.carousel-item img {
177
+ border-radius: 10px;
178
+ max-width: 100%;
179
+ height: auto; /* Ensures aspect ratio is maintained */
180
+ margin-bottom: 10px;
181
+ object-fit: contain; /* Ensures entire image is visible without distortion */
182
+}
183
+
184
+/* Movie info list */
185
+.carousel-item ul {
186
+ list-style: none;
187
+ padding-left: 0;
188
+ width: 100%;
189
+ margin: 0;
190
+ color: darkgreen;
191
+}
192
+
193
+.carousel-item ul li {
194
+ margin: 5px 0;
195
+ font-size: 0.9rem;
196
+ color: darkgreen;
197
+}
198
+
199
+.carousel-item ul li a {
200
+ color: darkgreen;
201
+ text-decoration: none;
202
+ transition: color 0.3s ease;
203
+}
204
+
205
+.carousel-item ul li a:hover {
206
+ color: darkred;
207
+}
208
+
209
+/* Delete button styling */
210
+button,
211
+.back_button a{
212
+ background-color: darkred;
213
+ color: darkgreen;
214
+ border: none;
215
+ border-radius: 8px;
216
+ padding: 8px 12px;
217
+ cursor: pointer;
218
+ font-size: 1rem;
219
+ transition: background-color 0.3s ease, color 0.3s ease;
220
+}
221
+
222
+button:hover,
223
+.back_button a:hover {
224
+ background-color: darkgreen;
225
+ color: darkred;
226
+}
227
+
228
+.actions {
229
+ display: flex;
230
+ flex-wrap: wrap;
231
+ gap: 20px; /* space between forms */
232
+ justify-content: center;
233
+ margin-bottom: 20px;
234
+}
235
+
236
+.action-form {
237
+ background-color: #1a0000; /* very dark red background */
238
+ padding: 15px;
239
+ border-radius: 12px;
240
+ display: flex;
241
+ flex-direction: column;
242
+ align-items: center;
243
+ min-width: 220px;
244
+ box-sizing: border-box;
245
+}
246
+
247
+.action-form label {
248
+ margin-bottom: 8px;
249
+ color: darkgreen;
250
+}
251
+
252
+.action-form input[type="text"] {
253
+ padding: 8px;
254
+ border-radius: 10px;
255
+ border: none;
256
+ margin-bottom: 12px;
257
+ width: 100%;
258
+ max-width: 200px;
259
+}
260
+
261
+.action-form button {
262
+ background-color: darkred;
263
+ color: darkgreen;
264
+ border: none;
265
+ padding: 10px 16px;
266
+ border-radius: 12px;
267
+ cursor: pointer;
268
+ transition: background-color 0.3s ease;
269
+ width: 100%;
270
+ max-width: 200px;
271
+}
272
+
273
+.action-form button:hover {
274
+ background-color: darkgreen;
275
+ color: black;
276
+}
277
+/* Responsive adjustments for smaller screens */
278
+@media (max-width: 768px) {
279
+ .main-content-container {
280
+ padding: 20px;
281
+ margin: 10px auto; /* Adjusted margin for centering */
282
+ width: calc(100% - 40px); /* Adjust width to account for 20px left/right margin */
283
+ }
284
+ .content-title {
285
+ font-size: 2rem;
286
+ }
287
+ .content-paragraph {
288
+ font-size: 1rem;
289
+ }
290
+ .carousel-item {
291
+ flex-basis: 80vp; /* Adjusted flex-basis for smaller screens */
292
+ padding: 10px;
293
+ }
294
+ .css-carousel {
295
+ scroll-padding-inline: calc(50% - (80vp / 2)); /* Adjust for smaller item size */
296
+ }
297
+}
298
+
299
+/* Responsive tweaks */
300
+@media (max-width: 600px) {
301
+ .carousel-item {
302
+ flex-basis: 180px; /* Adjusted flex-basis */
303
+ padding: 10px;
304
+ }
305
+ .css-carousel {
306
+ scroll-padding-inline: calc(50% - (180px / 2)); /* Adjust for smaller item size */
307
+ }
308
+}
309
+
310
+@media (max-width: 400px) {
311
+ .carousel-item {
312
+ flex-basis: 50vp; /* Adjusted flex-basis for very small screens */
313
+ }
314
+ .css-carousel {
315
+ scroll-padding-inline: calc(50% - (60vp/ 2)); /* Adjust for smallest item size */
316
+ }
317
+}
templates/base.html
+5
-3
@@ -4,9 +4,13 @@
4
<meta charset="UTF-8" />
5
<meta name="viewport" content="width=device-width, initial-scale=1" />
6
<title>{% block title %}{% endblock %}</title>
7
- <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
8
+ <script src="{{ url_for('static', filename='action.js') }}"></script>
9
</head>
10
<body>
11
+<canvas id="pixelCanvas"></canvas>
12
+
13
+<main class="main-content-container">
14
<header class="topnav">
15
{% set path = request.path %}
16
<a href="/" class="{% if path == '/' %}active{% endif %}">Home</a>
@@ -15,8 +19,6 @@
19
<a href="/users/new" class="{% if path == '/users/new' %}active{% endif %}">New User</a>
20
<a href="/movies/new" class="{% if path == '/movies/new' %}active{% endif %}">New Movie</a>
21
</header>
18
-
19
-<main>
22
{% block content %}{% endblock %}
23
</main>
24
</body>
templates/home.html
+12
-5
@@ -2,15 +2,22 @@
2
{% block title %}Movie - WebApp{% endblock %}
3
{% block content %}
4
<h1 class="home_title">Welcome to the Movie Web App</h1>
5
- <section class="home_section">
6
- <h2>Features</h2>
7
- <ul>
8
- <li>View all movies</li>
5
+ <section class="content-container">
6
+ <h2 class="content-title">Features</h2>
7
+ <img src="/static/homepic.jpg" alt="Movie Image" class="content-image">
8
+
9
+ <p class="content-paragraph">
10
+ This Movie Web App has the following features:
11
+ </p>
12
+ <ul class="content-paragraph">
13
+ <li>View all <a href="/movies">movies</a></li>
14
<li>View a single movie</li>
10
- <li>View all users</li>
15
+ <li>View all <a href="/users">users</a></li>
16
<li>View a single user</li>
17
<li>View all user's movies list</li>
18
<li>View a single user's movie</li>
19
</ul>
20
</section>
21
+
22
+
23
{% endblock %}
\ No newline at end of file
templates/index.html
new
+35
@@ -0,0 +1,35 @@
1
+<!DOCTYPE html>
2
+<html lang="en">
3
+
4
+<head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Random Pixelmap Background with Content</title>
8
+ <link rel="stylesheet" href="static/style.css">
9
+
10
+
11
+
12
+</head>
13
+
14
+<body>
15
+
16
+ <canvas id="pixelCanvas"></canvas>
17
+
18
+ <div class="main-content-container">
19
+ <h2 class="content-title">Welcome to the Dynamic World!</h2>
20
+ <p class="content-paragraph">
21
+ This is a customizable area where you can add your images and text.
22
+ The pixelmap background is active behind this content.
23
+ </p>
24
+
25
+ <img src="https://m.media-amazon.com/images/M/MV5BNjFkNjdiZjUtNzUzNy00NWM5LWFlNDUtNTRiYmJiZWNiYjkwXkEyXkFqcGc@._V1_SX300.jpg" alt="Placeholder Image" class="content-image">
26
+
27
+ <p class="content-paragraph">
28
+ Feel free to add more images, paragraphs, or any other HTML elements here.
29
+ The semi-transparent background ensures good readability.
30
+ </p>
31
+ </div>
32
+
33
+</body>
34
+
35
+</html>
\ No newline at end of file
templates/movies.html
+3
-2
@@ -16,9 +16,10 @@
16
<div class="css-carousel" tabindex="0" aria-label="Movies carousel">
17
{% for movie in movies %}
18
<div class="carousel-item" role="group" aria-roledescription="slide" aria-label="{{ loop.index }} of {{ movies|length }}">
19
- <img src="{{ movie.poster }}" alt="{{ movie.name }}" height="200" />
19
+ <a href="/movies/{{ movie.id }}">
20
+ <img src="{{ movie.poster }}" alt="{{ movie.name }}" height="200"></a>
21
<ul>
21
- <li><a href="/movies/{{ movie.id }}">{{ movie.name }} ({{ movie.year }})</a></li>
22
+ <li>{{ movie.name }} ({{ movie.year }})</a></li>
23
<li>
24
<form action="/movies" method="post">
25
<button type="submit" id="movie_id" name="movie_id" value="{{ movie.id }}">Delete</button>
templates/user_movies.html
+3
-2
@@ -35,9 +35,10 @@
35
<div class="css-carousel" tabindex="0" aria-label="User's movies carousel">
36
{% for movie in user_movies %}
37
<div class="carousel-item" role="group" aria-roledescription="slide" aria-label="{{ loop.index }} of {{ user_movies|length }}">
38
- <img src="{{ movie.poster }}" alt="{{ movie.name }}" height="200">
38
+ <a href="/users/{{ user.id }}/{{ movie.id }}">
39
+ <img src="{{ movie.poster }}" alt="{{ movie.name }}" height="200"></a>
40
<ul>
40
- <li><a href="/users/{{ user.id }}/{{ movie.id }}">{{ movie.name }} ({{ movie.year }})</a></li>
41
+ <li>{{ movie.name }} ({{ movie.year }})</li>
42
<li>Director: {{ movie.director }}</li>
43
<li>Rating: {{ movie.rating }}</li>
44
<li>User Rating: {{ movie.user_rating }}</li>