@@ -0,0 +1,311 @@
|
||||
# Canvas API - Hierarchical Image Fallback System
|
||||
|
||||
## Overview
|
||||
The Canvas API provides intelligent image serving with automatic fallback across multiple directory levels. This eliminates image duplication and makes it easy to share common elements (like foreground plants or backgrounds) across multiple products.
|
||||
|
||||
## Architecture
|
||||
|
||||
### URL Pattern
|
||||
```
|
||||
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
|
||||
```
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
app/static/images/
|
||||
├── doors/
|
||||
│ ├── storm/
|
||||
│ │ ├── 404/
|
||||
│ │ │ ├── base.jpg # Product-specific base
|
||||
│ │ │ ├── door-white.png # Product-specific door color
|
||||
│ │ │ └── door-black.png
|
||||
│ │ ├── 505/
|
||||
│ │ │ ├── base.jpg
|
||||
│ │ │ └── door-white.png
|
||||
│ │ └── layers/
|
||||
│ │ ├── base.jpg # Fallback for ALL storm doors
|
||||
│ │ ├── hardware.png # Shared hardware
|
||||
│ │ └── foreground.png # Shared foreground
|
||||
│ ├── entry/
|
||||
│ │ ├── 600/
|
||||
│ │ │ ├── base.jpg
|
||||
│ │ │ └── door-bronze.png
|
||||
│ │ └── layers/
|
||||
│ │ └── foreground-plants.png # Shared for entry doors
|
||||
│ └── layers/
|
||||
│ ├── hardware-generic.png # Fallback for ALL doors
|
||||
│ └── foreground-default.png
|
||||
├── windows/
|
||||
│ ├── storm/
|
||||
│ │ └── layers/
|
||||
│ │ └── foreground-minimal.png
|
||||
│ └── layers/
|
||||
│ └── base-generic.jpg
|
||||
└── layers/
|
||||
├── foreground-default.png # Global fallback
|
||||
├── base-generic.jpg # Global fallback
|
||||
└── hardware-generic.png # Global fallback
|
||||
```
|
||||
|
||||
### Fallback Priority
|
||||
When requesting an image, the system searches in this order (most specific to least specific):
|
||||
|
||||
1. **Product-specific with variant**: `/images/<type>/<subtype>/<code>/<layer>-<color>.png`
|
||||
2. **Product-specific**: `/images/<type>/<subtype>/<code>/<layer>.png`
|
||||
3. **Subtype fallback with variant**: `/images/<type>/<subtype>/layers/<layer>-<color>.png`
|
||||
4. **Subtype fallback**: `/images/<type>/<subtype>/layers/<layer>.png`
|
||||
5. **Type fallback**: `/images/<type>/layers/<layer>.png`
|
||||
6. **Global fallback**: `/images/layers/<layer>.png`
|
||||
7. **404**: Image not found
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Get Layer Image
|
||||
```
|
||||
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `product_code` (path, required): Product code (e.g., "404", "505")
|
||||
- `layer` (path, required): Layer name - must be one of: `base`, `door`, `hardware`, `overlay`, `foreground`
|
||||
- `color` (query, optional): Color variant (e.g., "white", "black", "bronze")
|
||||
- `material` (query, optional): Material variant (for future use)
|
||||
|
||||
**Response:**
|
||||
- Success: Image file (PNG, JPG, JPEG, or WebP)
|
||||
- Error 400: Invalid layer name
|
||||
- Error 404: Image not found
|
||||
|
||||
**Examples:**
|
||||
```
|
||||
GET /api/canvas/404/base
|
||||
GET /api/canvas/404/door?color=white
|
||||
GET /api/canvas/505/hardware
|
||||
GET /api/canvas/600/foreground
|
||||
```
|
||||
|
||||
### Get Canvas Info
|
||||
```
|
||||
GET /api/canvas/<product_code>/info
|
||||
```
|
||||
|
||||
Returns metadata about available layers for a product.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"product": "404",
|
||||
"type": "window",
|
||||
"subtype": "storm-window",
|
||||
"availableLayers": {
|
||||
"base": true,
|
||||
"door": true,
|
||||
"door_colors": ["white", "black", "bronze", "sandstone"],
|
||||
"hardware": true,
|
||||
"foreground": true
|
||||
},
|
||||
"colors": ["White", "Black", "Bronze", "Sandstone"],
|
||||
"materials": ["Aluminum"]
|
||||
}
|
||||
```
|
||||
|
||||
### Test Endpoint
|
||||
```
|
||||
GET /api/canvas/test
|
||||
```
|
||||
|
||||
Verifies the Canvas API is running.
|
||||
|
||||
## Product Configuration
|
||||
|
||||
### Enable Canvas API
|
||||
Add `"useCanvasAPI": true` to the product's `imageConfig`:
|
||||
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"description": "#404 FALCON STORM WINDOWS",
|
||||
"colors": ["White", "Black", "Bronze", "Sandstone"],
|
||||
"materials": ["Aluminum"],
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That's it! No need to specify paths or layers - the Canvas API will automatically find them using the fallback system.
|
||||
|
||||
### Optional: Legacy Layered Configuration
|
||||
If you're migrating from the old static layered system, you can keep both configurations during transition:
|
||||
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true,
|
||||
"layered": true,
|
||||
"basePath": "images/products/404/",
|
||||
"layers": {
|
||||
"base": "base.jpg",
|
||||
"door": {
|
||||
"white": "door-white.png",
|
||||
"black": "door-black.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## File Organization Strategy
|
||||
|
||||
### Product-Specific Files
|
||||
Place in `/images/<type>/<subtype>/<code>/`:
|
||||
- Unique base images with specific backgrounds
|
||||
- Color variants that are product-specific
|
||||
- Custom hardware or features
|
||||
|
||||
### Subtype Shared Files
|
||||
Place in `/images/<type>/<subtype>/layers/`:
|
||||
- Common hardware for that subtype
|
||||
- Shared foreground elements (plants, railings)
|
||||
- Default base images for that subtype
|
||||
|
||||
### Type Shared Files
|
||||
Place in `/images/<type>/layers/`:
|
||||
- Generic hardware for all products of that type
|
||||
- Common decorative elements
|
||||
|
||||
### Global Shared Files
|
||||
Place in `/images/layers/`:
|
||||
- Universal fallback images
|
||||
- Default placeholder elements
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From Static Layered System
|
||||
|
||||
**Before:**
|
||||
```
|
||||
app/static/images/
|
||||
└── products/
|
||||
├── 404/
|
||||
│ ├── base.jpg
|
||||
│ ├── door-white.png
|
||||
│ ├── door-black.png
|
||||
│ ├── hardware.png
|
||||
│ └── plants.png
|
||||
└── 505/
|
||||
├── base.jpg
|
||||
├── door-white.png
|
||||
├── hardware.png # Duplicate!
|
||||
└── plants.png # Duplicate!
|
||||
```
|
||||
|
||||
**After:**
|
||||
```
|
||||
app/static/images/
|
||||
└── doors/
|
||||
└── storm/
|
||||
├── 404/
|
||||
│ ├── base.jpg
|
||||
│ ├── door-white.png
|
||||
│ └── door-black.png
|
||||
├── 505/
|
||||
│ ├── base.jpg
|
||||
│ └── door-white.png
|
||||
└── layers/
|
||||
├── hardware.png # Shared by both!
|
||||
└── plants.png # Shared by both!
|
||||
```
|
||||
|
||||
**Update JSON:**
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true // Enable Canvas API
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Migration Steps
|
||||
|
||||
1. **Identify shared elements**: Look for duplicate files across products
|
||||
2. **Reorganize directory**: Move files to appropriate fallback levels
|
||||
3. **Update product JSON**: Add `"useCanvasAPI": true`
|
||||
4. **Test**: Verify images load correctly
|
||||
5. **Cleanup**: Remove old duplicate files
|
||||
|
||||
## Development Tips
|
||||
|
||||
### Testing Fallback Behavior
|
||||
1. Start with global fallback layer
|
||||
2. Test product without specific file - should show global fallback
|
||||
3. Add subtype-specific layer - should override global
|
||||
4. Add product-specific layer - should override subtype
|
||||
|
||||
### Debugging
|
||||
Check Flask logs to see which image path was found:
|
||||
```
|
||||
[INFO] Found image: /path/to/images/doors/storm/404/base.jpg
|
||||
```
|
||||
|
||||
If image not found, logs show what was searched:
|
||||
```
|
||||
[WARNING] No image found for 404/door (color: white)
|
||||
```
|
||||
|
||||
### Browser Testing
|
||||
Open browser console to see Canvas API requests:
|
||||
```
|
||||
GET /api/canvas/404/base → 200 OK
|
||||
GET /api/canvas/404/door?color=white → 200 OK
|
||||
GET /api/canvas/404/foreground → 200 OK (from fallback)
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Advantages
|
||||
- **Reduced file duplication**: Share common elements across products
|
||||
- **Smaller total file size**: No duplicate hardware/foreground images
|
||||
- **Easier maintenance**: Update one shared file affects all products
|
||||
- **Smart caching**: Browser caches shared layers for faster loading
|
||||
|
||||
### Best Practices
|
||||
- Optimize images before uploading (TinyPNG, ImageOptim)
|
||||
- Use appropriate formats:
|
||||
- JPG for base layers (no transparency needed)
|
||||
- PNG for layers with transparency
|
||||
- WebP for modern browsers (optional)
|
||||
- Keep file sizes reasonable:
|
||||
- Base: 200-500KB
|
||||
- Layers: 50-200KB each
|
||||
- Foreground: 50-150KB
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Image Not Showing
|
||||
1. Check product's `baseType` and `subType` in products.json
|
||||
2. Verify file exists in correct directory structure
|
||||
3. Check Flask logs for the search path
|
||||
4. Ensure product has `"useCanvasAPI": true`
|
||||
|
||||
### Wrong Image Displayed
|
||||
- Check fallback priority - more specific path should override generic
|
||||
- Verify file naming matches expected pattern
|
||||
- Check that color parameter matches filename (lowercase)
|
||||
|
||||
### 404 Errors
|
||||
- Open `/api/canvas/<product>/info` to see what's available
|
||||
- Check file permissions
|
||||
- Verify directory structure matches expected pattern
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential additions to the Canvas API system:
|
||||
- Dynamic color tinting (apply color to grayscale base)
|
||||
- Image composition/overlays
|
||||
- Real-time layer opacity/blend modes
|
||||
- A/B testing different foreground elements
|
||||
- Seasonal foreground rotation
|
||||
- Material-based texture overlays
|
||||
Reference in New Issue
Block a user