@@ -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
|
||||
@@ -0,0 +1,285 @@
|
||||
# Canvas API Implementation Summary
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
Your product finder now has a **hierarchical image fallback system** (Canvas API) that automatically serves images with smart fallbacks across multiple directory levels. This eliminates duplicate files and makes it easy to share common elements (like plants, hardware, backgrounds) across products.
|
||||
|
||||
## System Architecture
|
||||
|
||||
### Three-Layer Approach
|
||||
|
||||
1. **Backend (Python/Flask)** - Smart image resolution with fallback logic
|
||||
2. **Frontend (JavaScript)** - Dynamic image loading using Canvas API endpoints
|
||||
3. **File Structure** - Hierarchical organization with automatic fallbacks
|
||||
|
||||
```
|
||||
Product Specific → Subtype Shared → Type Shared → Global Shared
|
||||
```
|
||||
|
||||
## What Changed
|
||||
|
||||
### New Files Created
|
||||
|
||||
1. **`app/blueprints/canvas.py`** - Canvas API Flask blueprint with fallback logic
|
||||
- `/api/canvas/<product_code>/<layer>?color=<color>` - Get image with fallback
|
||||
- `/api/canvas/<product_code>/info` - Get available layers info
|
||||
- `/api/canvas/test` - Test endpoint
|
||||
|
||||
2. **`information/CANVAS_API_GUIDE.md`** - Complete technical documentation
|
||||
3. **`information/CANVAS_API_QUICKSTART.md`** - 5-minute setup guide
|
||||
4. **`information/CANVAS_DIRECTORY_SETUP.md`** - Directory setup scripts and helpers
|
||||
5. **`information/FOREGROUND_LAYER_EXAMPLE.md`** - Foreground layer guide (from earlier)
|
||||
6. **`information/FOREGROUND_QUICKSTART.md`** - Quick foreground guide (from earlier)
|
||||
|
||||
### Modified Files
|
||||
|
||||
1. **`app/app.py`** - Registered canvas blueprint
|
||||
2. **`app/static/js/script.js`** - Added Canvas API support
|
||||
- `buildCanvasAPIUrl()` - Build API URLs
|
||||
- `updateCanvasAPIPreview()` - Update layers using API
|
||||
- Updated rendering logic to support Canvas API
|
||||
3. **`app/static/css/styles.css`** - Added `.layer-foreground` with z-index 5 (from earlier)
|
||||
4. **`information/LAYERED_IMAGES.md`** - Updated with foreground layer info
|
||||
5. **`information/products-layered-example.json`** - Added Canvas API example (product #700)
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. Directory Structure
|
||||
```
|
||||
app/static/images/
|
||||
├── doors/
|
||||
│ └── storm/
|
||||
│ ├── 404/
|
||||
│ │ └── door-white.png # Product-specific
|
||||
│ └── layers/
|
||||
│ └── hardware.png # Shared by all storm doors
|
||||
└── layers/
|
||||
└── foreground.png # Global fallback
|
||||
```
|
||||
|
||||
### 2. Product Configuration
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Automatic Fallback
|
||||
Request: `/api/canvas/404/hardware`
|
||||
|
||||
Searches in order:
|
||||
1. `/images/doors/storm/404/hardware.png` ← Product-specific
|
||||
2. `/images/doors/storm/layers/hardware.png` ← Subtype shared ✓ **Found!**
|
||||
3. `/images/doors/layers/hardware.png` ← Type shared
|
||||
4. `/images/layers/hardware.png` ← Global
|
||||
|
||||
### 4. Frontend Rendering
|
||||
```javascript
|
||||
// JavaScript automatically calls Canvas API
|
||||
const url = buildCanvasAPIUrl('404', 'door', 'white');
|
||||
// Result: /api/canvas/404/door?color=white
|
||||
|
||||
doorLayer.src = url; // Image loads with automatic fallback!
|
||||
```
|
||||
|
||||
## Layering System (Updated)
|
||||
|
||||
All 5 layers are now supported:
|
||||
|
||||
1. **Base** (z-index: 1) - Background/house/frame
|
||||
2. **Door** (z-index: 2) - Door/window panel (color variants)
|
||||
3. **Hardware** (z-index: 3) - Handles, locks, hinges
|
||||
4. **Overlay** (z-index: 4) - Glass views, decorative overlays
|
||||
5. **Foreground** (z-index: 5) - Plants, decorations ⭐ **NEW from earlier request**
|
||||
|
||||
## Benefits
|
||||
|
||||
### ✅ Eliminates Duplication
|
||||
Before: Each product has own copy of hardware.png
|
||||
After: One shared hardware.png for all products of that type
|
||||
|
||||
### ✅ Smart Fallbacks
|
||||
Product can have custom image OR inherit from parent levels automatically
|
||||
|
||||
### ✅ Easier Maintenance
|
||||
Update one shared file → affects all products using it
|
||||
|
||||
### ✅ Flexible Organization
|
||||
- Product-specific overrides: `/images/<type>/<subtype>/<code>/`
|
||||
- Subtype shared: `/images/<type>/<subtype>/layers/`
|
||||
- Type shared: `/images/<type>/layers/`
|
||||
- Global fallback: `/images/layers/`
|
||||
|
||||
### ✅ Reduced File Size
|
||||
Example with 10 products sharing hardware + foreground:
|
||||
- Before: 10 × (100KB + 150KB) = 2.5MB
|
||||
- After: 1 × (100KB + 150KB) = 250KB
|
||||
- **Savings: 2.25MB (90% reduction for shared elements)**
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: All Storm Doors Share Hardware
|
||||
```
|
||||
/images/doors/storm/layers/hardware.png ← One file
|
||||
```
|
||||
Products 404, 505, 450 all use this automatically!
|
||||
|
||||
### Example 2: Product 404 Has Custom Foreground
|
||||
```
|
||||
/images/doors/storm/404/foreground.png ← Product 404
|
||||
/images/doors/storm/layers/foreground.png ← Products 505, 450
|
||||
```
|
||||
Product 404 gets custom, others get shared.
|
||||
|
||||
### Example 3: Global Plant Layer
|
||||
```
|
||||
/images/layers/foreground-plants.png ← Used by ALL products
|
||||
```
|
||||
Unless a more specific version exists.
|
||||
|
||||
## Migration Path
|
||||
|
||||
### Phase 1: Keep Existing System (Low Risk)
|
||||
- Leave current products unchanged
|
||||
- New products use Canvas API: `"useCanvasAPI": true`
|
||||
- Both systems work simultaneously
|
||||
|
||||
### Phase 2: Identify Duplicates
|
||||
- Run duplicate file detection scripts
|
||||
- Find common hardware, foregrounds, bases
|
||||
- Plan shared directory structure
|
||||
|
||||
### Phase 3: Reorganize Files
|
||||
- Create type/subtype structure
|
||||
- Move shared files to appropriate `/layers/` directories
|
||||
- Update product JSON: Add `"useCanvasAPI": true`
|
||||
|
||||
### Phase 4: Cleanup
|
||||
- Remove old duplicate files
|
||||
- Verify all products load correctly
|
||||
- Measure storage savings
|
||||
|
||||
## API Endpoints Reference
|
||||
|
||||
```
|
||||
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
|
||||
→ Returns image file with automatic fallback
|
||||
|
||||
GET /api/canvas/<product_code>/info
|
||||
→ Returns metadata about available layers
|
||||
|
||||
GET /api/canvas/test
|
||||
→ Test endpoint to verify API is working
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Enable Canvas API
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Keep Backward Compatibility (During Migration)
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true,
|
||||
"layered": true,
|
||||
"basePath": "images/products/404/",
|
||||
"layers": { /* ... */ }
|
||||
}
|
||||
}
|
||||
```
|
||||
If Canvas API fails, falls back to static layered system.
|
||||
|
||||
## Testing
|
||||
|
||||
### 1. Test API is Working
|
||||
```
|
||||
Visit: http://localhost:8080/api/canvas/test
|
||||
Expected: {"status": "ok"}
|
||||
```
|
||||
|
||||
### 2. Test Specific Image
|
||||
```
|
||||
Visit: http://localhost:8080/api/canvas/404/base
|
||||
Expected: Image file loads
|
||||
```
|
||||
|
||||
### 3. Test Fallback
|
||||
```
|
||||
Visit: http://localhost:8080/api/canvas/404/hardware
|
||||
Check Flask logs to see which path was used
|
||||
```
|
||||
|
||||
### 4. Test Product Info
|
||||
```
|
||||
Visit: http://localhost:8080/api/canvas/404/info
|
||||
Expected: JSON with available layers
|
||||
```
|
||||
|
||||
### 5. Test Product Page
|
||||
```
|
||||
Visit product page
|
||||
Open browser console
|
||||
Check for Canvas API requests: GET /api/canvas/404/door?color=white
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Images not loading?
|
||||
1. Check Flask logs for which path was searched
|
||||
2. Visit `/api/canvas/<product_code>/info` to see available layers
|
||||
3. Verify `"useCanvasAPI": true` in products.json
|
||||
4. Check directory structure matches type/subtype
|
||||
|
||||
### 404 errors?
|
||||
- File doesn't exist at any fallback level
|
||||
- Check file naming (lowercase for colors)
|
||||
- Verify baseType and subType in products.json
|
||||
|
||||
### Wrong image showing?
|
||||
- More specific path overrides generic
|
||||
- Check fallback priority order
|
||||
- Verify file exists where you expect
|
||||
|
||||
## Documentation Files
|
||||
|
||||
- **[CANVAS_API_GUIDE.md](CANVAS_API_GUIDE.md)** - Complete technical guide
|
||||
- **[CANVAS_API_QUICKSTART.md](CANVAS_API_QUICKSTART.md)** - 5-minute setup
|
||||
- **[CANVAS_DIRECTORY_SETUP.md](CANVAS_DIRECTORY_SETUP.md)** - Setup scripts
|
||||
- **[FOREGROUND_LAYER_EXAMPLE.md](FOREGROUND_LAYER_EXAMPLE.md)** - Foreground guide
|
||||
- **[FOREGROUND_QUICKSTART.md](FOREGROUND_QUICKSTART.md)** - Quick foreground setup
|
||||
- **[LAYERED_IMAGES.md](LAYERED_IMAGES.md)** - Full layering system
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ **Test the API**: Visit `/api/canvas/test`
|
||||
2. ✅ **Create directory structure**: Use scripts in CANVAS_DIRECTORY_SETUP.md
|
||||
3. ✅ **Move/organize images**: Place in appropriate fallback levels
|
||||
4. ✅ **Update products.json**: Add `"useCanvasAPI": true`
|
||||
5. ✅ **Test products**: Load product pages and verify images
|
||||
6. ✅ **Optimize**: Run duplicate detection and consolidate files
|
||||
|
||||
## Summary
|
||||
|
||||
You now have a sophisticated, production-ready image serving system with:
|
||||
|
||||
✨ **Smart hierarchical fallbacks**
|
||||
✨ **Automatic image resolution**
|
||||
✨ **5-layer compositing** (including foreground from earlier)
|
||||
✨ **Massive storage savings**
|
||||
✨ **Easy maintenance**
|
||||
✨ **Flexible organization**
|
||||
✨ **Backward compatible**
|
||||
|
||||
The system is fully implemented and ready to use. Start with a few test products, verify it works, then gradually migrate your entire catalog!
|
||||
@@ -0,0 +1,197 @@
|
||||
# Quick Start: Canvas API Hierarchical Image System
|
||||
|
||||
## What Is This?
|
||||
The Canvas API lets you share images across multiple products with automatic fallback. Instead of duplicating the same plants.png file in every product folder, you can have ONE shared file that all products use.
|
||||
|
||||
## 5-Minute Setup
|
||||
|
||||
### Step 1: Organize Your Images
|
||||
|
||||
**Create this structure:**
|
||||
```
|
||||
app/static/images/
|
||||
└── doors/
|
||||
└── storm/
|
||||
├── 404/
|
||||
│ └── door-white.png # Product-specific
|
||||
├── 505/
|
||||
│ └── door-black.png # Product-specific
|
||||
└── layers/
|
||||
├── base.jpg # Shared by all storm doors
|
||||
├── hardware.png # Shared by all storm doors
|
||||
└── foreground.png # Shared by all storm doors
|
||||
```
|
||||
|
||||
### Step 2: Enable Canvas API
|
||||
|
||||
**Edit products.json:**
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"description": "#404 FALCON STORM WINDOWS",
|
||||
"colors": ["White", "Black"],
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Test
|
||||
|
||||
1. Start your Flask server
|
||||
2. Visit: `http://localhost:8080/api/canvas/test`
|
||||
3. Should see: `{"status": "ok"}`
|
||||
4. Test specific image: `http://localhost:8080/api/canvas/404/base`
|
||||
5. Open product page - images should load automatically!
|
||||
|
||||
## How It Works
|
||||
|
||||
When you request `/api/canvas/404/door?color=white`, the system searches:
|
||||
|
||||
1. `/images/doors/storm/404/door-white.png` ← **Checks here first**
|
||||
2. `/images/doors/storm/404/door.png`
|
||||
3. `/images/doors/storm/layers/door-white.png`
|
||||
4. `/images/doors/storm/layers/door.png` ← **Uses this if 404-specific doesn't exist**
|
||||
5. `/images/doors/layers/door.png`
|
||||
6. `/images/layers/door.png`
|
||||
|
||||
## Directory Level Guide
|
||||
|
||||
| Level | Path | Use For | Example |
|
||||
|-------|------|---------|---------|
|
||||
| **Product** | `/images/doors/storm/404/` | Unique to this product | Custom door colors, unique base |
|
||||
| **Subtype** | `/images/doors/storm/layers/` | Shared across storm doors | Common hardware, shared plants |
|
||||
| **Type** | `/images/doors/layers/` | Shared across all doors | Generic handles, railings |
|
||||
| **Global** | `/images/layers/` | Shared across everything | Default fallback images |
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario 1: All Products Share Plants
|
||||
**Put foreground.png here:**
|
||||
```
|
||||
/images/doors/storm/layers/foreground.png
|
||||
```
|
||||
|
||||
Both product 404 and 505 will use this same file automatically!
|
||||
|
||||
### Scenario 2: Product 404 Needs Custom Plants
|
||||
**Add product-specific version:**
|
||||
```
|
||||
/images/doors/storm/404/foreground.png ← Product 404 uses this
|
||||
/images/doors/storm/layers/foreground.png ← Product 505 uses this
|
||||
```
|
||||
|
||||
### Scenario 3: Same Hardware for All Doors
|
||||
**Put at type level:**
|
||||
```
|
||||
/images/doors/layers/hardware.png
|
||||
```
|
||||
|
||||
Every door product (storm, entry, patio) uses the same hardware!
|
||||
|
||||
## Real-World Example
|
||||
|
||||
### Before (Static Layered System)
|
||||
```
|
||||
/images/products/404/
|
||||
base.jpg (800KB)
|
||||
door-white.png (200KB)
|
||||
hardware.png (100KB) ← Duplicate
|
||||
plants.png (150KB) ← Duplicate
|
||||
|
||||
/images/products/505/
|
||||
base.jpg (800KB)
|
||||
door-black.png (200KB)
|
||||
hardware.png (100KB) ← Duplicate!
|
||||
plants.png (150KB) ← Duplicate!
|
||||
|
||||
Total: 2.5MB
|
||||
```
|
||||
|
||||
### After (Canvas API)
|
||||
```
|
||||
/images/doors/storm/404/
|
||||
base.jpg (800KB)
|
||||
door-white.png (200KB)
|
||||
|
||||
/images/doors/storm/505/
|
||||
base.jpg (800KB)
|
||||
door-black.png (200KB)
|
||||
|
||||
/images/doors/storm/layers/
|
||||
hardware.png (100KB) ← Shared!
|
||||
plants.png (150KB) ← Shared!
|
||||
|
||||
Total: 2.25MB (saved 250KB)
|
||||
```
|
||||
|
||||
With 10 products sharing the same hardware/plants, you'd save **~2MB**!
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Enable for a Product
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
```
|
||||
/api/canvas/<product_code>/<layer>?color=<color>
|
||||
/api/canvas/<product_code>/info
|
||||
/api/canvas/test
|
||||
```
|
||||
|
||||
### Valid Layers
|
||||
- `base` - Background/house
|
||||
- `door` - Door/window panel
|
||||
- `hardware` - Handles/locks
|
||||
- `overlay` - Glass views
|
||||
- `foreground` - Plants/decorations
|
||||
|
||||
### Directory Pattern
|
||||
```
|
||||
/images/<type>/<subtype>/<product_code>/<layer>-<color>.<ext>
|
||||
/images/<type>/<subtype>/layers/<layer>.<ext>
|
||||
/images/<type>/layers/<layer>.<ext>
|
||||
/images/layers/<layer>.<ext>
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Images not loading?**
|
||||
1. Check Flask logs: `[INFO] Found image: /path/to/file`
|
||||
2. Visit: `/api/canvas/YOUR-PRODUCT/info`
|
||||
3. Verify `"useCanvasAPI": true` in products.json
|
||||
4. Check baseType/subType match directory structure
|
||||
|
||||
**Wrong image showing?**
|
||||
- More specific paths override generic ones
|
||||
- Product-specific overrides subtype
|
||||
- Subtype overrides type
|
||||
- Type overrides global
|
||||
|
||||
**404 error?**
|
||||
- File doesn't exist at any fallback level
|
||||
- Check file naming (lowercase for colors)
|
||||
- Verify directory structure matches type/subtype
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Read the [full Canvas API Guide](CANVAS_API_GUIDE.md) for advanced features
|
||||
2. ✅ Review [example JSON configurations](products-layered-example.json)
|
||||
3. ✅ Check the [fallback system documentation](CANVAS_API_GUIDE.md#fallback-priority)
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Eliminate duplication** - Share common files across products
|
||||
✅ **Easier updates** - Change one file, affects all products
|
||||
✅ **Smaller total size** - Less storage and bandwidth
|
||||
✅ **Smart fallbacks** - Products automatically inherit shared elements
|
||||
✅ **Flexible organization** - Add product-specific overrides anytime
|
||||
|
||||
That's it! Start organizing your images by type/subtype and watch the magic happen. 🎨
|
||||
@@ -0,0 +1,336 @@
|
||||
# Directory Structure Helper - Canvas API
|
||||
|
||||
## Quick Directory Setup Scripts
|
||||
|
||||
### Windows PowerShell Script
|
||||
Save as `create-canvas-structure.ps1`:
|
||||
|
||||
```powershell
|
||||
# Create Canvas API directory structure
|
||||
# Run from: app/static/images/
|
||||
|
||||
param(
|
||||
[string]$BaseType = "doors",
|
||||
[string]$SubType = "storm",
|
||||
[string]$ProductCode = ""
|
||||
)
|
||||
|
||||
# Create base structure
|
||||
$basePath = "."
|
||||
|
||||
# Create type/subtype/layers directories
|
||||
New-Item -ItemType Directory -Force -Path "$basePath/$BaseType/$SubType/layers" | Out-Null
|
||||
Write-Host "✓ Created: $BaseType/$SubType/layers/"
|
||||
|
||||
# Create product-specific directory if provided
|
||||
if ($ProductCode) {
|
||||
New-Item -ItemType Directory -Force -Path "$basePath/$BaseType/$SubType/$ProductCode" | Out-Null
|
||||
Write-Host "✓ Created: $BaseType/$SubType/$ProductCode/"
|
||||
}
|
||||
|
||||
# Create type-level layers directory
|
||||
New-Item -ItemType Directory -Force -Path "$basePath/$BaseType/layers" | Out-Null
|
||||
Write-Host "✓ Created: $BaseType/layers/"
|
||||
|
||||
# Create global layers directory
|
||||
New-Item -ItemType Directory -Force -Path "$basePath/layers" | Out-Null
|
||||
Write-Host "✓ Created: layers/"
|
||||
|
||||
Write-Host "`nDirectory structure created successfully!"
|
||||
Write-Host "Now add your images to the appropriate directories."
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```powershell
|
||||
# From app/static/images/ directory
|
||||
.\create-canvas-structure.ps1 -BaseType "doors" -SubType "storm" -ProductCode "404"
|
||||
.\create-canvas-structure.ps1 -BaseType "windows" -SubType "double-hung" -ProductCode "505"
|
||||
```
|
||||
|
||||
### Linux/Mac Bash Script
|
||||
Save as `create-canvas-structure.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Create Canvas API directory structure
|
||||
# Run from: app/static/images/
|
||||
|
||||
BASE_TYPE=${1:-"doors"}
|
||||
SUB_TYPE=${2:-"storm"}
|
||||
PRODUCT_CODE=$3
|
||||
|
||||
# Create type/subtype/layers directories
|
||||
mkdir -p "$BASE_TYPE/$SUB_TYPE/layers"
|
||||
echo "✓ Created: $BASE_TYPE/$SUB_TYPE/layers/"
|
||||
|
||||
# Create product-specific directory if provided
|
||||
if [ -n "$PRODUCT_CODE" ]; then
|
||||
mkdir -p "$BASE_TYPE/$SUB_TYPE/$PRODUCT_CODE"
|
||||
echo "✓ Created: $BASE_TYPE/$SUB_TYPE/$PRODUCT_CODE/"
|
||||
fi
|
||||
|
||||
# Create type-level layers directory
|
||||
mkdir -p "$BASE_TYPE/layers"
|
||||
echo "✓ Created: $BASE_TYPE/layers/"
|
||||
|
||||
# Create global layers directory
|
||||
mkdir -p "layers"
|
||||
echo "✓ Created: layers/"
|
||||
|
||||
echo ""
|
||||
echo "Directory structure created successfully!"
|
||||
echo "Now add your images to the appropriate directories."
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# From app/static/images/ directory
|
||||
chmod +x create-canvas-structure.sh
|
||||
./create-canvas-structure.sh doors storm 404
|
||||
./create-canvas-structure.sh windows double-hung 505
|
||||
```
|
||||
|
||||
## Python Script for Bulk Setup
|
||||
Save as `setup_canvas_structure.py` in `app/`:
|
||||
|
||||
```python
|
||||
"""
|
||||
Setup Canvas API directory structure for multiple products
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
|
||||
def load_products():
|
||||
"""Load products from products.json"""
|
||||
with open('data/products.json', 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
def create_canvas_directories():
|
||||
"""Create directory structure based on product data"""
|
||||
products = load_products()
|
||||
base_path = 'static/images'
|
||||
|
||||
created_dirs = set()
|
||||
|
||||
for product in products:
|
||||
# Get product type info
|
||||
base_type = product.get('baseType', '').lower()
|
||||
subtype_obj = product.get('subType', {})
|
||||
product_code = product.get('productCode') or product.get('id')
|
||||
|
||||
if not base_type or not product_code:
|
||||
continue
|
||||
|
||||
# Extract subtype
|
||||
sub_type = None
|
||||
if isinstance(subtype_obj, dict):
|
||||
if subtype_obj.get('door'):
|
||||
sub_type = subtype_obj['door'].lower().replace(' ', '-')
|
||||
elif subtype_obj.get('window'):
|
||||
sub_type = subtype_obj['window'].lower().replace(' ', '-')
|
||||
|
||||
if not sub_type:
|
||||
continue
|
||||
|
||||
# Create directories
|
||||
dirs_to_create = [
|
||||
f"{base_path}/{base_type}/{sub_type}/layers",
|
||||
f"{base_path}/{base_type}/{sub_type}/{product_code}",
|
||||
f"{base_path}/{base_type}/layers",
|
||||
f"{base_path}/layers"
|
||||
]
|
||||
|
||||
for dir_path in dirs_to_create:
|
||||
if dir_path not in created_dirs:
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
created_dirs.add(dir_path)
|
||||
print(f"✓ Created: {dir_path}")
|
||||
|
||||
print(f"\n✅ Created {len(created_dirs)} directories")
|
||||
print("\nNext steps:")
|
||||
print("1. Move your product images to the appropriate directories")
|
||||
print("2. Add 'useCanvasAPI': true to product imageConfig")
|
||||
print("3. Test with: /api/canvas/<product_code>/info")
|
||||
|
||||
if __name__ == '__main__':
|
||||
create_canvas_directories()
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
cd app/
|
||||
python setup_canvas_structure.py
|
||||
```
|
||||
|
||||
## Manual Directory Creation Reference
|
||||
|
||||
### Complete Structure Example
|
||||
```
|
||||
app/static/images/
|
||||
├── doors/
|
||||
│ ├── storm/
|
||||
│ │ ├── 404/
|
||||
│ │ │ ├── base.jpg
|
||||
│ │ │ ├── door-white.png
|
||||
│ │ │ └── door-black.png
|
||||
│ │ ├── 505/
|
||||
│ │ │ ├── base.jpg
|
||||
│ │ │ └── door-white.png
|
||||
│ │ └── layers/
|
||||
│ │ ├── base.jpg
|
||||
│ │ ├── hardware.png
|
||||
│ │ └── foreground.png
|
||||
│ ├── entry/
|
||||
│ │ ├── 600/
|
||||
│ │ │ ├── base.jpg
|
||||
│ │ │ └── door-bronze.png
|
||||
│ │ └── layers/
|
||||
│ │ └── foreground-plants.png
|
||||
│ └── layers/
|
||||
│ └── hardware-generic.png
|
||||
├── windows/
|
||||
│ ├── storm/
|
||||
│ │ ├── 450/
|
||||
│ │ │ └── base.jpg
|
||||
│ │ └── layers/
|
||||
│ │ └── foreground-minimal.png
|
||||
│ └── layers/
|
||||
│ └── base-generic.jpg
|
||||
└── layers/
|
||||
├── foreground-default.png
|
||||
└── base-generic.jpg
|
||||
```
|
||||
|
||||
## File Naming Conventions
|
||||
|
||||
### Layer Files
|
||||
- **Base**: `base.jpg` or `base.png`
|
||||
- **Door (no color)**: `door.png`
|
||||
- **Door (with color)**: `door-white.png`, `door-black.png`, `door-bronze.png`
|
||||
- **Hardware**: `hardware.png`
|
||||
- **Foreground**: `foreground.png` or descriptive like `foreground-plants.png`
|
||||
- **Overlay**: `overlay.png` or `view-inside.png`, `view-outside.png`
|
||||
|
||||
### Tips
|
||||
- Use lowercase for color names in filenames
|
||||
- Use hyphens to separate words
|
||||
- Be descriptive for shared files: `foreground-spring-plants.png`
|
||||
- Keep product-specific files simple: `base.jpg`, `door-white.png`
|
||||
|
||||
## Migration Helper
|
||||
|
||||
### Identify Duplicate Files
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
Get-ChildItem -Path "app/static/images/products" -Recurse -File |
|
||||
Group-Object -Property Length, Name |
|
||||
Where-Object { $_.Count -gt 1 } |
|
||||
Select-Object Name, Count
|
||||
```
|
||||
|
||||
```bash
|
||||
# Linux/Mac
|
||||
find app/static/images/products -type f -exec md5sum {} + |
|
||||
sort |
|
||||
awk 'BEGIN{cmd="md5sum"}{if($1==prev){print $2}else{prev=$1}}'
|
||||
```
|
||||
|
||||
This will show you which files are duplicated and can be shared.
|
||||
|
||||
## Validation Script
|
||||
|
||||
Save as `validate_canvas_structure.py`:
|
||||
|
||||
```python
|
||||
"""
|
||||
Validate Canvas API directory structure
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
|
||||
def validate_structure():
|
||||
"""Check if directory structure matches product data"""
|
||||
with open('app/data/products.json', 'r', encoding='utf-8') as f:
|
||||
products = json.load(f)
|
||||
|
||||
issues = []
|
||||
base_path = 'app/static/images'
|
||||
|
||||
for product in products:
|
||||
if not product.get('imageConfig', {}).get('useCanvasAPI'):
|
||||
continue
|
||||
|
||||
product_code = product.get('productCode') or product.get('id')
|
||||
base_type = product.get('baseType', '').lower()
|
||||
subtype_obj = product.get('subType', {})
|
||||
|
||||
# Extract subtype
|
||||
sub_type = None
|
||||
if isinstance(subtype_obj, dict):
|
||||
if subtype_obj.get('door'):
|
||||
sub_type = subtype_obj['door'].lower().replace(' ', '-')
|
||||
elif subtype_obj.get('window'):
|
||||
sub_type = subtype_obj['window'].lower().replace(' ', '-')
|
||||
|
||||
if not all([product_code, base_type, sub_type]):
|
||||
issues.append(f"⚠️ {product_code}: Missing type/subtype info")
|
||||
continue
|
||||
|
||||
# Check if product directory exists
|
||||
product_dir = f"{base_path}/{base_type}/{sub_type}/{product_code}"
|
||||
if not os.path.exists(product_dir):
|
||||
issues.append(f"❌ {product_code}: Directory not found: {product_dir}")
|
||||
else:
|
||||
# Check for at least one image file
|
||||
files = os.listdir(product_dir)
|
||||
image_files = [f for f in files if f.endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
||||
if not image_files:
|
||||
issues.append(f"⚠️ {product_code}: No images in {product_dir}")
|
||||
else:
|
||||
print(f"✓ {product_code}: {len(image_files)} images found")
|
||||
|
||||
if issues:
|
||||
print("\n❌ Issues found:")
|
||||
for issue in issues:
|
||||
print(f" {issue}")
|
||||
else:
|
||||
print("\n✅ All Canvas API products validated successfully!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
validate_structure()
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python validate_canvas_structure.py
|
||||
```
|
||||
|
||||
## Quick Commands
|
||||
|
||||
### Create structure for all common types
|
||||
```bash
|
||||
# Doors
|
||||
mkdir -p app/static/images/doors/{storm,entry,patio,screen}/layers
|
||||
mkdir -p app/static/images/doors/layers
|
||||
|
||||
# Windows
|
||||
mkdir -p app/static/images/windows/{storm,double-hung,casement,sliding}/layers
|
||||
mkdir -p app/static/images/windows/layers
|
||||
|
||||
# Global
|
||||
mkdir -p app/static/images/layers
|
||||
```
|
||||
|
||||
### Move images from old structure
|
||||
```bash
|
||||
# Example: Move shared hardware from products to shared layers
|
||||
mv app/static/images/products/*/hardware.png app/static/images/doors/storm/layers/
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
These scripts help you quickly set up and validate the Canvas API directory structure. Choose the approach that works best for your workflow:
|
||||
|
||||
- **Manual**: Create directories as needed
|
||||
- **Scripts**: Automate creation for multiple products
|
||||
- **Migration**: Use helper scripts to identify duplicates and reorganize
|
||||
@@ -0,0 +1,142 @@
|
||||
# Foreground Layer Example
|
||||
|
||||
## Overview
|
||||
The foreground layer adds depth and realism to product images by placing decorative elements (like plants, porch furniture, or architectural details) in front of the product.
|
||||
|
||||
## Layer Stacking Order
|
||||
From back to front:
|
||||
1. **Base Layer** (z-index: 1) - Background/house/frame
|
||||
2. **Door Layer** (z-index: 2) - Door or window panel (changes with color)
|
||||
3. **Hardware Layer** (z-index: 3) - Handles, locks, hinges
|
||||
4. **Overlay Layer** (z-index: 4) - Glass views or decorative overlays
|
||||
5. **Foreground Layer** (z-index: 5) - Plants, decorative items that appear in front
|
||||
|
||||
## Creating a Foreground Layer
|
||||
|
||||
### Step 1: Prepare Your Image
|
||||
1. Take or create an image of decorative elements (plants, porch items, etc.)
|
||||
2. Remove the background completely (use Photoshop, GIMP, or Photopea)
|
||||
3. Save as PNG with transparency
|
||||
4. Ensure dimensions match your base layer image
|
||||
|
||||
### Step 2: Position Elements
|
||||
When creating your foreground layer, position elements to:
|
||||
- Frame the product naturally
|
||||
- Not obscure critical product details
|
||||
- Add depth by having some elements extend beyond product edges
|
||||
- Consider perspective and lighting consistency
|
||||
|
||||
### Step 3: Export Settings
|
||||
- Format: PNG-24
|
||||
- Transparency: Enabled
|
||||
- Color Profile: sRGB
|
||||
- Resolution: Match your base layer (typically 800-1200px width)
|
||||
|
||||
## JSON Configuration Example
|
||||
|
||||
```json
|
||||
{
|
||||
"productCode": "600",
|
||||
"description": "#600 PREMIUM ENTRY DOOR",
|
||||
"image": "images/600.jpg",
|
||||
"colors": ["White", "Black", "Bronze", "Sandstone"],
|
||||
"materials": ["Aluminum", "Vinyl"],
|
||||
"imageConfig": {
|
||||
"layered": true,
|
||||
"basePath": "images/products/600/",
|
||||
"layers": {
|
||||
"base": "house-background.jpg",
|
||||
"door": {
|
||||
"white": "door-white.png",
|
||||
"black": "door-black.png",
|
||||
"bronze": "door-bronze.png",
|
||||
"sandstone": "door-sandstone.png"
|
||||
},
|
||||
"hardware": "handle-silver.png",
|
||||
"foreground": "front-plants.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
app/images/products/600/
|
||||
├── house-background.jpg # Background layer with house/frame
|
||||
├── door-white.png # White door (transparent background)
|
||||
├── door-black.png # Black door (transparent background)
|
||||
├── door-bronze.png # Bronze door (transparent background)
|
||||
├── door-sandstone.png # Sandstone door (transparent background)
|
||||
├── handle-silver.png # Hardware layer (transparent background)
|
||||
└── front-plants.png # Foreground layer with plants (transparent background)
|
||||
```
|
||||
|
||||
## Tips for Best Results
|
||||
|
||||
### Foreground Elements to Consider
|
||||
- **Potted plants**: Add life and color to the scene
|
||||
- **Hanging baskets**: Create visual interest at different heights
|
||||
- **Porch railings**: Partial railings in foreground add depth
|
||||
- **Architectural details**: Columns, trim, or decorative elements
|
||||
- **Seasonal decorations**: Pumpkins, wreaths, holiday items (create variants)
|
||||
|
||||
### Technical Tips
|
||||
- Keep file sizes reasonable (compress PNGs without losing quality)
|
||||
- Use soft shadows on foreground elements for realism
|
||||
- Match lighting direction across all layers
|
||||
- Consider blur/depth of field on very close foreground elements
|
||||
- Test on different screen sizes to ensure elements don't obscure product
|
||||
|
||||
### Common Mistakes to Avoid
|
||||
- ❌ Foreground elements too large, obscuring product
|
||||
- ❌ Inconsistent lighting between layers
|
||||
- ❌ Mismatched image dimensions
|
||||
- ❌ Harsh edges on transparent elements
|
||||
- ❌ Too much foreground detail competing with product
|
||||
|
||||
## Advanced: Multiple Foreground Variants
|
||||
|
||||
You can create different foreground options for variety:
|
||||
|
||||
```json
|
||||
{
|
||||
"productCode": "600",
|
||||
"description": "#600 PREMIUM ENTRY DOOR",
|
||||
"imageConfig": {
|
||||
"layered": true,
|
||||
"basePath": "images/products/600/",
|
||||
"layers": {
|
||||
"base": "house-background.jpg",
|
||||
"door": {
|
||||
"white": "door-white.png",
|
||||
"black": "door-black.png"
|
||||
},
|
||||
"hardware": "handle-silver.png",
|
||||
"foreground": "plants-spring.png"
|
||||
// Could add logic to swap between plants-spring.png, plants-summer.png, etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Your Foreground Layer
|
||||
|
||||
1. Load your product page
|
||||
2. Change door colors - foreground should stay visible on top
|
||||
3. Verify no important product details are obscured
|
||||
4. Check on mobile/tablet to ensure proper scaling
|
||||
5. Test with different browsers for compatibility
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- PNG files can be large - optimize them using tools like:
|
||||
- TinyPNG (https://tinypng.com)
|
||||
- ImageOptim (https://imageoptim.com)
|
||||
- Photoshop "Export for Web"
|
||||
- Target file size: 50-200KB for foreground layer
|
||||
- Use appropriate resolution (no need for 4K if displaying at 800px)
|
||||
|
||||
## Accessibility Note
|
||||
|
||||
The foreground layer has `pointer-events: none` CSS property, meaning clicks pass through to the controls below. This ensures the decorative layer doesn't interfere with user interaction.
|
||||
@@ -0,0 +1,113 @@
|
||||
# Quick Start: Adding Foreground Layers to Your Products
|
||||
|
||||
## Overview
|
||||
Your product finder now supports foreground layers - perfect for adding plants, decorative elements, or any items that should appear in front of the product.
|
||||
|
||||
## Layer Order (back to front)
|
||||
1. **Base** (z-index: 1) - House/background
|
||||
2. **Door** (z-index: 2) - Door/window (changes with color)
|
||||
3. **Hardware** (z-index: 3) - Handles/locks
|
||||
4. **Overlay** (z-index: 4) - Glass views
|
||||
5. **Foreground** (z-index: 5) - Plants/decorative items ⭐ NEW!
|
||||
|
||||
## Quick Implementation Steps
|
||||
|
||||
### Step 1: Prepare Your Foreground Image
|
||||
1. Create/photograph decorative elements (plants, porch items, etc.)
|
||||
2. Remove background (make transparent)
|
||||
3. Save as PNG
|
||||
4. Match dimensions with your base layer image
|
||||
|
||||
### Step 2: Save File to Correct Location
|
||||
```
|
||||
app/images/products/YOUR-PRODUCT-CODE/foreground.png
|
||||
```
|
||||
|
||||
Example:
|
||||
```
|
||||
app/images/products/600/
|
||||
├── house-background.jpg
|
||||
├── door-white.png
|
||||
├── door-black.png
|
||||
├── handle-silver.png
|
||||
└── plants.png ← Your new foreground layer
|
||||
```
|
||||
|
||||
### Step 3: Update Product JSON
|
||||
Edit your product in `app/data/products.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"productCode": "600",
|
||||
"description": "Your Product Name",
|
||||
"colors": ["White", "Black", "Bronze"],
|
||||
"imageConfig": {
|
||||
"layered": true,
|
||||
"basePath": "images/products/600/",
|
||||
"layers": {
|
||||
"base": "house-background.jpg",
|
||||
"door": {
|
||||
"white": "door-white.png",
|
||||
"black": "door-black.png",
|
||||
"bronze": "door-bronze.png"
|
||||
},
|
||||
"hardware": "handle-silver.png",
|
||||
"foreground": "plants.png" ← Add this line
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Test
|
||||
1. Save changes
|
||||
2. Refresh your browser
|
||||
3. Navigate to the product
|
||||
4. The foreground layer should now appear on top of everything
|
||||
|
||||
## Tips for Best Results
|
||||
|
||||
### Good Foreground Elements
|
||||
✅ Potted plants at corners of frame
|
||||
✅ Hanging baskets to the side
|
||||
✅ Partial porch railings
|
||||
✅ Seasonal decorations (wreaths, pumpkins)
|
||||
|
||||
### Avoid
|
||||
❌ Obscuring important product details
|
||||
❌ Elements too large or too busy
|
||||
❌ Mismatched lighting/shadows
|
||||
❌ Low-quality or pixelated images
|
||||
|
||||
## Example Products
|
||||
|
||||
See these files for complete examples:
|
||||
- [information/FOREGROUND_LAYER_EXAMPLE.md](FOREGROUND_LAYER_EXAMPLE.md) - Detailed guide
|
||||
- [information/products-layered-example.json](products-layered-example.json) - Product #600 example
|
||||
- [information/LAYERED_IMAGES.md](LAYERED_IMAGES.md) - Full layered system guide
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Foreground not showing?**
|
||||
- Check file path in JSON matches actual file location
|
||||
- Ensure `"layered": true` is set
|
||||
- Verify PNG has transparency
|
||||
- Check browser console for 404 errors
|
||||
|
||||
**Foreground blocking clicks?**
|
||||
- This shouldn't happen - the layer has `pointer-events: none`
|
||||
- If it does, check CSS for `.layer-foreground`
|
||||
|
||||
**Image quality issues?**
|
||||
- Use PNG-24 format
|
||||
- Match base layer dimensions
|
||||
- Optimize file size with TinyPNG or similar
|
||||
|
||||
## File Sizes & Performance
|
||||
- Target: 50-200KB for foreground PNG
|
||||
- Optimize images before uploading
|
||||
- Browser caches layers after first load
|
||||
|
||||
## Need Help?
|
||||
Check the detailed documentation:
|
||||
- `information/FOREGROUND_LAYER_EXAMPLE.md` - Complete guide with tips
|
||||
- `information/LAYERED_IMAGES.md` - Full layered system documentation
|
||||
@@ -11,6 +11,7 @@ Images are stacked in layers (like Photoshop layers):
|
||||
2. **Door Layer** - The door panel (PNG with transparency) - changes with color selection
|
||||
3. **Hardware Layer** - Handle/lock set (PNG with transparency) - can flip for left/right hinge
|
||||
4. **Overlay Layer** - Glass view/decorative elements (PNG with transparency) - optional
|
||||
5. **Foreground Layer** - Plants, decorative items that appear in front (PNG with transparency) - optional
|
||||
|
||||
### Fallback Behavior
|
||||
- **No layered config**: Shows the standard flat `image` field
|
||||
@@ -83,6 +84,32 @@ Images are stacked in layers (like Photoshop layers):
|
||||
}
|
||||
```
|
||||
|
||||
### Product with Foreground Layer (Plants/Decorative Elements)
|
||||
```json
|
||||
{
|
||||
"productCode": "600",
|
||||
"description": "#600 PREMIUM ENTRY DOOR",
|
||||
"image": "images/600.jpg",
|
||||
"colors": ["White", "Black", "Bronze"],
|
||||
"materials": ["Aluminum"],
|
||||
"imageConfig": {
|
||||
"layered": true,
|
||||
"basePath": "images/products/600/",
|
||||
"layers": {
|
||||
"base": "base.jpg",
|
||||
"door": {
|
||||
"white": "door-white.png",
|
||||
"black": "door-black.png",
|
||||
"bronze": "door-bronze.png"
|
||||
},
|
||||
"hardware": "handle.png",
|
||||
"foreground": "plants.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
**Note:** The foreground layer appears on top of all other layers and is perfect for adding plants, decorative elements, or other items that should appear in front of the product.
|
||||
|
||||
## File Structure
|
||||
|
||||
### Recommended Directory Layout
|
||||
@@ -97,6 +124,13 @@ app/images/products/
|
||||
│ ├── handle.png # Hardware (transparent BG)
|
||||
│ ├── view-inside.png # Optional inside view overlay
|
||||
│ └── view-outside.png # Optional outside view overlay
|
||||
├── 600/
|
||||
│ ├── base.jpg # Frame/house background
|
||||
│ ├── door-white.png # White door panel (transparent BG)
|
||||
│ ├── door-black.png # Black door panel (transparent BG)
|
||||
│ ├── door-bronze.png # Bronze door panel (transparent BG)
|
||||
│ ├── handle.png # Hardware (transparent BG)
|
||||
│ └── plants.png # Foreground layer - plants/decorative (transparent BG)
|
||||
├── 450/
|
||||
│ ├── base.jpg
|
||||
│ └── door-white.png
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
# Layer Transform System
|
||||
|
||||
## Overview
|
||||
|
||||
The Canvas API layer system supports CSS transforms for individual layers, allowing you to flip or rotate specific layers without modifying the image files. This is particularly useful for:
|
||||
|
||||
- **Door Hinge Direction**: Flip the door layer horizontally to show left-hinge vs right-hinge
|
||||
- **Mirror Effects**: Create symmetric variations of products
|
||||
- **Multi-Configuration Support**: Use the same image assets for multiple product variations
|
||||
|
||||
## Configuration
|
||||
|
||||
Add transform configuration to your product's `imageConfig` object:
|
||||
|
||||
```json
|
||||
{
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true,
|
||||
"layerTransforms": {
|
||||
"door": "flip-horizontal",
|
||||
"hardware": "flip-horizontal"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available Transforms
|
||||
|
||||
### `flip-horizontal`
|
||||
Flips the layer horizontally (left-right mirror).
|
||||
|
||||
**CSS Applied**: `transform: scaleX(-1)`
|
||||
|
||||
**Use Case**: Door hinge direction (left-hinge vs right-hinge)
|
||||
|
||||
```json
|
||||
"layerTransforms": {
|
||||
"door": "flip-horizontal"
|
||||
}
|
||||
```
|
||||
|
||||
### `flip-vertical`
|
||||
Flips the layer vertically (top-bottom mirror).
|
||||
|
||||
**CSS Applied**: `transform: scaleY(-1)`
|
||||
|
||||
**Use Case**: Ceiling-mounted vs floor-mounted products
|
||||
|
||||
```json
|
||||
"layerTransforms": {
|
||||
"base": "flip-vertical"
|
||||
}
|
||||
```
|
||||
|
||||
### `flip-both`
|
||||
Flips the layer both horizontally and vertically (180° rotation).
|
||||
|
||||
**CSS Applied**: `transform: scale(-1, -1)`
|
||||
|
||||
**Use Case**: Complete inversion of a layer
|
||||
|
||||
```json
|
||||
"layerTransforms": {
|
||||
"overlay": "flip-both"
|
||||
}
|
||||
```
|
||||
|
||||
## Layer Names
|
||||
|
||||
Available layers you can transform:
|
||||
- `base` - Background/house layer
|
||||
- `door` - Door/window layer (with color variations)
|
||||
- `hardware` - Hardware layer (handles, locks, etc.)
|
||||
- `overlay` - Additional overlay layer
|
||||
- `foreground` - Foreground layer (plants, decorations)
|
||||
|
||||
## Complete Example
|
||||
|
||||
### Left-Hinge Door (Default)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "404",
|
||||
"productCode": "404",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Right-Hinge Door (Flipped)
|
||||
|
||||
Create a separate product entry with a different code:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "404R",
|
||||
"productCode": "404R",
|
||||
"description": "#404 FALCON STORM WINDOWS (RIGHT-HINGE)",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true,
|
||||
"layerTransforms": {
|
||||
"door": "flip-horizontal",
|
||||
"hardware": "flip-horizontal"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Important**: Both products can share the same image files! The transform is applied in CSS at render time.
|
||||
|
||||
## Image Preparation Tips
|
||||
|
||||
When creating images that will be flipped:
|
||||
|
||||
1. **Design for the default orientation first** (e.g., left-hinge door)
|
||||
2. **Keep text/logos off flippable layers** - they will be reversed
|
||||
3. **Test the flipped version** to ensure it looks natural
|
||||
4. **Consider asymmetric details** - handles, hinges, decorative elements
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### CSS Classes
|
||||
|
||||
The system applies these CSS classes automatically:
|
||||
|
||||
- `.layer-flip-horizontal` - Horizontal flip
|
||||
- `.layer-flip-vertical` - Vertical flip
|
||||
- `.layer-flip-both` - Both axes flip
|
||||
|
||||
### JavaScript
|
||||
|
||||
Transforms are applied in `updateCanvasAPIPreview()` function:
|
||||
|
||||
```javascript
|
||||
const layerTransforms = product.imageConfig?.layerTransforms || {};
|
||||
|
||||
function applyTransform(element, layerName) {
|
||||
element.classList.remove('layer-flip-horizontal', 'layer-flip-vertical', 'layer-flip-both');
|
||||
const transform = layerTransforms[layerName];
|
||||
if (transform) {
|
||||
element.classList.add(`layer-${transform}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced: Multiple Products, Same Images
|
||||
|
||||
You can create an entire product family from a single set of images:
|
||||
|
||||
```
|
||||
/static/images/window/storm-window/404/
|
||||
├── door-white.png (left-hinge design)
|
||||
├── door-black.png (left-hinge design)
|
||||
└── ...
|
||||
|
||||
Products using these images:
|
||||
- 404 - Left-hinge (no transform)
|
||||
- 404R - Right-hinge (door flipped)
|
||||
- 404T - Top-mount variant (base flipped)
|
||||
- 404RT - Right-hinge top-mount (both flipped)
|
||||
```
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
CSS transforms are supported in all modern browsers:
|
||||
- Chrome/Edge: ✅
|
||||
- Firefox: ✅
|
||||
- Safari: ✅
|
||||
- Opera: ✅
|
||||
|
||||
## Performance
|
||||
|
||||
Layer transforms are GPU-accelerated CSS operations with no performance impact. Flipping layers is instant and doesn't require:
|
||||
- Additional HTTP requests
|
||||
- Image processing
|
||||
- Additional storage
|
||||
- Server-side rendering
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Transform not applying
|
||||
|
||||
1. Check that `useCanvasAPI: true` is set
|
||||
2. Verify layer name matches exactly (case-sensitive)
|
||||
3. Check browser console for JavaScript errors
|
||||
4. Ensure CSS is loaded properly
|
||||
|
||||
### Image looks distorted
|
||||
|
||||
Transforms maintain aspect ratio. If the image looks wrong:
|
||||
1. Verify the original image has correct proportions
|
||||
2. Check that all layers use the same canvas dimensions
|
||||
3. Test without transforms first to isolate the issue
|
||||
|
||||
### Text is backwards
|
||||
|
||||
This is expected! Don't place text or logos on layers that will be flipped. Instead:
|
||||
1. Keep text on non-flipped layers (usually `base` or `overlay`)
|
||||
2. Create separate images for left/right variants if text is essential
|
||||
3. Use the overlay layer for directional text
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Possible additions:
|
||||
- Rotation angles (90°, 180°, 270°)
|
||||
- Scale adjustments (zoom in/out specific layers)
|
||||
- Position offsets (shift layers left/right/up/down)
|
||||
- Animation/transition effects
|
||||
@@ -38,6 +38,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "600",
|
||||
"productCode": "600",
|
||||
"category": "DOORS",
|
||||
"description": "#600 PREMIUM ENTRY DOOR WITH PLANTS",
|
||||
"discontinued": false,
|
||||
"location": "Iola",
|
||||
"baseType": "Door",
|
||||
"subType": {
|
||||
"door": "Entry Door",
|
||||
"window": null
|
||||
},
|
||||
"materials": [
|
||||
"Aluminum"
|
||||
],
|
||||
"colors": [
|
||||
"White",
|
||||
"Black",
|
||||
"Bronze",
|
||||
"Sandstone"
|
||||
],
|
||||
"isAccessory": false,
|
||||
"compatibleAccessories": [],
|
||||
"image": "images/600.jpg",
|
||||
"imageConfig": {
|
||||
"layered": true,
|
||||
"basePath": "images/products/600/",
|
||||
"layers": {
|
||||
"base": "house-background.jpg",
|
||||
"door": {
|
||||
"white": "door-white.png",
|
||||
"black": "door-black.png",
|
||||
"bronze": "door-bronze.png",
|
||||
"sandstone": "door-sandstone.png"
|
||||
},
|
||||
"hardware": "handle-silver.png",
|
||||
"foreground": "plants.png"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "450",
|
||||
"productCode": "450",
|
||||
@@ -102,5 +142,34 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "700",
|
||||
"productCode": "700",
|
||||
"category": "DOORS",
|
||||
"description": "#700 MODERN ENTRY DOOR (Canvas API Example)",
|
||||
"discontinued": false,
|
||||
"location": "Iola",
|
||||
"baseType": "Door",
|
||||
"subType": {
|
||||
"door": "Entry Door",
|
||||
"window": null
|
||||
},
|
||||
"materials": [
|
||||
"Aluminum",
|
||||
"Vinyl"
|
||||
],
|
||||
"colors": [
|
||||
"White",
|
||||
"Black",
|
||||
"Bronze"
|
||||
],
|
||||
"isAccessory": false,
|
||||
"compatibleAccessories": [],
|
||||
"image": "images/700.jpg",
|
||||
"imageConfig": {
|
||||
"useCanvasAPI": true
|
||||
},
|
||||
"_comment": "Canvas API Example - Images will be automatically loaded from hierarchical fallback system. Product-specific images go in /images/doors/entry/700/, shared images go in /images/doors/entry/layers/, /images/doors/layers/, or /images/layers/"
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user