如何在 React Native 中使用最短路径旋转图像

How to rotate an image using the shortest route in React Native

我正在尝试使用 React Native 旋转图像。我已经设法做到这一点,如下例所示,但我更希望箭头使用较短的路线旋转到新方位(即在这种情况下顺时针而不是逆时针)。

如果当前 bearingAnim 值为 315 且下一个值为 45 则旋转将向后过渡,而不是从 315 => 45.

如何调整代码以使用最短旋转?

const bearing = this.bearingAnim.interpolate({
  inputRange: [0, 360],
  outputRange: ['0deg', '360deg'],
});

return <Animated.Image style={{ transform: [{ rotate: bearing }] }} source={markerGreen} />;

因为每个方向都可以设置为正角或负角(clockwise/counter顺时针旋转),例如315(顺时针)是 -45(逆时针),您可以比较当前方向和新方向(顺时针和逆时针)之间的增量绝对值,并将新角度设置为增量绝对值最小的角度:

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
          
      }

以下是一个 React.js 现场演示来说明这个概念:

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.stack.imgur.com/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>

我给你写了一个函数,可以根据旧方向计算出正确的角度

她需要获得新旧角度 它将return您需要添加的方向以达到新的方向 (如果箭头需要向后移动它会 return 减去它会向后移动)


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;
}

这是使用方法

const value =newRotate(oldValue,newValue)

 Animated.timing(routeAnimtion, {
      toValue: value,
      duration: 10,
    }).start();