Animation Added and Gif file updated
This commit is contained in:
110
app/components/canvas/DNAHelix.tsx
Normal file
110
app/components/canvas/DNAHelix.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef } from "react";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DNAHelix: React.FC<Props> = ({ className }) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return;
|
||||
|
||||
// Dimensions based on parent container
|
||||
let width = canvas.width = canvas.parentElement?.clientWidth || window.innerWidth;
|
||||
let height = canvas.height = canvas.parentElement?.clientHeight || window.innerHeight;
|
||||
|
||||
let animationFrameId: number;
|
||||
let time = 0;
|
||||
|
||||
const handleResize = () => {
|
||||
if (canvas.parentElement) {
|
||||
width = canvas.width = canvas.parentElement.clientWidth;
|
||||
height = canvas.height = canvas.parentElement.clientHeight;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
const render = () => {
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
time += 0.02; // Rotation speed
|
||||
|
||||
const strandGap = 40; // Vertical distance between "base pairs"
|
||||
const amplitude = 50; // Width of the helix
|
||||
const frequency = 0.015; // Tightness of the loops
|
||||
|
||||
// Position helix on the right side (85% of width) to keep content clear
|
||||
const centerX = width * 0.85;
|
||||
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
// Calculate number of points based on container height
|
||||
const numPoints = Math.ceil(height / strandGap) + 5;
|
||||
|
||||
for (let i = 0; i < numPoints; i++) {
|
||||
// Scroll the helix slowly downward
|
||||
const y = i * strandGap - (time * 10) % strandGap;
|
||||
|
||||
// Calculate rotation phase
|
||||
const phase = y * frequency + time;
|
||||
|
||||
// Strand 1 (Cyan)
|
||||
const x1 = centerX + Math.sin(phase) * amplitude;
|
||||
// Strand 2 (Emerald) - Offset by PI
|
||||
const x2 = centerX + Math.sin(phase + Math.PI) * amplitude;
|
||||
|
||||
// Depth calculation for 3D effect (fade when "back")
|
||||
const depth1 = Math.cos(phase);
|
||||
const depth2 = Math.cos(phase + Math.PI);
|
||||
|
||||
// Map depth (-1 to 1) to opacity (0.2 to 0.8)
|
||||
const alpha1 = (depth1 + 1) / 2 * 0.6 + 0.2;
|
||||
const alpha2 = (depth2 + 1) / 2 * 0.6 + 0.2;
|
||||
|
||||
// Draw Connector Line (Base Pair)
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y);
|
||||
ctx.lineTo(x2, y);
|
||||
ctx.strokeStyle = `rgba(34, 211, 238, 0.05)`; // Very faint connector
|
||||
ctx.stroke();
|
||||
|
||||
// Draw Strand 1 Particle
|
||||
ctx.beginPath();
|
||||
ctx.arc(x1, y, 4 + depth1, 0, Math.PI * 2);
|
||||
ctx.fillStyle = `rgba(34, 211, 238, ${alpha1})`; // Cyan
|
||||
ctx.fill();
|
||||
|
||||
// Draw Strand 2 Particle
|
||||
ctx.beginPath();
|
||||
ctx.arc(x2, y, 4 + depth2, 0, Math.PI * 2);
|
||||
ctx.fillStyle = `rgba(52, 211, 153, ${alpha2})`; // Emerald
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
animationFrameId = requestAnimationFrame(render);
|
||||
};
|
||||
|
||||
render();
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className={`block w-full h-full ${className}`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DNAHelix;
|
||||
Reference in New Issue
Block a user