Skip to content

Commit cf39a25

Browse files
committed
started hooks update
Started converting the functions and state to hooks, overcoming some hurdles as the hooks version will be very different.
1 parent e7a5eab commit cf39a25

File tree

1 file changed

+192
-23
lines changed

1 file changed

+192
-23
lines changed

src/carousel.jsx

Lines changed: 192 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,133 @@
1-
import React from "react";
1+
import React, { useState, useRef, useEffect, useReducer } from "react";
22
import "./styles.css";
33

4-
export default class Carousel extends React.Component {
4+
//A hook for getting the previous state/a previous value, if needed
5+
const usePrevious = value => {
6+
const ref = useRef();
7+
useEffect(() => {
8+
ref.current = value;
9+
});
10+
return ref.current;
11+
};
12+
13+
const useWidth = ref => {
14+
console.log("called useWidth");
15+
const [width, setWidth] = useState(0);
16+
useEffect(() => {
17+
setWidth(ref.current.getBoundingClientRect().width);
18+
}, [ref]);
19+
};
20+
21+
const telemetryReducer = (state, action) => {
22+
switch (action.type) {
23+
case "user_move":
24+
return {
25+
angle: action.angle,
26+
change: action.change,
27+
velocity: action.velocity
28+
};
29+
case "decay_move":
30+
return {
31+
...state,
32+
angle: action.angle,
33+
velocity: action.velocity
34+
};
35+
case "reset_change":
36+
return {
37+
...state,
38+
change: 0
39+
};
40+
case "reset_velocity":
41+
return { ...state, velocity: 0 };
42+
43+
default:
44+
return state;
45+
}
46+
};
47+
48+
const Carousel2 = () => {
49+
const [dragState, setDragState] = useState({
50+
dragging: false,
51+
dragStartX: 0,
52+
dragStartTime: 0
53+
});
54+
const [carouselTelemetry, dispatch] = useReducer(telemetryReducer, {
55+
angle: 0,
56+
change: 0,
57+
velocity: 0
58+
});
59+
const [decayInterval, setDecayInterval] = useState(null);
60+
const draggableRef = useRef();
61+
const width = useWidth(draggableRef);
62+
63+
useEffect(() => {
64+
window.addEventListener("mouseup", onDragEndMouse);
65+
window.addEventListener("touchend", onDragEndTouch);
66+
67+
return () => {
68+
window.removeEventListener("mouseup", onDragEndMouse);
69+
window.removeEventListener("touchend", onDragEndTouch);
70+
};
71+
}, []);
72+
73+
const onDragEndMouse = e => {
74+
window.removeEventListener("mousemove", this.onMouseMove);
75+
onDragEnd();
76+
};
77+
78+
const onDragEndTouch = e => {
79+
window.removeEventListener("touchmove", this.onTouchMove);
80+
onDragEnd();
81+
};
82+
83+
const onDragStartMouse = e => {
84+
onDragStart(e.clientX);
85+
window.addEventListener("mousemove", this.onMouseMove);
86+
};
87+
88+
const onDragStartTouch = e => {
89+
const touch = e.targetTouches[0];
90+
onDragStart(touch.clientX);
91+
window.addEventListener("touchmove", this.onTouchMove);
92+
};
93+
94+
const onDragStart = clientX => {
95+
if (decayInterval !== null) {
96+
clearInterval(decayInterval);
97+
}
98+
setDragState({ dragStartX: clientX, dragging: true, velocity: 0 });
99+
requestAnimationFrame(updatePosition);
100+
};
101+
102+
const onDragEnd = () => {
103+
setDragState(state => ({ ...state, change: 0, dragging: false }));
104+
setDecayInterval(state =>
105+
state === null ? setInterval(animateSliding, 66) : null
106+
);
107+
};
108+
109+
return (
110+
<div>
111+
<h1>Spin the text below!</h1>
112+
<div
113+
ref={this.draggableRef}
114+
onMouseDown={onDragStartMouse}
115+
onTouchStart={onDragStartTouch}
116+
className="spinText"
117+
>
118+
touch move me wherever necessary
119+
</div>
120+
<br />
121+
<br />
122+
<br />
123+
<span>
124+
{`pos: ${carouselTelemetry.angle} velo:${carouselTelemetry.velocity}`}
125+
</span>
126+
</div>
127+
);
128+
};
129+
130+
class Carousel extends React.Component {
5131
constructor(props) {
6132
super(props);
7133

@@ -13,8 +139,9 @@ export default class Carousel extends React.Component {
13139
dragStartTime: 0,
14140
carouselPosition: 0, //This can be a value between 0 - 360,
15141
change: 0,
142+
velocity: 0,
16143
width: 0,
17-
velocity: 0
144+
intervalId: null
18145
};
19146

20147
this.onDragStartMouse = this.onDragStartMouse.bind(this);
@@ -26,6 +153,7 @@ export default class Carousel extends React.Component {
26153
this.onDragStart = this.onDragStart.bind(this);
27154
this.onDragEndMouse = this.onDragEndMouse.bind(this);
28155
this.onDragEndTouch = this.onDragEndTouch.bind(this);
156+
this.animateSliding = this.animateSliding.bind(this);
29157
}
30158

31159
componentDidMount() {
@@ -42,14 +170,22 @@ export default class Carousel extends React.Component {
42170
}
43171

44172
onDragStart(clientX) {
45-
this.setState(() => ({ dragStartX: clientX, dragging: true }));
173+
if (this.state.intervalId !== null) {
174+
clearInterval(this.state.intervalId);
175+
}
176+
this.setState(() => ({ dragStartX: clientX, dragging: true, velocity: 0 }));
46177
requestAnimationFrame(this.updatePosition);
47178
}
48179

49180
onDragEnd() {
50-
if (this.state.dragging) {
51-
this.setState(() => ({ dragging: false, change: 0 }));
52-
}
181+
this.setState(prevState => ({
182+
dragging: false,
183+
change: 0,
184+
intervalId:
185+
prevState.intervalId === null
186+
? setInterval(this.animateSliding, 66)
187+
: null
188+
}));
53189
}
54190

55191
updatePosition() {
@@ -61,8 +197,10 @@ export default class Carousel extends React.Component {
61197
const now = Date.now();
62198
const elapsed = now - this.state.dragStartTime;
63199

64-
if (this.state.dragging && elapsed > 20) {
65-
console.log("carouselPos: ", this.state.carouselPosition);
200+
if (
201+
(this.state.dragging && elapsed > 20) ||
202+
this.state.intervalId !== null
203+
) {
66204
this.draggableRef.current.style.transform = `rotate3d(0,1,0,${
67205
this.state.carouselPosition
68206
}deg)`;
@@ -79,7 +217,10 @@ export default class Carousel extends React.Component {
79217
change - prevState.change,
80218
prevState.carouselPosition,
81219
prevState.width
82-
)
220+
),
221+
velocity:
222+
((change - prevState.change) / (Date.now() - prevState.dragStartTime)) *
223+
20
83224
}));
84225
}
85226

@@ -92,7 +233,10 @@ export default class Carousel extends React.Component {
92233
change - prevState.change,
93234
prevState.carouselPosition,
94235
prevState.width
95-
)
236+
),
237+
velocity:
238+
((change - prevState.change) / (Date.now() - prevState.dragStartTime)) *
239+
20
96240
}));
97241
}
98242

@@ -118,30 +262,51 @@ export default class Carousel extends React.Component {
118262
}
119263

120264
calculateCarouselPosition(currentMovement, previousCarouselState, width) {
121-
//current movement is something like +40 or -20 or whatever, depending on left or right movement
122-
// previouCarousel is something between 1 & 360, so we need to map changes to that, we may need to factor in the
123-
// component width when mapping the currentMovement to the carousel change
265+
//1) Here we take the distance spun since our last frame checked
266+
//2) We don't want to let a user spin more than the width of our element so that
267+
//a full spin is the start to the finish of the element in dragging distance,
268+
//so we min/max it.
269+
//3) We map the distance moved, relative to the elements with to 360 degrees to see
270+
//how much of the "carousel" we've spun
271+
//4) We don't ever want to be spinning more than 360 degrees or less than 0
124272
const cappedMovement =
125273
currentMovement > 0
126274
? Math.min(currentMovement, width)
127275
: Math.max(currentMovement, -width);
128-
const degreesMoved = (cappedMovement / width) * 360 + previousCarouselState; //may be -30 degrees or + 10 degrees? who knows
129-
//which percentage of the current div have we moved? and how many degrees is that for our 360 carousel
130-
//If mappedCarouselMovement + previousCarousel state is > 360, then we take the amount we go over by and instead add that to 0,
131-
//so we always have a number between 0 and 360. Same for < 0, if its -30 after the calculation, then we take that different and
132-
//subtract it from 360
276+
const degreesMoved = (cappedMovement / width) * 360 + previousCarouselState;
133277
const currentCarouselDegrees =
134278
degreesMoved > 360
135279
? degreesMoved - 360
136280
: degreesMoved < 0
137281
? degreesMoved + 360
138282
: degreesMoved;
139283

140-
return currentCarouselDegrees;
284+
return Math.round(currentCarouselDegrees);
285+
}
286+
287+
animateSliding() {
288+
const { velocity, dragging } = this.state;
289+
if (!dragging && Math.abs(velocity) > 0) {
290+
this.setState(prevState => ({
291+
carouselPosition: this.calculateCarouselPosition(
292+
velocity,
293+
prevState.carouselPosition,
294+
prevState.width
295+
),
296+
velocity:
297+
Math.abs(prevState.velocity / 1.5) < 0.3
298+
? 0
299+
: prevState.velocity / 1.5
300+
}));
301+
requestAnimationFrame(this.updatePosition);
302+
} else {
303+
clearInterval(this.state.intervalId);
304+
this.setState(() => ({ intervalId: null }));
305+
}
141306
}
142307

143308
render() {
144-
const { change, carouselPosition } = this.state;
309+
const { carouselPosition, velocity } = this.state;
145310

146311
return (
147312
<div>
@@ -154,9 +319,13 @@ export default class Carousel extends React.Component {
154319
>
155320
touch move me wherever necessary
156321
</div>
157-
{/* <Carousel360 imageNumber={carouselPosition} /> */}
158-
{carouselPosition}
322+
<br />
323+
<br />
324+
<br />
325+
{`pos: ${carouselPosition} velo:${velocity}`}
159326
</div>
160327
);
161328
}
162329
}
330+
331+
export default Carousel;

0 commit comments

Comments
 (0)