@leroysheep / MovieWebApp Mirror · read-only / commits / 9bdb43f076
Fork 0

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>