Reputation: 437
I have a set of absolute positioned circles that I want to curve/bend. So far I managed to curve them based on their index but result is slightly different from what I expected. Central circles go up/down too far and thus effect of bending is abrupt.
function App() {
const [parentHeight, setParentHeight] = React.useState(200);
const [parentWidth, setParentWidth] = React.useState(300);
const [curve, setCurve] = React.useState(0);
const childDiameter = 40;
const rows = [
{ position: 0, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] },
{ position: 1, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] },
{ position: 2, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] },
{ position: 3, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }
];
const getCurve = (idx, length) => {
return curve / (Math.abs(idx - (length - 1 - idx)) / 8);
};
return (
<div>
<div
style={{
position: "absolute",
width: `${parentWidth}px`,
height: `${parentHeight}px`,
top: 100,
left: 100,
boxSizing: "border-box",
display: "flex",
flexWrap: "wrap"
}}
>
{rows.map((row, rowIdx) => (
<div>
{row.seats.map((seat, idx) => (
<div
style={{
position: "absolute",
left: idx * (childDiameter + 10),
top:
rowIdx * (childDiameter + 10) +
getCurve(idx, row.seats.length),
backgroundColor: "grey",
border: "3px solid grey",
borderRadius: "50%",
width: childDiameter,
height: childDiameter
}}
>
{idx}
</div>
))}
</div>
))}
</div>
<input
id="typeinp"
type="range"
min="-200"
max="200"
value={curve}
onChange={evt => setCurve(parseInt(evt.target.value))}
step="1"
/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I know that I need to compensate somehow this shifting when I go to the center, but I can't figure out how. So how can I achive more smooth bending? Thank you.
Upvotes: 1
Views: 59
Reputation: 272806
This is a pure math issue. You are using this formula curve / (Math.abs(idx - (length - 1 - idx)) / 8)
which we can write in this case like this A / |2*x - 9|
(considering length
equal to 10
and A
equal to curve/8
)
If x
is close to infinity
then f(x)
will be close to 0
And if 2*x - 9
is close to 0
then then f(x)
will be close to infinity
and we have this for the middle values (4
or 5
).
Here is an illustration to better understand:
You can clearly see how the values are bigger when using 4
or 5
compared to the other thus the issue. Basically you are creating the above curve with your elements.
You need to use another formula to have a better cuvre and avoid the infinity
issue.
Below a better formula. I will not detail the math behind this choice but you can clearly notice that the polynom (X-5)²+X
doesn't have any solution in ℝ thus will never be equal to 0
(no infinity
for f(x)
) and it will create the needed curve for this purpose.
function App() {
const [parentHeight, setParentHeight] = React.useState(200);
const [parentWidth, setParentWidth] = React.useState(300);
const [curve, setCurve] = React.useState(0);
const childDiameter = 40;
const rows = [
{ position: 0, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] },
{ position: 1, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] },
{ position: 2, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] },
{ position: 3, seats: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] }
];
const getCurve = (idx, length) => {
return curve / ((idx - (length/2))*(idx - length/2) + idx);
};
return (
<div>
<div
style={{
position: "absolute",
width: `${parentWidth}px`,
height: `${parentHeight}px`,
top: 100,
left: 100,
boxSizing: "border-box",
display: "flex",
flexWrap: "wrap"
}}
>
{rows.map((row, rowIdx) => (
<div>
{row.seats.map((seat, idx) => (
<div
style={{
position: "absolute",
left: idx * (childDiameter + 10),
top:
rowIdx * (childDiameter + 10) +
getCurve(idx, row.seats.length),
backgroundColor: "grey",
border: "3px solid grey",
borderRadius: "50%",
width: childDiameter,
height: childDiameter
}}
>
{idx}
</div>
))}
</div>
))}
</div>
<input
id="typeinp"
type="range"
min="-200"
max="200"
value={curve}
onChange={evt => setCurve(parseInt(evt.target.value))}
step="1"
/>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 1