Skip to content

Commit

Permalink
Merge branch 'unit/27-click-and-drag'
Browse files Browse the repository at this point in the history
  • Loading branch information
ElynnaChuang committed May 24, 2023
2 parents 5dcc8a8 + 0366819 commit 56ebff3
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 1 deletion.
88 changes: 88 additions & 0 deletions src/Pages/27ClickAndDrag/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { useRef, useState } from 'react';
import { LayoutCol1 } from '@/Layouts';
import { Title } from '@/Components';
import styles from './styles.module.scss';

const items = Array.from({ length: 25 }, (el, i) => ({
name: (i + 1).toString().padStart(2, 0),
id: i,
}));

const getXPosition = (event, areaDOM) => {
if (!areaDOM) return 0;

const { pageX } = event;
const pageSpace = areaDOM.getBoundingClientRect().left;
return pageX - pageSpace;
};

const MOVE_RATIO = 5; // 實際移動:滑鼠移動

const ClickAndDragPage = () => {
const scrollAreaRef = useRef();
const [isDown, setIsDown] = useState(false);
const [scrollInfo, setScrollInfo] = useState({ startX: 0, scrollLeft: 0 });

const handleMouseDown = e => {
setIsDown(true);

const scrollArea = scrollAreaRef.current;
const startX = getXPosition(e, scrollArea);
const { scrollLeft } = scrollArea; // scroll bar &左邊距離
setScrollInfo({ startX, scrollLeft });
};
const handleMouseLeave = () => {
setIsDown(false);
};
const handleMouseUp = () => {
setIsDown(false);
};
const handleMouseMove = e => {
if (!isDown) return;
e.preventDefault();
const scrollArea = scrollAreaRef.current;

const maxScrollLeft = scrollArea.scrollWidth - scrollArea.clientWidth;
const newX = getXPosition(e, scrollArea);
const moveDistance = (scrollInfo.startX - newX) * MOVE_RATIO;
const newScrollLeft = scrollInfo.scrollLeft + moveDistance;

// (scrollWidth:捲動範圍總寬 / clientWidth:可視範圍寬 / scrollLeft:超出可視範圍的寬)
scrollArea.scrollLeft = newScrollLeft > maxScrollLeft ? maxScrollLeft : newScrollLeft;
};

return (
<LayoutCol1 baseClassName={styles.page}>
<Title
title={
<>
<span>Click</span> <span>and</span> <span>Drag</span>
</>
}
size='m'
titleClassName={styles.title}
/>
<div className={styles.container}>
<div
ref={scrollAreaRef}
role='button'
tabIndex={0}
className={`${styles.scroll_items} ${isDown ? styles.active : ''}`}
onMouseDown={handleMouseDown}
onMouseLeave={handleMouseLeave}
onMouseUp={handleMouseUp}
onMouseMove={handleMouseMove}
>
{items.map(({ id, name }) => (
<div className={styles.item} key={id}>
<p>{name}</p>
</div>
))}
</div>
<div className={styles.shadow} />
</div>
</LayoutCol1>
);
};

export default ClickAndDragPage;
141 changes: 141 additions & 0 deletions src/Pages/27ClickAndDrag/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
$color_1: #ffbab2;
$color_2: #f4d97d;
$color_3: #a6d9e6;

.page {
background: #fff;
background: radial-gradient(
closest-side,
rgba($color: $color_1, $alpha: 45%) 20%,
#fff0 20%
)
10px -10px,
radial-gradient(closest-side, rgba($color: $color_2, $alpha: 45%) 20%, #fff0 20%) 0
10px,
radial-gradient(closest-side, rgba($color: $color_3, $alpha: 45%) 20%, #fff0 20%) -10px -10px;
background-size: 40px 40px;
}

.title {
width: fit-content;
padding: 1rem 2rem 1.2rem;

background: linear-gradient(#fff, #fff) padding-box,
linear-gradient(to right, $color_1, $color_2, $color_3) border-box;
border: 8px solid transparent;
border-radius: 50px;

& span:nth-child(1) {
color: $color_1;
}

& span:nth-child(2) {
color: $color_2;
}

& span:nth-child(3) {
color: $color_3;
}
}

.container {
position: relative;
z-index: 99;

overflow: hidden;
display: flex;

width: 100%;
height: 100%;

background: #c2c2c2;
border-radius: 0.5rem;
}

.shadow {
pointer-events: none;

position: absolute;
top: 0;
left: 0;

width: 100%;
height: 100%;

box-shadow: inset 0 0 1rem 4px rgba($color: #000, $alpha: 40%);
}

.scroll_items {
cursor: grab;
user-select: none;

overflow-x: overlay;
display: flex;
align-items: center;

width: 100%;
height: 100%;
padding: 1rem 5rem 1rem 0;

perspective: 300px;
perspective-origin: center 60%;

&::-webkit-scrollbar {
height: 16px;
appearance: none;
}

&::-webkit-scrollbar-thumb {
background-color: #737373;
background-clip: content-box;
border: 4px solid transparent;
border-radius: 100px;
}

&::-webkit-scrollbar-track {
background: #fff0;
}
}

.active {
cursor: grabbing;
background-color: #ececec;
transition: background-color 0.2s ease-out;
}

.item {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;

width: 200px;
height: calc(100% - 100px);

box-shadow: inset 0 0 0 4px #fff;

& > p {
font-size: 2rem;
color: #fff;
}

&:nth-child(even) {
transform: scaleX(1.07) rotateY(20deg);
}

&:nth-child(odd) {
transform: scaleX(1.07) rotateY(-20deg);
}

&:nth-child(3n + 1) {
background: $color_1;
}

&:nth-child(3n + 2) {
background: $color_2;
}

&:nth-child(3n + 3) {
background: $color_3;
}
}
3 changes: 2 additions & 1 deletion src/routesConfig.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const WordToSpeechPage = lazy(() => import('./Pages/23WordToSpeech'));
const StickyNavPage = lazy(() => import('./Pages/24StickyNav'));
const EventPage = lazy(() => import('./Pages/25Event'));
const NavDropdownPage = lazy(() => import('./Pages/26NavDropdown'));

const ClickAndDragPage = lazy(() => import('./Pages/27ClickAndDrag'));
export const routes = [
{ id: 0, path: '/', element: <HomePage /> },
{ id: 1, path: '01', element: <DrumPage /> },
Expand Down Expand Up @@ -56,4 +56,5 @@ export const routes = [
{ id: 24, path: '24', element: <StickyNavPage /> },
{ id: 25, path: '25', element: <EventPage /> },
{ id: 26, path: '26', element: <NavDropdownPage /> },
{ id: 27, path: '27', element: <ClickAndDragPage /> },
];

0 comments on commit 56ebff3

Please sign in to comment.