mirror of
https://github.com/harivansh-afk/React-Portfolio.git
synced 2026-04-15 08:03:45 +00:00
448 lines
11 KiB
Markdown
448 lines
11 KiB
Markdown
# Revised Smooth Scrolling Implementation Plan for React Portfolio
|
|
|
|
## Overview
|
|
|
|
Based on the issues encountered with the previous implementation, this revised plan takes a simpler but more reliable approach to create a smooth scrolling portfolio with perfectly contained full-page sections. We'll implement a fullpage.js-inspired solution with proper section sizing, positioning, and transitions.
|
|
|
|
## Technologies
|
|
|
|
- **React Fullpage**: A simplified custom implementation inspired by fullpage.js
|
|
- **React Intersection Observer**: To detect when sections enter/exit the viewport
|
|
- **Framer Motion**: For smooth animations and transitions
|
|
|
|
## Implementation Steps
|
|
|
|
### 1. Setup Dependencies
|
|
|
|
```bash
|
|
npm install framer-motion react-intersection-observer
|
|
```
|
|
|
|
### 2. Create FullPage Component
|
|
|
|
#### Create `src/components/FullPage.js`
|
|
|
|
```javascript
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
|
|
const FullPage = ({ children }) => {
|
|
const [currentPage, setCurrentPage] = useState(0);
|
|
const [isScrolling, setIsScrolling] = useState(false);
|
|
const containerRef = useRef(null);
|
|
const pagesCount = React.Children.count(children);
|
|
|
|
// Handle page change
|
|
const goToPage = (pageIndex) => {
|
|
if (pageIndex >= 0 && pageIndex < pagesCount && !isScrolling) {
|
|
setIsScrolling(true);
|
|
setCurrentPage(pageIndex);
|
|
|
|
// Get the corresponding section ID to update URL
|
|
const sectionId = React.Children.toArray(children)[pageIndex].props.id;
|
|
window.history.replaceState(null, null, `#${sectionId}`);
|
|
|
|
setTimeout(() => {
|
|
setIsScrolling(false);
|
|
}, 1000); // Match this with your transition duration
|
|
}
|
|
};
|
|
|
|
// Handle wheel events
|
|
const handleWheel = (e) => {
|
|
if (isScrolling) return;
|
|
|
|
if (e.deltaY > 0) {
|
|
// Scrolling down
|
|
goToPage(currentPage + 1);
|
|
} else {
|
|
// Scrolling up
|
|
goToPage(currentPage - 1);
|
|
}
|
|
};
|
|
|
|
// Handle key events
|
|
const handleKeyDown = (e) => {
|
|
if (isScrolling) return;
|
|
|
|
if (e.key === 'ArrowDown' || e.key === 'PageDown') {
|
|
goToPage(currentPage + 1);
|
|
} else if (e.key === 'ArrowUp' || e.key === 'PageUp') {
|
|
goToPage(currentPage - 1);
|
|
}
|
|
};
|
|
|
|
// Initialize event listeners
|
|
useEffect(() => {
|
|
const container = containerRef.current;
|
|
if (container) {
|
|
container.addEventListener('wheel', handleWheel, { passive: false });
|
|
window.addEventListener('keydown', handleKeyDown);
|
|
|
|
return () => {
|
|
container.removeEventListener('wheel', handleWheel);
|
|
window.removeEventListener('keydown', handleKeyDown);
|
|
};
|
|
}
|
|
}, [currentPage, isScrolling]);
|
|
|
|
// Check for hash in URL on initial load
|
|
useEffect(() => {
|
|
const hash = window.location.hash.substring(1);
|
|
if (hash) {
|
|
const childrenArray = React.Children.toArray(children);
|
|
const initialPageIndex = childrenArray.findIndex(child => child.props.id === hash);
|
|
if (initialPageIndex !== -1) {
|
|
setCurrentPage(initialPageIndex);
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
className="fullpage-container"
|
|
style={{ overflow: 'hidden', height: '100vh', position: 'relative' }}
|
|
>
|
|
<div
|
|
className="fullpage-sections"
|
|
style={{
|
|
height: '100%',
|
|
width: '100%',
|
|
transition: 'transform 1s cubic-bezier(0.645, 0.045, 0.355, 1.000)',
|
|
transform: `translateY(-${currentPage * 100}vh)`
|
|
}}
|
|
>
|
|
{React.Children.map(children, (child, index) => (
|
|
<div
|
|
key={index}
|
|
className="fullpage-section"
|
|
style={{ height: '100vh', width: '100%' }}
|
|
>
|
|
{child}
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Pagination Dots */}
|
|
<div className="fullpage-pagination">
|
|
{Array.from({ length: pagesCount }).map((_, index) => (
|
|
<motion.div
|
|
key={index}
|
|
className={`pagination-dot ${currentPage === index ? 'active' : ''}`}
|
|
initial={{ opacity: 0.5, scale: 0.8 }}
|
|
animate={{
|
|
opacity: currentPage === index ? 1 : 0.5,
|
|
scale: currentPage === index ? 1 : 0.8
|
|
}}
|
|
onClick={() => goToPage(index)}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default FullPage;
|
|
```
|
|
|
|
### 3. Create Section Component
|
|
|
|
#### Create `src/components/Section.js`
|
|
|
|
```javascript
|
|
import React from 'react';
|
|
import { motion } from 'framer-motion';
|
|
|
|
const Section = ({ children, id, className = '' }) => {
|
|
return (
|
|
<motion.section
|
|
id={id}
|
|
className={`fullpage-section-content ${className}`}
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
style={{
|
|
height: '100%',
|
|
width: '100%',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
position: 'relative'
|
|
}}
|
|
>
|
|
{children}
|
|
</motion.section>
|
|
);
|
|
};
|
|
|
|
export default Section;
|
|
```
|
|
|
|
### 4. Update Navigation Component
|
|
|
|
#### Enhance Header Component
|
|
|
|
Update the header to work with our new FullPage component by handling section navigation.
|
|
|
|
```javascript
|
|
// In src/header/index.js
|
|
|
|
import React, { useState, useContext } from 'react';
|
|
import "./style.css";
|
|
import { RiMenuLine, RiCloseLine } from "react-icons/ri";
|
|
import { logotext } from "../content_option";
|
|
import Themetoggle from "../components/themetoggle";
|
|
import { Socialicons } from "../components/socialicons";
|
|
|
|
const Headermain = () => {
|
|
const [isActive, setActive] = useState("false");
|
|
|
|
const handleToggle = () => {
|
|
setActive(!isActive);
|
|
document.body.classList.toggle("ovhidden");
|
|
};
|
|
|
|
const handleNavClick = (e, targetId) => {
|
|
e.preventDefault();
|
|
handleToggle();
|
|
|
|
// Find all section elements
|
|
const sections = document.querySelectorAll('.fullpage-section');
|
|
const targetSection = Array.from(sections).findIndex(
|
|
section => section.querySelector(`#${targetId}`) !== null
|
|
);
|
|
|
|
// Trigger a custom event that FullPage component will listen for
|
|
if (targetSection !== -1) {
|
|
const customEvent = new CustomEvent('navigateTo', {
|
|
detail: { index: targetSection }
|
|
});
|
|
document.dispatchEvent(customEvent);
|
|
}
|
|
};
|
|
|
|
// The rest of the component remains the same
|
|
}
|
|
```
|
|
|
|
### 5. Update App.js
|
|
|
|
Rewrite App.js to use our new FullPage and Section components.
|
|
|
|
```javascript
|
|
import React from "react";
|
|
import "bootstrap/dist/css/bootstrap.min.css";
|
|
import AnimatedCursor from "../hooks/AnimatedCursor";
|
|
import Headermain from "../header";
|
|
import { Home } from "../pages/home";
|
|
import { About } from "../pages/about";
|
|
import { Portfolio } from "../pages/portfolio";
|
|
import { ContactUs } from "../pages/contact";
|
|
import { Socialicons } from "../components/socialicons";
|
|
import FullPage from "../components/FullPage";
|
|
import Section from "../components/Section";
|
|
import "./App.css";
|
|
|
|
export default function App() {
|
|
return (
|
|
<div className="app-container">
|
|
<div className="cursor__dot">
|
|
<AnimatedCursor
|
|
innerSize={15}
|
|
outerSize={15}
|
|
color="255, 255 ,255"
|
|
outerAlpha={0.4}
|
|
innerScale={0.7}
|
|
outerScale={5}
|
|
/>
|
|
</div>
|
|
<Headermain />
|
|
<FullPage>
|
|
<Section id="home">
|
|
<Home />
|
|
</Section>
|
|
<Section id="about">
|
|
<About />
|
|
</Section>
|
|
<Section id="portfolio">
|
|
<Portfolio />
|
|
</Section>
|
|
<Section id="contact">
|
|
<ContactUs />
|
|
</Section>
|
|
</FullPage>
|
|
<Socialicons />
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 6. Add Necessary CSS Styles
|
|
|
|
#### Update `src/app/App.css`
|
|
|
|
```css
|
|
/* Reset styles */
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html, body {
|
|
overflow: hidden;
|
|
position: fixed;
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
|
|
/* Full page styles */
|
|
.app-container {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.fullpage-container {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.fullpage-section {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.fullpage-section-content {
|
|
padding-top: 60px; /* Account for header */
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* Navigation dots */
|
|
.fullpage-pagination {
|
|
position: fixed;
|
|
right: 20px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
z-index: 100;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
|
|
.pagination-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background-color: var(--text-color, #fff);
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.pagination-dot.active {
|
|
transform: scale(1.2);
|
|
}
|
|
|
|
/* Make sure header is above fullpage */
|
|
.site__header {
|
|
z-index: 1000;
|
|
}
|
|
|
|
/* Social icons positioning */
|
|
.stick_follow_icon {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
z-index: 1000;
|
|
}
|
|
|
|
/* Responsive styles */
|
|
@media (max-width: 768px) {
|
|
.fullpage-pagination {
|
|
right: 10px;
|
|
gap: 10px;
|
|
}
|
|
|
|
.pagination-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
.fullpage-section-content {
|
|
padding-top: 50px;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 7. Modify FullPage Component to Listen for Navigation Events
|
|
|
|
Update the FullPage component to listen for custom navigation events from the header.
|
|
|
|
```javascript
|
|
// Add this useEffect in the FullPage component
|
|
|
|
useEffect(() => {
|
|
const handleNavigation = (e) => {
|
|
const { index } = e.detail;
|
|
goToPage(index);
|
|
};
|
|
|
|
document.addEventListener('navigateTo', handleNavigation);
|
|
|
|
return () => {
|
|
document.removeEventListener('navigateTo', handleNavigation);
|
|
};
|
|
}, []);
|
|
```
|
|
|
|
## Implementation Timeline
|
|
|
|
1. **Setup (30 minutes)**:
|
|
- Install dependencies
|
|
- Create necessary files
|
|
|
|
2. **Core Components (2 hours)**:
|
|
- Implement FullPage component
|
|
- Implement Section component
|
|
- Setup basic navigation
|
|
|
|
3. **Styling and Refinement (1.5 hours)**:
|
|
- Implement CSS styles
|
|
- Add pagination dots
|
|
- Ensure responsive behavior
|
|
|
|
4. **Navigation Integration (1 hour)**:
|
|
- Connect header navigation with fullpage scrolling
|
|
- Implement URL hash updates
|
|
|
|
5. **Testing and Debugging (1 hour)**:
|
|
- Test on different browsers
|
|
- Ensure smooth scrolling on different devices
|
|
|
|
## Expected Results
|
|
|
|
- Clean, full-viewport sections that are perfectly contained
|
|
- Smooth transitions between sections
|
|
- Navigation dots for quick section access
|
|
- Header links that correctly navigate to sections
|
|
- URL hash updates for direct section access
|
|
- Keyboard navigation support
|
|
- Responsive design that works on all devices
|
|
|
|
## Future Enhancements
|
|
|
|
- Add touch swipe support for mobile devices
|
|
- Implement section-specific animations
|
|
- Add scroll progress indicator
|
|
- Create horizontal sliding sections for portfolio items
|
|
- Implement background parallax effects
|