简体   繁体   English

用绝对位置理解 React Native 中的 CSS 三角形和菱形交互

[英]Understanding CSS triangles and rhombus interactions in React native with absolute positions

I have a complex structure diagram with Rhombuses and triangles.我有一个带有菱形和三角形的复杂结构图。 I was able to create the structure using CSS tricks and absolute position property.我能够使用 CSS 技巧和绝对 position 属性创建结构。

I want each rhombus and triangle to be clickable but the behavior is not as expected given how CSS triangles work with the borders.我希望每个菱形和三角形都是可点击的,但考虑到 CSS 三角形如何与边界一起工作,行为并不像预期的那样。

An output image with some markers In this image we have a rhombus and two triangles,带有一些标记的 output 图像在此图像中,我们有一个菱形和两个三角形,

What I want:我想要的是:

  • On clicking at A,B,E - rhombus is clicked点击 A,B,E - 菱形被点击
  • On clicking C,D - first triangle (numbered 2 in picture) is clicked.单击 C,D - 单击第一个三角形(图中编号为 2)。

What happens instead:相反会发生什么:

  • On clicking A, nothing happens (I suspect the absolute and transform property here)单击 A 时,什么也没有发生(我怀疑绝对和变换属性在这里)
  • On clicking B, first triangle (numbered 2 in picture) is clicked.单击 B 时,单击第一个三角形(图中编号为 2)。 It is because of the transparent right border of first triangle.这是因为第一个三角形的透明右边框。
  • On clicking C, second triangle (numbered 3 in picture) is clicked.单击 C 时,会单击第二个三角形(图中编号为 3)。 It is because of the transparent top border of second triangle.这是因为第二个三角形的透明顶部边框。
  • On clicking D, first triangle is clicked which is the expected behaviour.单击 D 时,单击第一个三角形,这是预期的行为。
  • On clicking E, rhombus is clicked which is the expected behaviour.单击 E 时,会单击菱形,这是预期的行为。

How can I make such complex interactive patterns with series of such shapes?如何用一系列这样的形状制作如此复杂的交互模式? Shall I consider using images/SVGs instead of CSS shapes?我应该考虑使用图像/SVG 代替 CSS 形状吗? I also need to add multiple dynamic count of numbers/text/emojis in each shape.我还需要在每个形状中添加多个数字/文本/表情符号的动态计数。

Here's a simplified snippet of code:这是一个简化的代码片段:

import React from 'react';
import {
  View,
  TouchableOpacity,
  StyleSheet,
  SafeAreaView,
} from 'react-native';

const MyComplexDiagram = ({navigation, route}) => {

  const clickShape = text => {
    console.log('clicked: ', text);
  };
  return (
    <SafeAreaView style={{flex: 1}}>
      <View>
        <TouchableOpacity onPress={() => clickShape("rhombus)}>
          <View style={[styles.rhombus]} />
        </TouchableOpacity>

        <TouchableOpacity onPress={() => clickShape("first triangle")}>
          <View style={[styles.firstTriangle]} />
        </TouchableOpacity>
        
        <TouchableOpacity onPress={() => clickShape("second triangle")}>
          <View style={[styles.secondTriangle]} />
        </TouchableOpacity>
    </SafeAreaView>
  );
};

export default MyComplexShape;

const styles = StyleSheet.create({
  rhombus: {
    position: 'absolute',
    top: 50,
    left: 100,
    width: 100,
    height: 100,
    backgroundColor: 'green',
    transform: [{rotate: '45deg'}],
  },
  firstTriangle: {
    position: 'absolute',
    top: 0,
    left: 0,
    backgroundColor: 'transparent',
    borderStyle: 'solid',
    borderLeftWidth: 50,
    borderRightWidth: 50,
    borderTopWidth: 50,
    borderLeftColor: 'transparent',
    borderRightColor: 'transparent',
    borderTopColor: 'red',
  }),
  secondTriangle: {
    position: 'absolute',
    top: 0,
    left: 0,
    backgroundColor: 'transparent',
    borderStyle: 'solid',
    borderLeftWidth: 50 * Math.sqrt(2),
    borderTopWidth: 50 * Math.sqrt(2),
    borderBottomWidth: 50 * Math.sqrt(2),
    borderLeftColor: 'black',
    borderBottomColor: 'transparent',
    borderTopColor: 'transparent',
  }
});

Im not sure if the same thing happens in a browser context, but the issue with the css trick is that although the view appears to be a triangle, it is actually still a rectangle.我不确定在浏览器上下文中是否会发生同样的事情,但 css 技巧的问题在于,虽然视图看起来是三角形,但实际上仍然是矩形。 Giving the triangles a non-transparent background color will demonstrate this.给三角形一个不透明的背景颜色将证明这一点。 The middle triangle is intercepting the first triangle presses because its hidden rectangle entirely overlaps the first triangle.中间的三角形拦截了第一个三角形,因为它的隐藏矩形与第一个三角形完全重叠。 (Here's a demo showing that). (这里有一个演示显示)。 You can overcome this by the other TouchableOpacities a higher zIndex than the middle one:你可以通过其他的 TouchableOpacities 来克服这个问题,它的zIndex比中间的高:

import React, { useMemo, useEffect, useRef, useState } from 'react';
import {
  View,
  TouchableOpacity,
  StyleSheet,
  SafeAreaView,
  useWindowDimensions,
  Button,
  Text
} from 'react-native';

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  withSequence,
  withRepeat,
  cancelAnimation,
  withDelay,
} from 'react-native-reanimated';
const getHypotenuse = (side1, side2) => {
  if (!side2) side2 = side1;
  return Math.sqrt(side1 ** 2 + side2 ** 2);
};

const startRotation = 270;
const MyComplexDiagram = ({ navigation, route }) => {
  const [animationIsRunning, setAnimationIsRunning] = useState(false);
  const rotation = useSharedValue(startRotation);
  const backgroundColor = useSharedValue('rgba(0,0,0,0)');
  const { width, height } = useWindowDimensions();
  const rotationStyle = useAnimatedStyle(() => {
    return {
      transform: [{ rotate: `${rotation.value}deg` }],
    };
  });
  const colorStyle = useAnimatedStyle(() => {
    return {
      backgroundColor: backgroundColor.value,
    };
  });
  const { rhombusStyle, middleTriangleStyle, leftTriangleStyle } =
    useMemo(() => {
      const remainingWidth =
        width - styles.container.padding - styles.shapeContainer.padding;
      const rhombusSize = remainingWidth * 0.3;
      const rhombusHypotenuse = getHypotenuse(rhombusSize);
      const rhombusLeft = remainingWidth - rhombusHypotenuse;
      const rhombusTop = 25;
      const middleLeft = rhombusLeft - rhombusSize;
      const leftLeft = middleLeft;
      return {
        rhombusStyle: {
          width: rhombusSize,
          height: rhombusSize,
          left: rhombusLeft,
          top: rhombusTop,
        },
        middleTriangleStyle: {
          borderWidth: rhombusSize * 0.8,
          borderLeftWidth: rhombusSize * 0.8,
          left: rhombusLeft - rhombusSize * 1.1,
          top: 10,
        },
        leftTriangleStyle: {
          borderWidth: rhombusSize,
          borderBottom: rhombusSize,
          left: rhombusLeft - rhombusHypotenuse * 1.7,
          top: 0,
        },
      };
    }, [width]);
  const clickShape = (text) => {
    console.log('clicked: ', text);
  };
  const toggleAnimation = () => {
    if (animationIsRunning) {
      backgroundColor.value = withTiming('rgba(0,0,0,0)');
    } else {
      backgroundColor.value = withRepeat(
        withSequence(
          withTiming('rgba(255,0,0,1)', { duration: 2000 }),
          withTiming('rgba(0,0,0,0'),
          withDelay(4000,withTiming('rgba(0,0,0,0'))
        ),
        -1
      );
    }
    setAnimationIsRunning((prev) => !prev);
  };

  return (
    <SafeAreaView style={styles.container}>
      <Button
        title={`${animationIsRunning ? 'Hide' : 'Show'} Triangle Background`}
        onPress={toggleAnimation}
      />
      <Text style={{zIndex:5}}>{animationIsRunning ? 'Using zIndex to force black triangle to be on top of orange':'Default behavior without zIndex changes'}</Text>
      <View style={styles.shapeContainer}>
        <TouchableOpacity
          style={animationIsRunning && { zIndex: 5 }}
          onPress={() => clickShape('leftTriangle')}>
          <Animated.View
            style={[styles.leftTriangle, leftTriangleStyle, colorStyle]}
          />
        </TouchableOpacity>
        <TouchableOpacity
          style={animationIsRunning && { zIndex: 4 }}
          onPress={() => clickShape('middleTriangle')}>
          <Animated.View
            style={[
              styles.middleTriangle,
              middleTriangleStyle,
              rotationStyle,
              colorStyle,
            ]}
          />
        </TouchableOpacity>
        <TouchableOpacity
          style={animationIsRunning && { zIndex: 5 }}
          onPress={() => clickShape('rhombus')}>
          <Animated.View style={[styles.rhombus, rhombusStyle]} />
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

export default MyComplexDiagram;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    // alignItems:'center',
    justifyContent: 'center',
    padding: 8,
  },
  shapeContainer: {
    padding: 10,
  },
  rhombus: {
    transform: [{ rotate: '45deg' }],
    position: 'absolute',
    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    backgroundColor: 'green',
  },
  middleTriangle: {
    position: 'absolute',
    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    width: 0,
    height: 0,
    borderColor: 'transparent',
    borderRightColor: 'orange',
  },
  leftTriangle: {
    position: 'absolute',
    transform: [{ rotate: '225deg' }],

    top: 0,
    bottom: 0,
    right: 0,
    left: 0,
    width: 0,
    height: 0,
    borderColor: 'transparent',
    borderLeftColor: 'black',
  },
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM