如何在React Native View中自动缩放SVG元素?

时间:2018-02-03 21:47:15

标签: react-native

我正在尝试将react-native-svg元素放在View内,以便以一定的固定宽高比渲染,但随后缩放到尽可能大的范围内包含视图的范围。

Svg元素(来自react-native-svg)似乎只接受绝对widthheight属性(我尝试使用百分比,但没有任何渲染,并且调试确认在到达本机视图时百分比值为NSNull。我不确定如何达到预期的效果。这是我到目前为止所尝试的内容:

// I have a component defined like this:
export default class MySvgCircle extends React.Component {
    render() {
        return (
            <View style={[this.props.style, {alignItems: 'center', justifyContent: 'center'}]} ref="containingView">
                <View style={{flex: 1, justifyContent: 'center', alignItems: 'center', aspectRatio: 1.0}}>
                    <Svg.Svg width="100" height="100">
                        <Svg.Circle cx="50" cy="50" r="40" stroke="blue" strokeWidth="1.0" fill="transparent" />
                        <Svg.Circle cx="50" cy="50" r="37" stroke="red" strokeWidth="6.0" fill="transparent" />
                    </Svg.Svg>
                </View>
            </View>
        );
    }
}

// And then consumed like this:
<MySvgCircle style={{height: 200, width: 200, backgroundColor: "powderblue"}}/>

这就是我在渲染时所看到的。

this is what I see

我希望将红色和蓝色圆圈按比例放大以填充200x200区域(如果包含视图是矩形而不是正方形则保持圆形),而无需预先知道组件的消费者/用户所需的大小。

如上所述,我尝试使用百分比,如下所示(其余部分相同):

<Svg.Svg width="100%" height="100%">

但是SVG部分根本没有画画。像这样:

no SVG rendering

在控制台日志中没有错误消息或其他无效的指示。

在RN中布局后测量UI元素的方法似乎是异步的,这似乎与我尝试做的事情很不匹配。我可以使用某种缩放或转换魔法吗?

所需的输出看起来像这样(通过硬编码值获得):

desired output

如果包含的视图不是一个完美的正方形,我希望它能像这样工作:

horizontal rectangle behavior vertical rectangle behavior

4 个答案:

答案 0 :(得分:3)

这里是一个行为类似于图像的组件:

import React from 'react';
import { View } from 'react-native';

import Svg, { Circle } from 'react-native-svg';

const WrappedSvg = () =>
  (
    <View style={{ aspectRatio: 1, backgroundColor: 'blue' }}>
      <Svg height="100%" width="100%" viewBox="0 0 100 100">
        <Circle r="50" cx="50" cy="50" fill="red" />
      </Svg>
    </View>
  );

在上下文中:

const WrappedSvgTest = () => (
  <View>
    <View style={{
      width: '100%',
      height: 140,
      alignItems: 'center',
      backgroundColor: '#eeeeee'
    }}
    >
      <WrappedSvg />
    </View>

    {/* spacer */}
    <View style={{ height: 100 }} />

    <View style={{
      width: 120,
      height: 280,
      justifyContent: 'space-around',
      backgroundColor: '#eeeeee'
    }}
    >
      <WrappedSvg />
    </View>
  </View>
);

诀窍是将SVG元素包装在保留其宽高比的视图中,然后将SVG大小设置为100%的宽度和高度。

我相信SVG元素大小和视图框大小之间存在一些复杂的相互作用,这使得SVG渲染比您期望的要小,或者在某些情况下根本无法渲染。您可以通过将<View>标签保持固定的长宽比并将<Svg>标签设置为100%的宽度和高度来避免这种情况,因此视图框的长宽比始终与元素比例匹配。

请确保将aspectRatio设置为viewbox.width / viewbox.height

答案 1 :(得分:2)

您必须与viewBox一起使用宽度和高度。通常,viewBox必须放置所需形状的原始尺寸。通过根据您的需要定义宽度/高度,您的形状将向下/向上缩放。

请仔细阅读本教程,其中已经非常清楚地解释了这些概念。

https://www.sarasoueidan.com/blog/svg-coordinate-systems/

答案 2 :(得分:1)

  preserveAspectRatio="xMinYMin slice"

您应该这样做

<Svg 
 height="100%" 
 preserveAspectRatio="xMinYMin slice" 
 width="100%" 
 viewBox="0 0 100 100"
>


答案 3 :(得分:1)

对于我的SVG,我使用的是https://material.io/resources/icons

对我来说,解决此问题的方法是确保您不会像我一样迷惑viewBoxPaths中的给定值,而只更改heightwidth进行填充,然后像其他答案一样使用容器:

<View style={{
    height: 100, display: 'flex',
}}>
    <TouchableOpacity style={{
        display: 'flex', justifyContent: 'center',
        alignItems: 'center', aspectRatio: 1,
    }}>
        <Svg fill="white" height="100%"
             width="100%" viewBox="0 0 24 24">
            <Path d="M0 0h24v24H0z" fill="none"/>
            <Path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
        </Svg>
    </TouchableOpacity>
</View>