-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
237 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import { Button } from '@/component/UI/button'; | ||
import { useCallback, useState } from 'react'; | ||
|
||
export const CircleScaleExercise = () => { | ||
// Initial positions of the circles | ||
const [circles, setCircles] = useState([ | ||
{ id: 1, cx: 200, value: 0 }, | ||
{ id: 2, cx: 220, value: 50 }, | ||
{ id: 3, cx: 240, value: 60 }, | ||
{ id: 4, cx: 260, value: 82 }, | ||
{ id: 5, cx: 280, value: 100 }, | ||
]); | ||
const [draggingCircleId, setDraggingCircleId] = useState(null); | ||
|
||
// Handle mouse down event to start dragging | ||
const handleMouseDown = useCallback((e, id) => { | ||
setDraggingCircleId(id); | ||
}, []); | ||
|
||
// Handle mouse move event to update circle's position | ||
const handleMouseMove = useCallback( | ||
(e) => { | ||
if (draggingCircleId !== null) { | ||
const svgRect = e.currentTarget.getBoundingClientRect(); | ||
const newCx = e.clientX - svgRect.left; | ||
const boundedCx = Math.min(Math.max(newCx, 0), 500); | ||
|
||
setCircles((prevCircles) => | ||
prevCircles.map((circle) => | ||
circle.id === draggingCircleId | ||
? { ...circle, cx: boundedCx } | ||
: circle | ||
) | ||
); | ||
} | ||
}, | ||
[draggingCircleId] | ||
); | ||
|
||
// Handle mouse up event to stop dragging | ||
const handleMouseUp = useCallback(() => { | ||
setDraggingCircleId(null); | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<div className="mx-auto"> | ||
<svg | ||
width={500} | ||
height={400} | ||
overflow={'visible'} | ||
onMouseMove={handleMouseMove} | ||
onMouseUp={handleMouseUp} | ||
onMouseDown={(e) => e.preventDefault()} // Prevent default behavior to avoid unwanted text selection | ||
onMouseEnter={() => setDraggingCircleId(null)} | ||
> | ||
{circles.map((circle) => ( | ||
<g key={circle.id}> | ||
<circle | ||
cx={circle.cx} | ||
cy={140} | ||
r={30} | ||
fill="#69b3a2" | ||
stroke="black" | ||
fillOpacity={1} | ||
onMouseDown={(e) => handleMouseDown(e, circle.id)} | ||
cursor={'pointer'} | ||
/> | ||
<text | ||
x={circle.cx} | ||
y={140} | ||
textAnchor="middle" | ||
alignmentBaseline="central" | ||
fontSize={12} | ||
cursor={'pointer'} | ||
pointerEvents={'none'} | ||
> | ||
{circle.value} | ||
</text> | ||
</g> | ||
))} | ||
|
||
{/* Annotation */} | ||
<line x1={0} x2={500} y1={200} y2={200} stroke="black" /> | ||
|
||
<line x1={0} x2={0} y1={200} y2={200 + 5} stroke="black" /> | ||
<text | ||
x={0} | ||
y={200 + 20} | ||
textAnchor="middle" | ||
fill="black" | ||
fontSize={14} | ||
> | ||
0 px | ||
</text> | ||
|
||
<line x1={250} x2={250} y1={200} y2={200 + 5} stroke="black" /> | ||
<text | ||
x={250} | ||
y={200 + 20} | ||
textAnchor="middle" | ||
fill="black" | ||
fontSize={14} | ||
> | ||
250 px | ||
</text> | ||
|
||
<line x1={500} x2={500} y1={200} y2={200 + 5} stroke="black" /> | ||
<text | ||
x={500} | ||
y={200 + 20} | ||
textAnchor="middle" | ||
fill="black" | ||
fontSize={14} | ||
> | ||
500 px | ||
</text> | ||
</svg> | ||
</div> | ||
|
||
<div> | ||
<Button | ||
onClick={() => { | ||
setCircles([ | ||
{ id: 1, cx: 0, value: 0 }, | ||
{ id: 2, cx: 500 / 2, value: 50 }, | ||
{ id: 3, cx: (60 / 100) * 500, value: 60 }, | ||
{ id: 4, cx: (82 / 100) * 500, value: 82 }, | ||
{ id: 5, cx: 500, value: 100 }, | ||
]); | ||
}} | ||
> | ||
Show right positions | ||
</Button> | ||
</div> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import React, { useCallback, useState } from 'react'; | ||
import TitleAndDescription from '@/component/TitleAndDescription'; | ||
import { LayoutCourse } from '@/component/LayoutCourse'; | ||
import { lessonList } from '@/util/lessonList'; | ||
import { CodeBlock } from '@/component/UI/CodeBlock'; | ||
import { Button } from '@/component/UI/button'; | ||
import Link from 'next/link'; | ||
import { CircleScaleExercise } from './CircleScaleExercise'; | ||
|
||
const previousURL = '/course/scales/introduction'; | ||
const currentURL = '/course/scales/linear-scale'; | ||
const nextURL = '/course/scales/other-scale'; | ||
const seoDescription = ''; | ||
|
||
export default function Home() { | ||
const currentLesson = lessonList.find((l) => l.link === currentURL); | ||
|
||
return ( | ||
<LayoutCourse | ||
title={currentLesson.name} | ||
seoDescription={seoDescription} | ||
nextTocItem={lessonList.find((l) => l.link === nextURL)} | ||
previousTocItem={lessonList.find((l) => l.link === previousURL)} | ||
> | ||
<TitleAndDescription | ||
title={currentLesson.name} | ||
lessonStatus={currentLesson.status} | ||
readTime={currentLesson.readTime} | ||
selectedLesson={currentLesson} | ||
description={ | ||
<> | ||
<p> | ||
The previous lesson described the concept of{' '} | ||
<Link href="/course/scales/introduction">scale</Link> in data | ||
visualization. Scales allow, for instance, to translate a value in | ||
our dataset to a position on the screen. | ||
</p> | ||
<p> | ||
Now, let's study the most common scale type and its d3.js | ||
implementation: the <b>linear</b> scale and its{' '} | ||
<code>scaleLinear()</code> function. | ||
</p> | ||
</> | ||
} | ||
/> | ||
|
||
<CircleScaleExercise /> | ||
|
||
{/* - | ||
- | ||
- | ||
- | ||
- | ||
- | ||
- */} | ||
|
||
<h2> | ||
The <code>scaleLinear()</code> function | ||
</h2> | ||
<p> | ||
The <code>scaleLinear()</code> function is part of the{' '} | ||
<a href="https://github.com/d3/d3-scale">d3-scale</a> module of d3.js. | ||
</p> | ||
<p> | ||
It expects 2 inputs: a <b>domain</b> and a <b>range</b>. | ||
</p> | ||
<h3>🏠 Domain</h3> | ||
<p> | ||
Usually an array of length 2. It provides the <code>min</code> and the{' '} | ||
<code>max</code> of the values we have in the dataset. | ||
</p> | ||
<h3>📏 Range</h3> | ||
<p> | ||
Usually an array of length 2. It provides the start and the end of the | ||
positions we are targeting in pixel. | ||
</p> | ||
<p> | ||
The output is a function that expects only 1 argument. You give it a | ||
value from the domain, and it returns the corresponding value in the | ||
Range | ||
</p> | ||
|
||
{/* - | ||
- | ||
- | ||
- | ||
- | ||
- | ||
- */} | ||
|
||
<h2>Much more power</h2> | ||
<p>The scaleLinear function actually make much more than that!!!</p> | ||
</LayoutCourse> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters