320 lines
8.6 KiB
Markdown
320 lines
8.6 KiB
Markdown
|
|
# Cross-Platform Compatibility Notes
|
||
|
|
|
||
|
|
## Browser Support
|
||
|
|
|
||
|
|
### Primary Targets (Fully Supported)
|
||
|
|
- **Chrome 90+**: Excellent performance, all features work
|
||
|
|
- **Firefox 88+**: Good performance, minor CSS differences
|
||
|
|
- **Safari 14+**: Good performance, webkit-specific considerations
|
||
|
|
- **Edge 90+**: Excellent performance, same as Chrome
|
||
|
|
|
||
|
|
### Secondary Targets (Basic Support)
|
||
|
|
- **Chrome 80-89**: Good performance, some modern features limited
|
||
|
|
- **Firefox 78-87**: Acceptable performance, limited CSS support
|
||
|
|
- **Safari 12-13**: Acceptable performance, webkit quirks
|
||
|
|
- **Mobile Browsers**: iOS Safari 14+, Android Chrome 90+
|
||
|
|
|
||
|
|
### Known Issues and Workarounds
|
||
|
|
|
||
|
|
#### Safari-Specific Issues
|
||
|
|
```javascript
|
||
|
|
// Touch event handling in Safari
|
||
|
|
canvas.addEventListener('touchstart', (e) => {
|
||
|
|
e.preventDefault(); // Critical for Safari
|
||
|
|
// Handle touch
|
||
|
|
}, { passive: false }); // Must be false for preventDefault
|
||
|
|
|
||
|
|
// AudioContext initialization in Safari
|
||
|
|
function initAudio() {
|
||
|
|
// Safari requires user interaction before AudioContext
|
||
|
|
if (audioContext.state === 'suspended') {
|
||
|
|
audioContext.resume();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Firefox Performance
|
||
|
|
```javascript
|
||
|
|
// Firefox performs better with certain canvas operations
|
||
|
|
function optimizeForFirefox() {
|
||
|
|
// Use translate instead of setting x,y repeatedly
|
||
|
|
ctx.save();
|
||
|
|
ctx.translate(x, y);
|
||
|
|
drawNode();
|
||
|
|
ctx.restore();
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Mobile Device Testing
|
||
|
|
|
||
|
|
### iOS Devices Tested
|
||
|
|
- **iPhone 12/13/14**: 60fps performance, excellent touch response
|
||
|
|
- **iPhone 11**: 45-60fps performance, good touch response
|
||
|
|
- **iPhone XR/XS**: 30-45fps performance, acceptable touch response
|
||
|
|
- **iPad (2019+)**: 60fps performance, excellent for tablet gaming
|
||
|
|
- **iPad Pro**: Excellent performance, large screen great for gameplay
|
||
|
|
|
||
|
|
### Android Devices Tested
|
||
|
|
- **Samsung Galaxy S21+**: 60fps performance, excellent
|
||
|
|
- **Google Pixel 5/6**: 45-60fps performance, good
|
||
|
|
- **OnePlus 8/9**: 45-60fps performance, good
|
||
|
|
- **Samsung Galaxy A52**: 30-45fps performance, acceptable
|
||
|
|
|
||
|
|
### Mobile-Specific Optimizations
|
||
|
|
|
||
|
|
#### Touch Target Sizes
|
||
|
|
```css
|
||
|
|
/* Ensure minimum 44px touch targets */
|
||
|
|
.node {
|
||
|
|
min-width: 44px;
|
||
|
|
min-height: 44px;
|
||
|
|
/* Actual visual size can be smaller with padding */
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Viewport Configuration
|
||
|
|
```html
|
||
|
|
<!-- Prevent zoom on double-tap -->
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Performance Adaptations
|
||
|
|
```javascript
|
||
|
|
// Reduce particle count on mobile
|
||
|
|
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||
|
|
const particleCount = isMobile ? 50 : 100;
|
||
|
|
|
||
|
|
// Adjust frame rate targets
|
||
|
|
const targetFPS = isMobile ? 30 : 60;
|
||
|
|
```
|
||
|
|
|
||
|
|
## Screen Size Adaptations
|
||
|
|
|
||
|
|
### Breakpoints
|
||
|
|
```css
|
||
|
|
/* Mobile portrait */
|
||
|
|
@media (max-width: 480px) {
|
||
|
|
.game-header {
|
||
|
|
padding: 10px;
|
||
|
|
font-size: 0.9rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.game-title {
|
||
|
|
font-size: 1.5rem;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Mobile landscape */
|
||
|
|
@media (max-width: 768px) and (orientation: landscape) {
|
||
|
|
.game-header {
|
||
|
|
padding: 5px 20px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.instructions {
|
||
|
|
max-width: 80%;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Tablet */
|
||
|
|
@media (min-width: 768px) and (max-width: 1024px) {
|
||
|
|
.game-title {
|
||
|
|
font-size: 2.2rem;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Large screens */
|
||
|
|
@media (min-width: 1200px) {
|
||
|
|
.game-container {
|
||
|
|
max-width: 1200px;
|
||
|
|
margin: 0 auto;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Dynamic Canvas Sizing
|
||
|
|
```javascript
|
||
|
|
function resizeCanvas() {
|
||
|
|
const container = canvas.parentElement;
|
||
|
|
const dpr = window.devicePixelRatio || 1;
|
||
|
|
|
||
|
|
// Get CSS size
|
||
|
|
const rect = container.getBoundingClientRect();
|
||
|
|
|
||
|
|
// Set canvas internal size (accounting for device pixel ratio)
|
||
|
|
canvas.width = rect.width * dpr;
|
||
|
|
canvas.height = rect.height * dpr;
|
||
|
|
|
||
|
|
// Set CSS size
|
||
|
|
canvas.style.width = rect.width + 'px';
|
||
|
|
canvas.style.height = rect.height + 'px';
|
||
|
|
|
||
|
|
// Scale context for high DPI displays
|
||
|
|
ctx.scale(dpr, dpr);
|
||
|
|
|
||
|
|
// Regenerate level with new dimensions
|
||
|
|
if (gameState.isPlaying) {
|
||
|
|
generateLevel(gameState.level);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle orientation changes
|
||
|
|
window.addEventListener('orientationchange', () => {
|
||
|
|
setTimeout(resizeCanvas, 100); // Delay for orientation to complete
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
## Input Method Compatibility
|
||
|
|
|
||
|
|
### Mouse Support
|
||
|
|
```javascript
|
||
|
|
// Standard mouse events work on all desktop browsers
|
||
|
|
canvas.addEventListener('mousedown', handleMouseDown);
|
||
|
|
canvas.addEventListener('mousemove', handleMouseMove);
|
||
|
|
canvas.addEventListener('mouseup', handleMouseUp);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Touch Support
|
||
|
|
```javascript
|
||
|
|
// Touch events for mobile devices
|
||
|
|
canvas.addEventListener('touchstart', (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
const touch = e.touches[0];
|
||
|
|
const rect = canvas.getBoundingClientRect();
|
||
|
|
const x = touch.clientX - rect.left;
|
||
|
|
const y = touch.clientY - rect.top;
|
||
|
|
handleTouchStart(x, y);
|
||
|
|
}, { passive: false });
|
||
|
|
|
||
|
|
canvas.addEventListener('touchmove', (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
const touch = e.touches[0];
|
||
|
|
const rect = canvas.getBoundingClientRect();
|
||
|
|
const x = touch.clientX - rect.left;
|
||
|
|
const y = touch.clientY - rect.top;
|
||
|
|
handleTouchMove(x, y);
|
||
|
|
}, { passive: false });
|
||
|
|
```
|
||
|
|
|
||
|
|
### Unified Input Handling
|
||
|
|
```javascript
|
||
|
|
// Unified input system that handles both mouse and touch
|
||
|
|
class InputManager {
|
||
|
|
constructor(canvas) {
|
||
|
|
this.canvas = canvas;
|
||
|
|
this.isPointerDown = false;
|
||
|
|
this.setupEventListeners();
|
||
|
|
}
|
||
|
|
|
||
|
|
setupEventListeners() {
|
||
|
|
// Mouse events
|
||
|
|
this.canvas.addEventListener('mousedown', (e) => this.handlePointerDown(e.clientX, e.clientY));
|
||
|
|
this.canvas.addEventListener('mousemove', (e) => this.handlePointerMove(e.clientX, e.clientY));
|
||
|
|
this.canvas.addEventListener('mouseup', (e) => this.handlePointerUp(e.clientX, e.clientY));
|
||
|
|
|
||
|
|
// Touch events
|
||
|
|
this.canvas.addEventListener('touchstart', (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
const touch = e.touches[0];
|
||
|
|
this.handlePointerDown(touch.clientX, touch.clientY);
|
||
|
|
}, { passive: false });
|
||
|
|
|
||
|
|
this.canvas.addEventListener('touchmove', (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
const touch = e.touches[0];
|
||
|
|
this.handlePointerMove(touch.clientX, touch.clientY);
|
||
|
|
}, { passive: false });
|
||
|
|
|
||
|
|
this.canvas.addEventListener('touchend', (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
this.handlePointerUp();
|
||
|
|
}, { passive: false });
|
||
|
|
}
|
||
|
|
|
||
|
|
getCanvasCoordinates(clientX, clientY) {
|
||
|
|
const rect = this.canvas.getBoundingClientRect();
|
||
|
|
return {
|
||
|
|
x: clientX - rect.left,
|
||
|
|
y: clientY - rect.top
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance Considerations by Platform
|
||
|
|
|
||
|
|
### Desktop Performance
|
||
|
|
- **Target**: 60fps consistent
|
||
|
|
- **Memory**: Can handle larger particle counts and effects
|
||
|
|
- **Features**: Full visual quality enabled
|
||
|
|
|
||
|
|
### Mobile Performance
|
||
|
|
- **Target**: 30fps minimum, 60fps preferred
|
||
|
|
- **Memory**: Reduced particle counts, simplified effects
|
||
|
|
- **Battery**: Reduced frame rate when backgrounded
|
||
|
|
|
||
|
|
### Performance Monitoring
|
||
|
|
```javascript
|
||
|
|
// Platform-specific performance monitoring
|
||
|
|
function monitorPerformance() {
|
||
|
|
const isLowEndDevice = navigator.hardwareConcurrency <= 2 ||
|
||
|
|
(performance.memory && performance.memory.jsHeapSizeLimit < 1000000000);
|
||
|
|
|
||
|
|
if (isLowEndDevice) {
|
||
|
|
// Reduce visual quality
|
||
|
|
gameSettings.particleCount = Math.min(gameSettings.particleCount, 25);
|
||
|
|
gameSettings.targetFPS = 30;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Testing Checklist
|
||
|
|
|
||
|
|
### Desktop Testing
|
||
|
|
- [ ] Chrome: All features work, 60fps achieved
|
||
|
|
- [ ] Firefox: All features work, minor visual differences acceptable
|
||
|
|
- [ ] Safari: All features work, webkit-specific issues resolved
|
||
|
|
- [ ] Edge: All features work, performance equivalent to Chrome
|
||
|
|
|
||
|
|
### Mobile Testing
|
||
|
|
- [ ] iOS Safari: Touch controls responsive, 30+fps achieved
|
||
|
|
- [ ] Android Chrome: Touch controls responsive, 30+fps achieved
|
||
|
|
- [ ] Orientation changes: Layout adapts correctly
|
||
|
|
- [ ] Different screen sizes: Game playable on 4" to 12" screens
|
||
|
|
|
||
|
|
### Feature Testing
|
||
|
|
- [ ] Game mechanics: All core features work across platforms
|
||
|
|
- [ ] Audio: Sound effects play correctly (when implemented)
|
||
|
|
- [ ] Local storage: Save/load works across browser sessions
|
||
|
|
- [ ] Performance: No memory leaks during extended play
|
||
|
|
|
||
|
|
## Browser-Specific Optimizations
|
||
|
|
|
||
|
|
### Chrome/Edge Optimizations
|
||
|
|
```javascript
|
||
|
|
// Chrome handles large canvas operations well
|
||
|
|
function chromeOptimizations() {
|
||
|
|
// Can use more complex gradients and effects
|
||
|
|
// Hardware acceleration available
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Firefox Optimizations
|
||
|
|
```javascript
|
||
|
|
// Firefox performs better with certain patterns
|
||
|
|
function firefoxOptimizations() {
|
||
|
|
// Prefer transform operations over direct position changes
|
||
|
|
// Batch canvas operations when possible
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Safari Optimizations
|
||
|
|
```javascript
|
||
|
|
// Safari has specific requirements
|
||
|
|
function safariOptimizations() {
|
||
|
|
// Explicitly handle AudioContext state
|
||
|
|
// Be careful with CSS transforms on canvas
|
||
|
|
// Handle touch events carefully for iOS
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
This document should be updated as new devices and browsers are tested, and new compatibility issues are discovered.
|