Skip to content

Commit 838917e

Browse files
committed
handle styled animated info
1 parent 4753d8c commit 838917e

File tree

5 files changed

+139
-28
lines changed

5 files changed

+139
-28
lines changed

showcase/src/Body.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useCallback } from 'react'
1+
import React, { useState, useEffect, useCallback } from 'react'
22
import styled from 'styled-components'
33
import { Router } from '@reach/router'
44
import { HEADER_ALLOWANCE } from './utils/constants'
@@ -21,7 +21,8 @@ import { INFO } from './constants/patternInfo'
2121

2222
import GithubLogo from './assets/github_logo.svg'
2323
import InfoIllustration from './assets/light_bulb.svg'
24-
import { useAnimatedBulb } from './content/AnimatedBulb'
24+
import { useAnimatedBulb } from './content/useAnimatedBulb'
25+
import { useAnimatedInfo } from './content/useAnimatedInfo'
2526

2627
const StyledAppBody = styled.div`
2728
background: #191921;
@@ -51,17 +52,24 @@ const PRs = PR_IDs.map(id => `${PR_ROOT}/${id}`)
5152

5253
const Header = ({ title, patternNumber }) => {
5354
// animated el
54-
const [lightBulbEl, setlightBulbEl] = useState(null)
55+
const [{ lightBulbEl, infoEl, infoTextEl }, setRefState] = useState({})
5556

56-
const setLightBulbElRef = useCallback(node => {
57+
const setRef = useCallback(node => {
5758
if (node !== null) {
58-
setlightBulbEl(node)
59+
setRefState(prevRefState => ({
60+
...prevRefState,
61+
[node.dataset.refkey]: node
62+
}))
5963
}
6064
}, [])
6165

6266
const animatedBulbTimeline = useAnimatedBulb({
6367
el: lightBulbEl
6468
})
69+
const animatedInfoTimeline = useAnimatedInfo({
70+
bgEl: infoEl,
71+
textEl: infoTextEl
72+
})
6573

6674
// toggle info display
6775
const [isInfoShown, setInfoShown] = useState(false)
@@ -71,13 +79,29 @@ const Header = ({ title, patternNumber }) => {
7179
animatedBulbTimeline.replay()
7280
}
7381

82+
useEffect(() => {
83+
if (!isInfoShown) {
84+
return
85+
}
86+
87+
const timer = setTimeout(() => {
88+
animatedInfoTimeline.replay()
89+
}, 100)
90+
91+
return () => clearTimeout(timer)
92+
}, [isInfoShown])
93+
7494
return (
7595
<StyledHeader>
7696
{isInfoShown && (
77-
<StyledInfoContainer>{INFO[patternNumber]}</StyledInfoContainer>
97+
<StyledInfoContainer ref={setRef} data-refkey='infoEl'>
98+
<p ref={setRef} data-refkey='infoTextEl'>
99+
{INFO[patternNumber]}
100+
</p>
101+
</StyledInfoContainer>
78102
)}
79103
<H1>{title}</H1>
80-
<div ref={setLightBulbElRef}>
104+
<div ref={setRef} data-refkey='lightBulbEl'>
81105
<InfoIllustration
82106
style={{ width: '30px', marginLeft: '5px', cursor: 'pointer' }}
83107
onClick={toggleInfo}
@@ -155,6 +179,7 @@ const RouteComponent = ({ pattern, patternNumber, isMediumOrLarger }) => {
155179
</StyledContentContainer>
156180
)
157181
}
182+
158183
const Body = ({ setShowSidebar, isMediumOrLarger }) => {
159184
const toggleSidebar = () => setShowSidebar(val => !val)
160185

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const THE_MEDIUM_CLAP =
2-
"The Medium clap is such a great component to build. It's practical and poses real world problems. Note how the 'before' and 'after' demos behave in the same way but are powered by different mechanisms: an higher order component, and a custom hook!"
2+
"The Medium clap is a great example component. It's practical and poses real world challenges. Note how the 'before' and 'after' demos behave similar ways, but are powered by different patterns: an higher order component, and a custom hook! Don't forget to check the code implementation 😉"
33

44
export const INFO = {
5-
'1': THE_MEDIUM_CLAP,
6-
'2': 'Hola hola!!!'
5+
'1': THE_MEDIUM_CLAP
76
}

showcase/src/content/StyledContent.js

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ export const SCREEN_SIZES = {
2121
xl: 1140
2222
}
2323

24+
export const media = Object.keys(SCREEN_SIZES).reduce((accumulator, label) => {
25+
// use em in breakpoints to work properly cross-browser and support users
26+
// changing their browsers font-size: https://zellwk.com/blog/media-query-units/
27+
const emSize = SCREEN_SIZES[label] / 16
28+
accumulator[label] = (...args) => css`
29+
@media (min-width: ${emSize}em) {
30+
${css(...args)};
31+
}
32+
`
33+
return accumulator
34+
}, {})
35+
2436
export const StyledFloatingBtn = styled.button`
2537
background: ${() => PALE_RED};
2638
width: 71px;
@@ -47,24 +59,27 @@ export const Header = styled.div`
4759
`
4860

4961
export const StyledInfoContainer = styled.aside`
50-
position: absolute;
51-
top: 0;
52-
background: red;
53-
`
54-
55-
// "0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)"
62+
overflow: hidden;
63+
padding: 5px;
64+
z-index: 10;
65+
position: fixed;
66+
top: initial;
67+
bottom: 0;
68+
right: 0;
69+
left: 0;
70+
border: 0;
71+
background: #191921;
72+
border-top: 1px solid white;
5673
57-
export const media = Object.keys(SCREEN_SIZES).reduce((accumulator, label) => {
58-
// use em in breakpoints to work properly cross-browser and support users
59-
// changing their browsers font-size: https://zellwk.com/blog/media-query-units/
60-
const emSize = SCREEN_SIZES[label] / 16
61-
accumulator[label] = (...args) => css`
62-
@media (min-width: ${emSize}em) {
63-
${css(...args)};
64-
}
65-
`
66-
return accumulator
67-
}, {})
74+
${media.md`
75+
position: absolute;
76+
top: 20px;
77+
bottom: initial;
78+
right: initial;
79+
left: initial;
80+
border: 1px solid white;
81+
`}
82+
`
6883

6984
export const StyledContentContainer = styled.div`
7085
padding: ${() => `${HEADER_ALLOWANCE}vh ${SIDEBAR_LEFT_PADDING}vw`};

showcase/src/content/AnimatedBulb.js renamed to showcase/src/content/useAnimatedBulb.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState, useLayoutEffect } from 'react'
1+
import { useState, useLayoutEffect } from 'react'
22
import mojs from 'mo-js'
33

44
export const useAnimatedBulb = ({ el }) => {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { useState, useLayoutEffect } from 'react'
2+
import mojs from 'mo-js'
3+
4+
export const useAnimatedInfo = ({ bgEl, textEl }) => {
5+
const [animationTimeline, setAnimationTimeline] = useState(
6+
new mojs.Timeline()
7+
)
8+
9+
useLayoutEffect(() => {
10+
if (!bgEl) {
11+
return
12+
}
13+
14+
const shiftCurve = mojs.easing.path(
15+
'M0,100 C50,100 50,100 50,50 C50,0 50,0 100,0'
16+
)
17+
const scaleCurveBase = mojs.easing.path(
18+
'M0,100 C21.3776817,95.8051376 50,77.3262711 50,-700 C50,80.1708527 76.6222458,93.9449005 100,100'
19+
)
20+
const scaleCurve = p => {
21+
return 1 + scaleCurveBase(p)
22+
}
23+
const nScaleCurve = p => {
24+
return 1 - scaleCurveBase(p) / 10
25+
}
26+
27+
const bgAnimate = new mojs.Html({
28+
el: bgEl,
29+
opacity: { 0: 1 },
30+
duration: 350
31+
})
32+
33+
const movingRect = new mojs.Shape({
34+
parent: bgEl,
35+
shape: 'rect',
36+
fill: { '#F64040': '#F64040', curve: scaleCurve },
37+
radius: 10,
38+
rx: 3,
39+
x: { [-125]: 600, easing: shiftCurve },
40+
y: 50,
41+
scaleX: { 1: 1, curve: scaleCurve },
42+
scaleY: { 1: 1, curve: nScaleCurve },
43+
origin: { '0 50%': '100% 50%', easing: shiftCurve },
44+
duration: 1000,
45+
isShowEnd: false
46+
})
47+
48+
const burst = new mojs.Burst({
49+
parent: bgEl,
50+
radius: { 0: 100 },
51+
x: { '50%': '90%' },
52+
count: 5,
53+
children: {
54+
shape: 'polygon',
55+
fill: { cyan: 'yellow' },
56+
radius: 20,
57+
angle: { 360: 0 },
58+
duration: 300
59+
}
60+
})
61+
62+
const updatedAnimationTimeline = animationTimeline.add([
63+
bgAnimate,
64+
movingRect,
65+
burst
66+
])
67+
68+
setAnimationTimeline(updatedAnimationTimeline)
69+
}, [bgEl, animationTimeline])
70+
71+
return animationTimeline
72+
}

0 commit comments

Comments
 (0)