151
src/WaveForm.jsx
151
src/WaveForm.jsx
@@ -12,30 +12,32 @@ const BLUE_SHADES = [
|
||||
const animateFallbackBars = (canvas, ctx, fallbackData) => {
|
||||
const HEIGHT = canvas.height;
|
||||
const barCount = 64; // Increased for more detail
|
||||
const barWidth = Math.max(6, Math.floor(canvas.width / barCount)); // Minimum 6px width
|
||||
const barWidth = Math.max(8, Math.floor(canvas.width / barCount)); // Minimum 8px width for visibility
|
||||
const centerY = HEIGHT / 2; // Center point for bars
|
||||
let x = 0;
|
||||
|
||||
for (let i = 0; i < barCount; i++) {
|
||||
// Make bars taller and more visible
|
||||
const barHeight = Math.max(20, fallbackData[i] * HEIGHT * 0.4); // Minimum 20px height, use 40% of screen
|
||||
const blueShade = Math.min(3, Math.floor(fallbackData[i] * 4));
|
||||
console.log('Drawing', barCount, 'bars with data:', fallbackData.slice(0, 5));
|
||||
|
||||
for (let i = 0; i < barCount && i < fallbackData.length; i++) {
|
||||
// Make bars much taller and more visible
|
||||
const normalizedHeight = Math.max(0.1, Math.min(1, fallbackData[i]));
|
||||
const barHeight = Math.max(30, normalizedHeight * HEIGHT * 0.6); // Minimum 30px height, use 60% of screen
|
||||
const blueShade = Math.min(3, Math.floor(normalizedHeight * 4));
|
||||
|
||||
// Create gradient for better visibility
|
||||
const gradient = ctx.createLinearGradient(0, centerY - barHeight/2, 0, centerY + barHeight/2);
|
||||
gradient.addColorStop(0, BLUE_SHADES[0]);
|
||||
gradient.addColorStop(0.5, BLUE_SHADES[blueShade] || BLUE_SHADES[0]);
|
||||
gradient.addColorStop(1, BLUE_SHADES[3]);
|
||||
// Use solid bright colors instead of gradients for better Tizen visibility
|
||||
ctx.fillStyle = BLUE_SHADES[blueShade] || BLUE_SHADES[0];
|
||||
|
||||
ctx.fillStyle = gradient;
|
||||
// Draw bars from center, extending up and down
|
||||
ctx.fillRect(x + 2, centerY - barHeight/2, barWidth - 4, barHeight);
|
||||
const barY = centerY - barHeight/2;
|
||||
const barX = x + 2;
|
||||
const barW = barWidth - 4;
|
||||
|
||||
// Add glow effect
|
||||
ctx.shadowColor = "rgba(255,255,255,0.8)";
|
||||
ctx.shadowBlur = 5;
|
||||
ctx.fillRect(x + 2, centerY - barHeight/2, barWidth - 4, barHeight);
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.fillRect(barX, barY, barW, barHeight);
|
||||
|
||||
// Add strong white outline for maximum visibility
|
||||
ctx.strokeStyle = "rgba(255,255,255,1)";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(barX, barY, barW, barHeight);
|
||||
|
||||
x += barWidth;
|
||||
}
|
||||
@@ -86,14 +88,16 @@ const animateBars = (analyser, canvas, ctx, dataArray, bufferLength) => {
|
||||
const WaveForm = ({ analyzerData }) => {
|
||||
const canvasRef = useRef(null);
|
||||
const [width, height] = useSize();
|
||||
const [useFallback, setUseFallback] = useState(false);
|
||||
const [useFallback, setUseFallback] = useState(true); // Start with fallback immediately
|
||||
const [fallbackData, setFallbackData] = useState([]);
|
||||
const fallbackIntervalRef = useRef(null);
|
||||
const animationRef = useRef(null);
|
||||
|
||||
// Initialize fallback data
|
||||
useEffect(() => {
|
||||
const initialData = Array(64).fill(0).map(() => Math.random() * 0.7 + 0.3); // Higher initial values, more bars
|
||||
const initialData = Array(64).fill(0).map(() => Math.random() * 0.8 + 0.2); // Higher initial values, more bars
|
||||
setFallbackData(initialData);
|
||||
console.log('WaveForm initialized with fallback data');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -103,7 +107,8 @@ const WaveForm = ({ analyzerData }) => {
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
let animationId;
|
||||
console.log('Canvas initialized:', canvas.width, 'x', canvas.height);
|
||||
|
||||
let webAudioWorking = true;
|
||||
|
||||
// Check if we have valid analyzer data
|
||||
@@ -112,6 +117,8 @@ const WaveForm = ({ analyzerData }) => {
|
||||
analyzerData.dataArray &&
|
||||
analyzerData.bufferLength;
|
||||
|
||||
console.log('Has valid analyzer:', hasValidAnalyzer);
|
||||
|
||||
const render = () => {
|
||||
try {
|
||||
// Clear canvas completely (transparent background)
|
||||
@@ -122,6 +129,7 @@ const WaveForm = ({ analyzerData }) => {
|
||||
|
||||
if (hasValidAnalyzer && webAudioWorking && !useFallback) {
|
||||
// Try Web Audio API first
|
||||
console.log('Using Web Audio API');
|
||||
const success = animateBars(
|
||||
analyzerData.analyzer,
|
||||
canvas,
|
||||
@@ -133,6 +141,7 @@ const WaveForm = ({ analyzerData }) => {
|
||||
if (!success) {
|
||||
webAudioWorking = false;
|
||||
setUseFallback(true);
|
||||
console.log('Web Audio failed, switching to fallback');
|
||||
}
|
||||
} else {
|
||||
// Use fallback animation
|
||||
@@ -140,65 +149,85 @@ const WaveForm = ({ analyzerData }) => {
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
animationId = requestAnimationFrame(render);
|
||||
animationRef.current = requestAnimationFrame(render);
|
||||
} catch (error) {
|
||||
console.warn('Canvas rendering error:', error);
|
||||
setUseFallback(true);
|
||||
animationId = requestAnimationFrame(render);
|
||||
animationRef.current = requestAnimationFrame(render);
|
||||
}
|
||||
};
|
||||
|
||||
// Always start fallback animation for immediate visibility
|
||||
// Start animation data updates
|
||||
console.log('Starting animation updates');
|
||||
fallbackIntervalRef.current = setInterval(() => {
|
||||
setFallbackData(prevData =>
|
||||
prevData.map((val, index) => {
|
||||
// Create wave-like pattern with more dramatic changes
|
||||
const wave = Math.sin((Date.now() * 0.005) + (index * 0.2)) * 0.3;
|
||||
const target = Math.random() * 0.6 + 0.4 + wave; // Higher amplitude with wave effect
|
||||
const smoothing = 0.3; // Faster transitions
|
||||
return Math.max(0.2, Math.min(1, val + (target - val) * smoothing));
|
||||
})
|
||||
);
|
||||
}, 50); // Much faster updates for smoother animation
|
||||
setFallbackData(prevData => {
|
||||
const newData = prevData.map((val, index) => {
|
||||
// Create more dramatic wave pattern
|
||||
const time = Date.now() * 0.008; // Slower for more visible movement
|
||||
const wave = Math.sin(time + (index * 0.3)) * 0.4; // Bigger wave amplitude
|
||||
const randomness = (Math.random() - 0.5) * 0.3;
|
||||
const target = 0.5 + wave + randomness; // Base of 0.5 + wave + randomness
|
||||
const smoothing = 0.15; // Smoother transitions
|
||||
return Math.max(0.1, Math.min(1, val + (target - val) * smoothing));
|
||||
});
|
||||
return newData;
|
||||
});
|
||||
}, 60); // 60ms updates for smooth motion
|
||||
|
||||
render();
|
||||
|
||||
return () => {
|
||||
if (animationId) {
|
||||
cancelAnimationFrame(animationId);
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
}
|
||||
if (fallbackIntervalRef.current) {
|
||||
clearInterval(fallbackIntervalRef.current);
|
||||
}
|
||||
};
|
||||
}, [analyzerData, width, height, useFallback, fallbackData]);
|
||||
}, [analyzerData, width, height, fallbackData]);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
style={{
|
||||
position: "absolute", // Changed back to absolute
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%", // Use percentage instead of viewport units
|
||||
height: "100%",
|
||||
zIndex: -1, // Put behind other content
|
||||
// Tizen TV optimizations
|
||||
willChange: "auto",
|
||||
imageRendering: "auto",
|
||||
// Ensure complete transparency
|
||||
backgroundColor: "transparent",
|
||||
// Prevent touch/click interference
|
||||
pointerEvents: "none",
|
||||
// Ensure it doesn't interfere with layout
|
||||
display: "block"
|
||||
}}
|
||||
ref={canvasRef}
|
||||
width={width || window.innerWidth} // Fallback dimensions
|
||||
height={height || window.innerHeight}
|
||||
// Add accessibility for Smart TV navigation
|
||||
role="img"
|
||||
aria-label={useFallback ? "Audio visualization (compatibility mode)" : "Real-time audio visualization"}
|
||||
/>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<canvas
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
zIndex: -1,
|
||||
willChange: "auto",
|
||||
imageRendering: "auto",
|
||||
backgroundColor: "transparent",
|
||||
pointerEvents: "none",
|
||||
display: "block"
|
||||
}}
|
||||
ref={canvasRef}
|
||||
width={width || window.innerWidth}
|
||||
height={height || window.innerHeight}
|
||||
role="img"
|
||||
aria-label={useFallback ? "Audio visualization (compatibility mode)" : "Real-time audio visualization"}
|
||||
/>
|
||||
|
||||
{/* Debug info - remove this in production */}
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: '10px',
|
||||
right: '10px',
|
||||
background: 'rgba(0,0,0,0.7)',
|
||||
color: 'white',
|
||||
padding: '10px',
|
||||
borderRadius: '5px',
|
||||
fontSize: '12px',
|
||||
zIndex: 1000,
|
||||
fontFamily: 'monospace'
|
||||
}}>
|
||||
<div>Canvas: {width}x{height}</div>
|
||||
<div>Fallback: {useFallback ? 'YES' : 'NO'}</div>
|
||||
<div>Data: {fallbackData.length} bars</div>
|
||||
<div>Sample: {fallbackData.slice(0,3).map(v => v.toFixed(2)).join(', ')}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Referencia en una nueva incidencia
Block a user