【问题标题】:How to find correct values for width, height and viewBox with react-native-svg如何使用 react-native-svg 找到宽度、高度和 viewBox 的正确值
【发布时间】:2020-08-22 18:04:49
【问题描述】:

我一直在尝试做一些看似简单的事情,但我已经尝试了几个小时,但找不到解决方案。

我有一个 SVG 需要位于屏幕顶部。它来自具有以下尺寸的设计师:

<Svg width="354px" height="190px" viewBox="0 0 354 190">...</Svg>

React Native 中,这将进入容器内部,并且 SVG 需要占据屏幕的整个宽度,我从中获取:

Dimensions.get("window").width

我的问题是,我还没有找到一种方法来缩放 SVG 以占用 100% 的屏幕宽度、找出正确的高度(或自动设置它的方法)并保持纵横比。我已经尝试了一百万种方法,包括使用容器的 aspectRatio 样式及其 height(或者根本不设置 height) .每当我找到一些有效的“比例”时,我都会尝试使用具有不同屏幕宽度的不同设备,但它看起来一点也不好看(裁剪、小于屏幕宽度等)。

我觉得 SVG (react-native-svg) 中的 preserveAspectRatio 属性在某种程度上与 aspectRatio 样式冲突。我对 preserveAspectRatio 完全迷失了方向,我还没有找到一种方法可以让它在不被裁剪的情况下进行缩放。

有谁知道如何做到这一点?

这是我的最终代码,它返回一个显示 SVG 的 HeatMap 组件,但尽管它具有正确的高度,但 SVG 的一部分从右侧超出了屏幕(看起来被裁剪了,因为它太宽了):

const windowWidth = Dimensions.get("window").width;

const getSVGRootProps = ({ width, height }) => ({
  width: "100%",
  height: "100%",
  viewBox: `0 0 ${width} ${height}`,
  preserveAspectRatio: "xMinYMin meet",
});

const FieldShape = () => {
  const width = 354; // Original width
  const height = 190; // Original height
  const aspectRatio = width / height;
  // adjusted height = <screen width> * original height / original width
  const calculatedHeight = (windowWidth * height) / width;
  const fieldStyles = {
    width: windowWidth,
    height: calculatedHeight,
    aspectRatio,
  };

  return (
    <View style={fieldStyles}>
      <Svg {...getSVGRootProps({ windowWidth, calculatedHeight })}>
      ...
      </Svg>
    </View>
  );
};

const HeatMap = () => {
  return <FieldShape />;
};

这是结果:

【问题讨论】:

  • 使用 getBBox() 方法。在这里阅读更多:stackoverflow.com/questions/44748197/…
  • 我不确定我是否可以在 react-native-svg 中使用它,我还没有看到这里实现的方法......

标签: css react-native svg aspect-ratio


【解决方案1】:

我已经找到了解决方案,我将其发布在这里,以防有人遇到与 react native 和 SVG 相同的问题。基本上,如果您尝试获取 SVG 文件并将其转换为具有“动态”部分的组件(例如根据数据以编程方式将颜色设置为路径,或显示 SVG 文本),您可能会遇到这个问题。

我所做的是使用 SVGR 将原始 SVG 转换为 react native 组件(使用 react-native-svg)。然后我只是根据需要用变量(来自道具)替换了硬编码数据。它看起来不错,但我对组件的大小有疑问。我找不到一种一致的方式来在不同的设备尺寸和分辨率上显示它。这看起来很容易,但我尝试了几个小时,每个屏幕尺寸的结果都不同。在此处询问并在 react-native-svg repo 上提出问题后,我没有得到任何答案,也没有任何线索(不怪任何人,只是说这可能不是很多人遇到的问题)。所以我挖了又挖,终于找到了this post by Lea Verou,她在那里谈到了绝对和相对 SVG 路径。这让我想,也许我在试图找到完美的调整大小公式时遇到了很多问题,因为我的路径不是相对的,而是绝对的。所以我尝试了这个jsfiddle by heyzeuss,粘贴我的(原始)SVG 代码,然后复制结果。我将结果粘贴到这个方便的SVGR playground (SVG to JS) tool 中,然后我更改了一些位以实现我的目标:

  • 我希望我的 SVG 占据整个屏幕的宽度,宽度和高度相应地缩放。

所以这就是我改变的:

// SVG's original size is 519 width, 260 height
// <Svg width="519" height="260" viewBox="0 0 519 260">...</Svg>
// I also added a container, which enforces the aspect ratio
const originalWidth = 519;
const originalHeight = 260;
const aspectRatio = originalWidth / originalHeight;
const windowWidth = Dimensions.get("window").width;
return (
    <View style={{ width: windowWidth, aspectRatio }}>
        <Svg 
            width="100%" 
            height="100%" 
            viewBox={`0 0 ${originalWidth} ${originalHeight}`}>
        ...
        </Svg>
    </View>
)

我在这样做的过程中学到了一些东西,例如,create-react-app 中包含一个@svgr/cli,并且在我的 react-native 项目中也可以使用,而无需安装任何额外的东西,因此它必须与原始依赖项捆绑在一起也。您可以运行此命令,它会将文件夹中的单个文件或所有文件从 .svg 转换为 React 组件:

npx @svgr/cli [-d out-dir] [--ignore-existing] [src-dir]

用于将绝对路径转换为相对路径的脚本是该库的一部分,名为Snap.svg,但您只需要其中的 1% (Snap.path.toRelative)。我正在考虑使用一个小工具来获取 svg 文件中的所有路径,并应用这种转换。老实说,我多年来一直在处理 SVG 文件,但我从来没有真正了解过它的内部工作原理、路径格式、坐标等,所以这很有启发性:)

如果您遇到同样的情况,我希望这对您有所帮助!

【讨论】:

  • 刚刚遇到这个问题,您的解决方案运行良好。感谢您抽出宝贵的时间!
  • 我遇到了同样的问题,你帮了我很多。谢谢你:)
  • 嗨@Lui Serrano。我也面临类似的问题,但没有得到解决。当你有机会时,你能不能快速看一下。 stackoverflow.com/questions/68156272/…
  • 非常有帮助,谢谢。
  • @Luis Serrano 谢谢!你拯救了我的一天,或者至少一半,因为我在尝试扩展我的 svg 时失去了一半:)
【解决方案2】:

扩展 Luis Serrano 的答案,您可以省略 windowWidth 并使用 100%

例子:

import * as React from 'react';
import Svg, { Circle, Path } from 'react-native-svg';
import { View } from 'react-native';

export default function SvgExample() {

  const originalWidth = 500;
  const originalHeight = 500;
  const aspectRatio = originalWidth / originalHeight;

  return (
    <View style={{ width: "100%", aspectRatio, backgroundColor: "salmon" }}>
      <Svg width="100%" height="100%" viewBox={`0 0 ${originalWidth} ${originalHeight}`}>
        <Circle cx="250" cy="250" r="40" fill="yellow" />
        <Circle cx="240" cy="240" r="4" fill="black" />
        <Circle cx="260" cy="240" r="4" fill="black" />
        <Path d="M 240 265 A 20 20 0 0 0 260 260" strokeWidth={2} stroke="black" />
      </Svg>
    </View>
  )
}

这样做的好处是它尊重封闭视图的填充

import { StyleSheet, View } from 'react-native';
import SvgExample from './SvgExample';

export default function TabOneScreen() {
  return (
    <View style={styles.container}>
      <SvgExample/>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    padding: 20,
  },
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-20
    • 2018-10-24
    • 2014-12-03
    • 2017-05-26
    • 2011-03-01
    • 2017-11-03
    • 1970-01-01
    • 2017-10-26
    相关资源
    最近更新 更多