Skip to content

Commit 51d5f2b

Browse files
committed
♻️ 💄 Animatios hook
1 parent 09fbf95 commit 51d5f2b

File tree

10 files changed

+178
-54
lines changed

10 files changed

+178
-54
lines changed

components/home/connection/Connection.jsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,33 @@
1+
/* eslint-disable @next/next/no-img-element */
2+
import { useRef } from 'react'
3+
import { getLiteral } from '../../../common/i18n'
14
import * as ROUTES from '../../../common/routes'
5+
import useInnerParallax from '../../../hooks/useInnerParallax'
26

37
import ButtonLink from '../../button-link/ButtonLink'
48

59
const Connection = ({ title, buttonText }) => {
10+
const imageRef = useRef(null)
11+
const backgroundRef = useRef(null)
12+
13+
const { translateY: imageTranslateY } = useInnerParallax(
14+
backgroundRef,
15+
imageRef,
16+
)
17+
618
return (
719
<div className="connection">
20+
<div className="connection__background" ref={backgroundRef}>
21+
<img
22+
className="connection__image"
23+
ref={imageRef}
24+
style={{
25+
transform: `scale(1.1) translateY(${imageTranslateY}%)`,
26+
}}
27+
src="/images/footer.jpg"
28+
alt={getLiteral('connection:image-description')}
29+
/>
30+
</div>
831
<div className="connection__content">
932
<h2 className="connection__title">{title}</h2>
1033

components/home/connection/connection.scss

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
position: relative;
44
z-index: $index-footer-image-content;
55

6-
background-image: linear-gradient(177.63deg, rgba(10, 3, 214, 0) 1.99%, rgba(107, 0, 207, 0.354) 64.04%, rgba(227, 46, 64, 0.6) 109.9%), url('/images/footer.jpg');
7-
background-position: center;
8-
background-size: cover;
9-
106
padding: spacing(5) spacing(2) spacing(5) spacing(5);
117
margin: 0 spacing(2);
128

@@ -28,13 +24,44 @@
2824
min-height: calc(100vh - $footer-height);
2925
}
3026

27+
&__background {
28+
position: absolute;
29+
width: 100%;
30+
height: 100%;
31+
32+
overflow: hidden;
33+
34+
&::after {
35+
content: '';
36+
37+
position: absolute;
38+
top: 0;
39+
left: 0;
40+
41+
height: 100%;
42+
width: 100%;
43+
44+
background-image: linear-gradient(177.63deg, rgba(10, 3, 214, 0) 1.99%, rgba(107, 0, 207, 0.354) 64.04%, rgba(227, 46, 64, 0.6) 109.9%);
45+
}
46+
}
47+
48+
&__image {
49+
height: 100%;
50+
width: 100%;
51+
object-fit: cover;
52+
53+
transform: scale(1.025);
54+
transform-origin: bottom center;
55+
}
56+
3157
&__content {
3258
@extend %grid;
3359
align-items: center;
3460

3561
}
3662

3763
&__title {
64+
position: relative;
3865
margin-bottom: spacing(4);
3966
grid-column: 1 / 5;
4067

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* eslint-disable @next/next/no-img-element */
2+
import { useRef } from 'react'
3+
4+
import useParallax from '../../../hooks/useParallax'
5+
6+
const ExampleCard = ({ image, imageDescription, title }) => {
7+
const ref = useRef(null)
8+
9+
const { translateY: backgroundTranslateY } = useParallax(ref, 0.02)
10+
11+
return (
12+
<div
13+
className="get-involved__example"
14+
ref={ref}
15+
style={{ transform: `translateY(-${backgroundTranslateY}px)` }}
16+
>
17+
<img className="get-involved__image" src={image} alt={imageDescription} />
18+
<p>{title}</p>
19+
</div>
20+
)
21+
}
22+
23+
export default ExampleCard

components/home/get-involved/GetInvolved.jsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable @next/next/no-img-element */
22
import md from 'markdown-it'
3+
import ExampleCard from './ExampleCard'
34

45
const GetInvolved = ({ title, examplesTitle, examples, content }) => {
56
return (
@@ -19,14 +20,12 @@ const GetInvolved = ({ title, examplesTitle, examples, content }) => {
1920

2021
<div className="get-involved__list">
2122
{examples.map(({ title, image, imageDescription }) => (
22-
<div key={`example-${title}`} className="get-involved__example">
23-
<img
24-
className="get-involved__image"
25-
src={image}
26-
alt={imageDescription}
27-
/>
28-
<p>{title}</p>
29-
</div>
23+
<ExampleCard
24+
key={`example-${title}`}
25+
image={image}
26+
imageDescription={imageDescription}
27+
title={title}
28+
/>
3029
))}
3130
</div>
3231
</div>

components/home/get-involved/get-involved.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@
142142
@extend %header-4;
143143
text-align: center;
144144

145+
transition: transform 1s $bezier-default;
146+
145147
&:not(:last-child) {
146148
margin-bottom: spacing(1);
147149
}

components/home/hero/Hero.jsx

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,22 @@
11
/* eslint-disable @next/next/no-img-element */
2-
import { useRef, useEffect, useState } from 'react'
2+
import { useRef } from 'react'
33

44
import ButtonLink from '../../button-link/ButtonLink'
55

66
import * as ROUTES from '../../../common/routes'
77
import { getLiteral } from '../../../common/i18n'
8+
import useParallax from '../../../hooks/useParallax'
9+
import useInnerParallax from '../../../hooks/useInnerParallax'
810

911
const Hero = ({ date, title, buttonText }) => {
1012
const imageRef = useRef(null)
1113
const backgroundRef = useRef(null)
1214

13-
const [imageTranslateY, setImageTraslateY] = useState(0)
14-
const [backgroundTranslateY, setBackgroundTraslateY] = useState(0)
15-
16-
useEffect(() => {
17-
const background = backgroundRef.current
18-
const image = imageRef.current
19-
20-
if (!background || !image) {
21-
return
22-
}
23-
24-
const handleScroll = () => {
25-
const callback = () => {
26-
const backgroundTop = background.getBoundingClientRect().top
27-
const backgroundBottom = background.getBoundingClientRect().bottom
28-
29-
if (backgroundTop < window.innerHeight && backgroundBottom > 0) {
30-
const distance = window.scrollY * 0.1
31-
32-
const scaleDistance = (
33-
((backgroundBottom * 100) / window.innerHeight) *
34-
0.025
35-
).toFixed(2)
36-
37-
setBackgroundTraslateY(distance)
38-
setImageTraslateY(scaleDistance)
39-
}
40-
}
41-
42-
requestAnimationFrame(callback)
43-
}
44-
45-
handleScroll()
46-
window.addEventListener('scroll', handleScroll)
47-
48-
return () => {
49-
window.removeEventListener('scroll', handleScroll)
50-
}
51-
}, [backgroundRef])
15+
const { translateY: backgroundTranslateY } = useParallax(backgroundRef)
16+
const { translateY: imageTranslateY } = useInnerParallax(
17+
backgroundRef,
18+
imageRef,
19+
)
5220

5321
return (
5422
<section className="hero">

components/home/hero/hero.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262

6363
transform: scale(1.025);
6464
transform-origin: bottom center;
65-
6665
}
6766

6867
&__content {

content/commons.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"anchor-nav:events": "Schedule",
88
"anchor-nav:connection": "Connection",
99

10+
"connection:image-description": "Connection illustration",
11+
1012
"event-type:podcast": "Podcast",
1113
"event-type:stream": "Stream",
1214
"event-type:talk": "Talk",
@@ -15,7 +17,6 @@
1517
"event-type:conference": "Conference",
1618
"event-type:misc": "Misc",
1719

18-
"footer:image-description": "Footer illustration",
1920
"footer:copyright": "Made with ♥︎️ by GitHub",
2021
"footer:privacy-title": "Privacy terms and conditions",
2122
"footer:privacy-url": "https://docs.github.com/es/site-policy/privacy-policies/github-privacy-statement",

hooks/useInnerParallax.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { useEffect, useState } from 'react'
2+
3+
const useInnerParallax = (elementRef, wrapperRef, factor = 0.025) => {
4+
const [translateY, setImageTraslateY] = useState(0)
5+
6+
useEffect(() => {
7+
const background = elementRef.current
8+
const image = wrapperRef.current
9+
10+
if (!background || !image) {
11+
return
12+
}
13+
14+
const handleScroll = () => {
15+
const callback = () => {
16+
const top = background.getBoundingClientRect().top
17+
const bottom = background.getBoundingClientRect().bottom
18+
19+
if (top < window.innerHeight && bottom > 0) {
20+
const scaleDistance = (
21+
((bottom * 100) / window.innerHeight) *
22+
factor
23+
).toFixed(2)
24+
25+
setImageTraslateY(scaleDistance)
26+
}
27+
}
28+
29+
requestAnimationFrame(callback)
30+
}
31+
32+
handleScroll()
33+
window.addEventListener('scroll', handleScroll)
34+
35+
return () => {
36+
window.removeEventListener('scroll', handleScroll)
37+
}
38+
}, [elementRef, factor, wrapperRef])
39+
40+
return { translateY }
41+
}
42+
43+
export default useInnerParallax

hooks/useParallax.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { useEffect, useState } from 'react'
2+
3+
const useParallax = (ref, factor = 0.1) => {
4+
const [translateY, setTranslateY] = useState(0)
5+
6+
useEffect(() => {
7+
const background = ref.current
8+
9+
if (!background) {
10+
return
11+
}
12+
13+
const handleScroll = () => {
14+
const callback = () => {
15+
const top = background.getBoundingClientRect().top
16+
const bottom = background.getBoundingClientRect().bottom
17+
18+
if (top < window.innerHeight && bottom > 0) {
19+
const distance = window.scrollY * factor
20+
21+
setTranslateY(distance)
22+
}
23+
}
24+
25+
requestAnimationFrame(callback)
26+
}
27+
28+
handleScroll()
29+
window.addEventListener('scroll', handleScroll)
30+
31+
return () => {
32+
window.removeEventListener('scroll', handleScroll)
33+
}
34+
}, [factor, ref])
35+
36+
return { translateY }
37+
}
38+
39+
export default useParallax

0 commit comments

Comments
 (0)