Reputation: 4783
I'm trying to rotate an image using React Native. I have managed to do this as shown in the example below, but I would prefer that the arrow spins to the new bearing using the shorter route (i.e. clockwise in this case instead of counterclockwise).
If the current bearingAnim
value is 315
and the next value is 45
the rotation will transition backwards, rather than going from 315 => 45
.
How can I adjust the code to use the shortest rotation?
const bearing = this.bearingAnim.interpolate({
inputRange: [0, 360],
outputRange: ['0deg', '360deg'],
});
return <Animated.Image style={{ transform: [{ rotate: bearing }] }} source={markerGreen} />;
Upvotes: 1
Views: 1271
Reputation: 15530
As each direction may be set as a positive or negative angle (clockwise/counter clockwise rotation), e.g. 315 (clockwise) is -45 (counter clockwise), you may compare the absolute values of a delta between current direction and new direction (both clockwise and counter clockwise) and set your new angle to the one having least absolute value of the delta:
const getNewDirectionangle = (direction, newDirection) => {
const delta = {
clockwise: (newDirection-direction)%360,
counterClockwise: (newDirection-direction-360)%360
}
return
Math.abs(delta.clockwise) < Math.abs(delta.counterClockwise) ?
direction + delta.clockwise :
direction + delta.counterClockwise
}
Following is a React.js live-demo to illustrate the concept:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const Pointer = ({rotationAngle}) => {
return (
<div className="map">
<div
className="pointer"
style={{transform: `rotate(${rotationAngle}deg)`}}
/>
</div>
)
}
const Controls = ({onRotate}) => {
const [azimuth, setAzimuth] = useState(),
onInput = ({target:{value}}) => setAzimuth(value%360)
return (
<form>
<input type="number" onChange={onInput} />
<input type="button" value="Rotate" onClick={() => onRotate(azimuth)} />
</form>
)
}
const App = () => {
const [direction, setDirection] = useState(0),
onRotate = newDirection => {
const delta = {
clockwise: (newDirection-direction)%360,
counterClockwise: (newDirection-direction-360)%360
}
setDirection(
Math.abs(delta.clockwise) < Math.abs(delta.counterClockwise) ?
direction + delta.clockwise :
direction + delta.counterClockwise
)
}
return (
<div className="wrapper">
<Pointer rotationAngle={direction} />
<Controls {...{onRotate}}/>
</div>
)
}
render (
<App />,
rootNode
)
.wrapper {
width: 250px;
display: flex;
flex-direction: column;
align-items: center;
}
.map {
width: 100%;
height: 120px;
display: flex;
flex-direction: row;
justify-content: center;
background-image: url("https://i.sstatic.net/3yuKI.png");
background-size: cover;
margin: 5px
}
.pointer {
transition: transform 1s ease-in;
transform-origin: 50% 58%;
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMS43NTIgMjYuMjU2Ij48ZGVmcyBpZD0iZGVmczUiPjxjbGlwUGF0aCBpZD0iYSIgY2xpcFBhdGhVbml0cz0idXNlclNwYWNlT25Vc2UiPjxwYXRoIGQ9Ik0xNi4zMy0uNDhoODMuNzM0djEwOS4zNUgxNi4zM3oiIGlkPSJwYXRoMiIgLz48L2NsaXBQYXRoPjwvZGVmcz48cGF0aCBkPSJNMjguOTk5IDg3LjMzNmMtMTYuMDUtMTYuMDUtMTYuMDUtNDIuMDg3IDAtNTguMTM3QzM4LjY4NSAxOS41MTIgNDguMzcgOS44MjYgNTguMDc3LjE0TDg3LjEzNiAyOS4yYzE2LjA1IDE2LjA1IDE2LjA1IDQyLjA4NyAwIDU4LjEzNy0xNi4wNSAxNi4wNS00Mi4wODcgMTYuMDUtNTguMTM3IDB6IiBjbGlwLXBhdGg9InVybCgjYSkiIGZpbGw9IiM3MGFkNDciIGZpbGwtcnVsZT0iZXZlbm9kZCIgdHJhbnNmb3JtPSJtYXRyaXgoLjI2NDU4IDAgMCAuMjY0NTggLTQuNDg4IC0uMDM3KSIgaWQ9InBhdGg3IiAvPjxwYXRoIGQ9Ik0zLjA3NCAxNS4zODJjMC00LjI4OSAzLjQ5NS03Ljc3MyA3Ljc5NC03Ljc3MyA0LjMwNSAwIDcuNzk0IDMuNDg0IDcuNzk0IDcuNzczIDAgNC4yOTQtMy40ODkgNy43NzMtNy43OTQgNy43NzMtNC4zIDAtNy43OTQtMy40NzktNy43OTQtNy43NzN6IiBmaWxsPSIjZmZmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGlkPSJwYXRoOSIgLz48L3N2Zz4=");
background-repeat: no-repeat;
background-size: cover;
width: 50px;
height: 60px;
margin: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
Upvotes: 1
Reputation: 7965
I wrote you a function that calculates the correct angle according to the old direction
She needs to get the old and new angle It will return the direction you need to add to reach the new direction (If the arrow needs to move back it will return minus and it will move back)
function newRotate(oldV, newV) {
const diff1 = newV - oldV;
let myOldV = oldV;
if (newV < oldV) newV += 360;
else myOldV += 360;
const diff2 = newV - myOldV;
const shortDiff = Math.abs(diff1) < Math.abs(diff2) ? diff1 : diff2;
return oldV + shortDiff;
}
Here's how to use it
const value =newRotate(oldValue,newValue)
Animated.timing(routeAnimtion, {
toValue: value,
duration: 10,
}).start();
Upvotes: 1