diff --git a/pages/course/svg/d3-shape.tsx b/pages/course/svg/d3-shape.tsx new file mode 100644 index 00000000..55948c1d --- /dev/null +++ b/pages/course/svg/d3-shape.tsx @@ -0,0 +1,252 @@ +import React from 'react'; +import TitleAndDescription from '@/component/TitleAndDescription'; +import { LayoutCourse } from '@/component/LayoutCourse'; +import { lessonList } from '@/util/lessonList'; +import Link from 'next/link'; +import { CodeBlock } from '@/component/UI/CodeBlock'; +import { Sidenote } from '@/component/SideNote'; +import { CodeSandbox } from '@/component/CodeSandbox'; +import { ExerciseAccordion } from '@/component/ExerciseAccordion'; +import { + Exercise, + ExerciseDoubleSandbox, +} from '@/component/ExerciseDoubleSandbox'; + +const previousURL = '/course/svg/path-element'; +const currentURL = '/course/svg/d3-shape'; +const nextURL = '/course/svg/tips-and-tricks'; +const seoDescription = ''; + +export default function Home() { + const currentLesson = lessonList.find((l) => l.link === currentURL); + + if (!currentLesson) { + return null; + } + + return ( + l.link === nextURL)} + previousTocItem={lessonList.find((l) => l.link === previousURL)} + > + +

+ Some shapes can be quite complex—like rings, slices, paths, + polygons, and curves. Manually creating the `d` attribute for + these shapes would be a nightmare! +

+

+ Fortunately, D3 provides a module specifically for handling all + these shapes. Let’s explore how it can simplify our work. +

+ + } + /> +

Most basic example

+

+ The <path> element allows to draw literally any shape + in the SVG area. +

+

+ You provide it with a series of drawing commands that will make + straight or curved lines, resulting in your final shape. +

+

Let's check a basic example:

+ + + +`.trim()} + /> +

This will result in the following shape:

+ + + +

Nice! This almost look like a line chart already!

+

+ As you can see the <path> element expect a required{' '} + d attribute. This attribute defines the shape of the path. + Let's discover its syntax. +

+

+ Understanding the d Attribute +

+

+ The d attribute defines the shape and outline of the path + by specifying a series of drawing commands. +

+

+ Each command consists of a letter that represents the type of + drawing action (such as M for Move To) followed by + one or more numbers that define the coordinates or control + points. +

+

Here is a breakdown of the svg path above:

+
+ + Remember that the coordinate system starts from the top left in + svg! +

+ } + /> +
+
    +
  • + M0 105: moves the starting point of the path to + coordinates 0, 105. (M stands for{' '} + move to) +
  • +
  • + L100 200: draws a straight line from the current point ( + 0, 105) to the point (100, 200). ( + L stands for line to) +
  • +
  • + 200 200: draws a new straight line from the current point + to the point (200, 200). +
  • +
+

+ Now, experiment with some changes in the sandbox below to get a better + understanding of how it works: +

+
+ +
+

The good news 🎁

+

+ You won’t need to manually write the d attribute yourself. + Understanding its role is helpful, but in practice, you'll rely on a + d3.js helper function to generate paths for you. +

+

+ This helper function is called line(). It takes your data + and automatically convert it into the appropriate path{' '} + d string. It looks like this: +

+ d.x) + .y((d) => d.y); + +const pathData = lineGenerator(data); + +// console.log(pathData) +// "M0 40 L50 70 L100 150 L150 150 L200 200 L250 50 L300 90" + `} + /> +

+ This is one of the reasons why d3.js is so powerful and beloved for data + visualization! 💙 +

+

Exercises

+ First line chart, + content: , + }, + { + title: ( + + Close a path with Z (and build your first area + chart) + + ), + content: , + }, + ]} + />{' '} +
+ ); +} + +const exercices: Exercise[] = [ + { + whyItMatters: ( + <> +

+ Creating a graph involves translating a set of numbers from a + table (the data) into coordinates on a screen! +

+

+ While doing this manually can be tedious, we'll soon explore how d3.js + can automate the process and make it much easier. +

+ + ), + toDo: ( + + ), + practiceSandbox: 'exercise/SvgPathFirstLineChartPractice', + solutionSandbox: 'exercise/SvgPathFirstLineChartSolution', + }, + + { + whyItMatters: ( + <> +

+ The <path> element can be used to draw extended + lines or create closed shapes. +

+

+ To close a shape, add Z at the end of the d{' '} + attribute and use the fill property to apply a color. +

+ + ), + toDo: ( + + ), + practiceSandbox: 'exercise/SvgPathFirstAreaChartPractice', + solutionSandbox: 'exercise/SvgPathFirstAreaChartSolution', + }, +]; diff --git a/pages/course/svg/main-svg-elements.tsx b/pages/course/svg/main-svg-elements.tsx index e2ee1ce6..7df71e1e 100644 --- a/pages/course/svg/main-svg-elements.tsx +++ b/pages/course/svg/main-svg-elements.tsx @@ -13,7 +13,7 @@ import { const previousURL = '/course/svg/introduction'; const currentURL = '/course/svg/main-svg-elements'; -const nextURL = undefined; +const nextURL = '/course/svg/path-element'; const seoDescription = ''; export default function Home() { diff --git a/pages/course/svg/path-element.tsx b/pages/course/svg/path-element.tsx index e245eaa0..4536c228 100644 --- a/pages/course/svg/path-element.tsx +++ b/pages/course/svg/path-element.tsx @@ -7,10 +7,15 @@ import { CodeBlock } from '@/component/UI/CodeBlock'; import { Sidenote } from '@/component/SideNote'; import { CodeSandbox } from '@/component/CodeSandbox'; import { ExerciseAccordion } from '@/component/ExerciseAccordion'; +import { + Exercise, + ExerciseDoubleSandbox, +} from '@/component/ExerciseDoubleSandbox'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/component/UI/tabs'; const previousURL = '/course/svg/main-svg-elements'; const currentURL = '/course/svg/path-element'; -const nextURL = '/course/svg/tips-and-tricks'; +const nextURL = '/course/svg/d3-shape'; const seoDescription = ''; export default function Home() { @@ -48,7 +53,15 @@ export default function Home() { } /> - + {/* - + - + - + - + - + - + - + - + */}

Most basic example

The <path> element allows to draw literally any shape @@ -78,14 +91,21 @@ export default function Home() { stroke="#69b3a2" /> -

Nice! This almost look like a line chart already!

As you can see the <path> element expect a required{' '} d attribute. This attribute defines the shape of the path. Let's discover its syntax.

- + {/* - + - + - + - + - + - + - + - + */}

Understanding the d Attribute

@@ -130,59 +150,119 @@ export default function Home() { Now, experiment with some changes in the sandbox below to get a better understanding of how it works:

-
- + {/* - + - + - + - + - + - + - + - + */} +

Not only lines!

+

+ The path element can create a wide variety of shapes, + including the most complex ones. +

+

+ Two particularly useful features are the Bézier curve (C) + and the arc curve (A). Check how they work with those 2 + diagrams: +

+ +
+ + Curve + Arc + +
+ +
+ +
+
+ +
+ +
+
+
+

+
+

+

+ Ok, that's definitely not easy to grasp. +

+

+ In the sandbox below, try to play with the numbers, and see what + happens. +

+
+ +
+ {/* - + - + - + - + - + - + - + - + */}

The good news 🎁

You won’t need to manually write the d attribute yourself. - Understanding its role is helpful, but in practice, you'll rely on d3.js - helper functions to generate paths for you. + Understanding its role is helpful, but in practice, you'll rely on a + d3.js helper function to generate paths for you.

- These functions take your data and automatically convert it into - the appropriate path data. + This helper function is called line(). It takes your data + and automatically convert it into the appropriate path{' '} + d string. It looks like this:

+ d.x) + .y((d) => d.y); + +const pathData = lineGenerator(data); + +// console.log(pathData) +// "M0 40 L50 70 L100 150 L150 150 L200 200 L250 50 L300 90" + `} + />

This is one of the reasons why d3.js is so powerful and beloved for data visualization! 💙

- + {/* - + - + - + - + - + - + - + - + */}

Exercises

Your first line chart, - whyItMatters: ( - <> -

- Creating a graph involves translating a set of numbers{' '} - from a table (the data) into coordinates on a screen! -

-

- While doing this manually can be tedious, we'll soon explore - how d3.js can automate the process and make it much - easier. -

- - ), - toDo: ( - - ), - practiceSandbox: 'exercise/SvgPathFirstLineChartPractice', - solutionSandbox: 'exercise/SvgPathFirstLineChartSolution', + title: First line chart, + content: , }, - { title: ( @@ -190,44 +270,72 @@ export default function Home() { chart) ), - whyItMatters: ( - <> -

- The <path> element can be used to draw - extended lines or create closed shapes. -

-

- To close a shape, add Z at the end of the{' '} - d attribute and use the fill{' '} - property to apply a color. -

- - ), - toDo: ( -
    -
  • - Modify the path to start at the bottom-left corner of the SVG - area. -
  • -
  • - Adjust the path to end at the bottom-right corner of the SVG - area. -
  • -
  • - Add a Z at the end of the d{' '} - attribute to close the shape. -
  • -
  • - Update the fill property to apply a color to the - shape. -
  • -
- ), - practiceSandbox: 'exercise/SvgPathFirstAreaChartPractice', - solutionSandbox: 'exercise/SvgPathFirstAreaChartSolution', + content: , }, ]} - /> + />{' '} ); } + +const exercices: Exercise[] = [ + { + whyItMatters: ( + <> +

+ Creating a graph involves translating a set of numbers from a + table (the data) into coordinates on a screen! +

+

+ While doing this manually can be tedious, we'll soon explore how d3.js + can automate the process and make it much easier. +

+ + ), + toDo: ( +
    +
  • + Add a path element to the svg area +
  • +
  • The x coordinates must be 0, 50, 100, 150, 200, 250, 300
  • +
  • The y coordinates must be 40, 70, 150, 150, 200, 50, 90
  • +
+ ), + practiceSandbox: 'exercise/SvgPathFirstLineChartPractice', + solutionSandbox: 'exercise/SvgPathFirstLineChartSolution', + }, + + { + whyItMatters: ( + <> +

+ The <path> element can be used to draw extended + lines or create closed shapes. +

+

+ To close a shape, add Z at the end of the d{' '} + attribute and use the fill property to apply a color. +

+ + ), + toDo: ( +
    +
  • + Modify the path to start at the bottom-left corner of the SVG area. +
  • +
  • + Adjust the path to end at the bottom-right corner of the SVG area. +
  • +
  • + Add a Z at the end of the d attribute to + close the shape. +
  • +
  • + Update the fill property to apply a color to the shape. +
  • +
+ ), + practiceSandbox: 'exercise/SvgPathFirstAreaChartPractice', + solutionSandbox: 'exercise/SvgPathFirstAreaChartSolution', + }, +]; diff --git a/public/excalidraw/arc-curve-svg.png b/public/excalidraw/arc-curve-svg.png new file mode 100644 index 00000000..13659c56 Binary files /dev/null and b/public/excalidraw/arc-curve-svg.png differ diff --git a/public/excalidraw/bezier-curve-svg.png b/public/excalidraw/bezier-curve-svg.png new file mode 100644 index 00000000..6739a8cb Binary files /dev/null and b/public/excalidraw/bezier-curve-svg.png differ diff --git a/util/lessonList.tsx b/util/lessonList.tsx index 1e9b9801..b9a71b48 100644 --- a/util/lessonList.tsx +++ b/util/lessonList.tsx @@ -124,6 +124,18 @@ export const lessonList: Lesson[] = [ status: 'free', moduleId: 'svg', }, + { + name: 'Building shapes with d3', + description: ( + <> +

Some shapes are complicated to draw. Fortunately, d3.js is here.

+ + ), + readTime: 4, + link: '/course/svg/d3-shape', + status: 'wip', + moduleId: 'svg', + }, { name: 'SVG tips & tricks', description: ( diff --git a/viz/exercise/SvgCurveArcPractice/Graph.tsx b/viz/exercise/SvgCurveArcPractice/Graph.tsx new file mode 100644 index 00000000..8e558365 --- /dev/null +++ b/viz/exercise/SvgCurveArcPractice/Graph.tsx @@ -0,0 +1,9 @@ +// margin of 30 above and below + +export const Graph = () => { + return ( + + // Paths go here + + ); +}; diff --git a/viz/exercise/SvgCurveArcPractice/index.js b/viz/exercise/SvgCurveArcPractice/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/SvgCurveArcPractice/index.js @@ -0,0 +1,6 @@ +// File used to render something in codesandbox only +import ReactDOM from 'react-dom'; +import { Graph } from './Graph'; + +const rootElement = document.getElementById('root'); +ReactDOM.render(, rootElement); diff --git a/viz/exercise/SvgCurveArcPractice/package.json b/viz/exercise/SvgCurveArcPractice/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/SvgCurveArcPractice/package.json @@ -0,0 +1,28 @@ +{ + "name": "pie-chart-basic", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "index.js", + "dependencies": { + "react": "17.0.2", + "react-dom": "17.0.2", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/viz/exercise/SvgCurveArcSolution/Graph.tsx b/viz/exercise/SvgCurveArcSolution/Graph.tsx new file mode 100644 index 00000000..a15c2dff --- /dev/null +++ b/viz/exercise/SvgCurveArcSolution/Graph.tsx @@ -0,0 +1,8 @@ +export const Graph = () => { + return ( + + + + + ); +}; diff --git a/viz/exercise/SvgCurveArcSolution/index.js b/viz/exercise/SvgCurveArcSolution/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/SvgCurveArcSolution/index.js @@ -0,0 +1,6 @@ +// File used to render something in codesandbox only +import ReactDOM from 'react-dom'; +import { Graph } from './Graph'; + +const rootElement = document.getElementById('root'); +ReactDOM.render(, rootElement); diff --git a/viz/exercise/SvgCurveArcSolution/package.json b/viz/exercise/SvgCurveArcSolution/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/SvgCurveArcSolution/package.json @@ -0,0 +1,28 @@ +{ + "name": "pie-chart-basic", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "index.js", + "dependencies": { + "react": "17.0.2", + "react-dom": "17.0.2", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +}