首页 > 解决方案 > d3 动画 React Native 代码在 IOS 中很快而在 Android 中很慢?

问题描述

我有一个地球仪,我想在 x 轴和 y 轴上旋转并使用 reanimated 2 放大和缩小捏合,这是代码:

const Globe = () => {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const scale = useSharedValue(1);
  const focalX = useSharedValue(0);
  const focalY = useSharedValue(0);

  const mapExtent = SVG_WIDTH > SVG_HEIGHT ? SVG_HEIGHT : SVG_WIDTH;

  const onGestureEvent = useAnimatedGestureHandler<
    PanGestureHandlerGestureEvent,
    {
      x: number;
      y: number;
    }
  >({
    onStart: (_, ctx) => {
      ctx.x = translateX.value;
      ctx.y = translateY.value;
    },
    onActive: ({translationX, translationY}, ctx) => {
      translateX.value = ctx.x + translationX;
      translateY.value = ctx.y + translationY;
    },
  });

  const onPinchGesture = useAnimatedGestureHandler<
    PinchGestureHandlerGestureEvent,
    {scale: number}
  >({
    onStart: (_, ctx) => {
      ctx.scale = scale.value;
    },
    onActive: (event, ctx) => {
      scale.value = event.scale;
      focalX.value = event.focalX;
      focalY.value = event.focalY;
    },
  });

  const style = useAnimatedStyle(() => {
    return {
      transform: [
        {translateX: focalX.value},
        {translateY: focalY.value},
        {translateX: -SVG_WIDTH / 2},
        {translateY: -SVG_HEIGHT / 2},
        {scale: scale.value},
        {translateX: -focalX.value},
        {translateY: -focalY.value},
        {translateX: SVG_WIDTH / 2},
        {translateY: SVG_HEIGHT / 2},
      ],
    };
  });

  useAnimatedReaction(
    () => ({x: translateX.value, y: translateY.value}),
    ({x, y}) => {
      requestAnimationFrame(() => {
        runOnJS(setX)(x);
        runOnJS(setY)(y);
      });
    },
    [translateX, translateY],
  );

  return (
    <PinchGestureHandler onGestureEvent={onPinchGesture}>
      <Animated.View>
        <PanGestureHandler onGestureEvent={onGestureEvent}>
          <Animated.View style={style}>
            <Svg width={SVG_WIDTH} height={SVG_HEIGHT}>
              <G>
                <Circle
                  cx={SVG_WIDTH / 2}
                  cy={mapExtent / 2}
                  r={mapExtent / 2}
                  fill={'#0099FF'}
                />
                <GlobePath x={x} y={y} />
                <GlobeCircles x={x} y={y} />
              </G>
            </Svg>
          </Animated.View>
        </PanGestureHandler>
      </Animated.View>
    </PinchGestureHandler>
  );
};

我所做的是基于 x 和 y,我在 GlobePath 和 GlobeCircles 上像这样更新 d3 投影:

const GlobePath = ({x, y}: {x: number; y: number}) => {
  const projection = d3
    .geoOrthographic()
    .rotate([x, -y])
    .scale(mapExtent / 2)
    .clipAngle(clipAngle)
    .translate([SVG_WIDTH / 2, mapExtent / 2]);

  const geoPath = d3.geoPath().projection(projection);
  return (
    <Path
      d={geoPath(COUNTRIES) as string}
      stroke={'#666666'}
      strokeOpacity={0.3}
      strokeWidth={0.6}
      fill={'#DDDDDD'}
      opacity={1}
    />
  );
};

const GlobeCircles = ({x, y}: {x: number; y: number}) => {
  //first should be longitude
  const coordinates = [
    [19.744822, -34.633016],
    [80.229349, 9.818078],
    [-98.862052, 37.848794],
  ];
  const projection = d3
    .geoOrthographic()
    .rotate([x, -y])
    .scale(mapExtent / 2)
    .clipAngle(clipAngle)
    .translate([SVG_WIDTH / 2, mapExtent / 2]);

  return (
    <G>
      {coordinates.map((data, i) => {
        //calculate xy position from coordinates (long,lat)
        const lang = data[0];
        const lat = data[1];
        const coordinate = projection([lang, lat]);

        const xdata = (coordinate as [number, number])[0]; //first should be longitude
        const ydata = (coordinate as [number, number])[1];

        //if marker out of circle or back side marker will hide

        const centerPos = projection.invert
          ? projection.invert([SVG_WIDTH / 2, mapExtent / 2])
          : [0, 0];

        const d = d3.geoDistance([lang, lat], centerPos as [number, number]);

        const opacity = d > 1.57 ? 0 : 1;

        return (
          <Circle
            key={`circle:${i}`}
            cx={xdata}
            cy={ydata}
            r={6}
            fillOpacity={0.5}
            opacity={opacity}
            fill="red"
          />
        );
      })}
    </G>
  );
};

现在我不太确定为什么我在 android 上的动画很慢,但在 ios 上却很快?代码看起来很简单,只是 x 和 y 发生了变化,而且捏放大和缩小在 android 上不起作用。帮助?

在此处输入图像描述

标签: reactjsreact-natived3.js

解决方案


推荐阅读