如何使用 react-native-svg 找到宽度、高度和 viewBox 的正确值
How to find correct values for width, height and viewBox with react-native-svg
我一直在尝试做一些看似简单的事情,但我已经尝试了几个小时,却找不到解决方案。
我有一个需要位于屏幕顶部的 SVG。它来自设计师,尺寸如下:
<Svg width="354px" height="190px" viewBox="0 0 354 190">...</Svg>
在 React Native 中,它会进入容器内部,SVG 需要占据屏幕的整个宽度,我从中获取:
Dimensions.get("window").width
我的问题是,我还没有找到一种方法来缩放 SVG 以占据 100% 的屏幕宽度,找到正确的高度(或自动设置它的方法),并保持纵横比比率。我试过一百万种东西,包括尝试使用容器的 aspectRatio 样式及其 height (或者不设置 高度)。每当我发现某些 "proportions" 有效时,我都会尝试使用不同屏幕宽度的不同设备,但它看起来一点也不好(裁剪、小于屏幕宽度等)。
我觉得 SVG (react-native-svg) 中的 preserveAspectRatio 属性 与 aspectRatio[=40= 有点冲突] 风格。我完全迷失了 preserveAspectRatio,我还没有找到一种方法来使其缩放而不被裁剪。
有人知道如何实现吗?
这是我的最终代码,returns 一个显示 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 />;
};
这是结果:
我找到了解决方案,我将其发布在这里,以防有人 运行 遇到与本机反应和 SVG 相同的问题。基本上,如果您尝试获取 SVG 文件并将其转换为具有 "dynamic" 部分的组件(例如以编程方式根据数据将颜色设置为路径,或显示 SVG 文本),您可能会 运行 进入这个问题。
我所做的是使用SVGR to convert the original SVG into a react native component (with react-native-svg). Then I just replaced hardcoded data with variables (from props) as needed. It looked good, but I had a problem with the component's size. I couldn't find a consistent way to display it across different device sizes and resolutions. It seemed easy, but I tried for hours and the results were different on each screen size. After asking here and opening an issue on react-native-svg repo, I got no answers and no clues (not blaming anyone, just saying it was maybe not something a lot of people runs into). So I digged and digged and I finally found this post by Lea Verou, where she talked about absolute and relative SVG paths. That made me think that maybe I was having so many issues trying to find the perfect resizing formula because my paths weren't relative, but absolute. So I tried this jsfiddle by heyzeuss, pasting my (original) SVG code, and then copying the results. I pasted the results into this handy 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, but you'll only need like a 1% of it (Snap.path.toRelative) 的库的一部分。我正在考虑使用一个小工具来获取 svg 文件中的所有路径,并应用此转换。老实说,多年来我一直在处理 SVG 文件,但我从未真正了解过它的内部工作原理、路径格式、坐标等等,所以这很有启发性:)
如果您遇到同样的情况,希望这对您有所帮助!
扩展 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,
},
});
我一直在尝试做一些看似简单的事情,但我已经尝试了几个小时,却找不到解决方案。
我有一个需要位于屏幕顶部的 SVG。它来自设计师,尺寸如下:
<Svg width="354px" height="190px" viewBox="0 0 354 190">...</Svg>
在 React Native 中,它会进入容器内部,SVG 需要占据屏幕的整个宽度,我从中获取:
Dimensions.get("window").width
我的问题是,我还没有找到一种方法来缩放 SVG 以占据 100% 的屏幕宽度,找到正确的高度(或自动设置它的方法),并保持纵横比比率。我试过一百万种东西,包括尝试使用容器的 aspectRatio 样式及其 height (或者不设置 高度)。每当我发现某些 "proportions" 有效时,我都会尝试使用不同屏幕宽度的不同设备,但它看起来一点也不好(裁剪、小于屏幕宽度等)。
我觉得 SVG (react-native-svg) 中的 preserveAspectRatio 属性 与 aspectRatio[=40= 有点冲突] 风格。我完全迷失了 preserveAspectRatio,我还没有找到一种方法来使其缩放而不被裁剪。
有人知道如何实现吗?
这是我的最终代码,returns 一个显示 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 />;
};
这是结果:
我找到了解决方案,我将其发布在这里,以防有人 运行 遇到与本机反应和 SVG 相同的问题。基本上,如果您尝试获取 SVG 文件并将其转换为具有 "dynamic" 部分的组件(例如以编程方式根据数据将颜色设置为路径,或显示 SVG 文本),您可能会 运行 进入这个问题。
我所做的是使用SVGR to convert the original SVG into a react native component (with react-native-svg). Then I just replaced hardcoded data with variables (from props) as needed. It looked good, but I had a problem with the component's size. I couldn't find a consistent way to display it across different device sizes and resolutions. It seemed easy, but I tried for hours and the results were different on each screen size. After asking here and opening an issue on react-native-svg repo, I got no answers and no clues (not blaming anyone, just saying it was maybe not something a lot of people runs into). So I digged and digged and I finally found this post by Lea Verou, where she talked about absolute and relative SVG paths. That made me think that maybe I was having so many issues trying to find the perfect resizing formula because my paths weren't relative, but absolute. So I tried this jsfiddle by heyzeuss, pasting my (original) SVG code, and then copying the results. I pasted the results into this handy 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, but you'll only need like a 1% of it (Snap.path.toRelative) 的库的一部分。我正在考虑使用一个小工具来获取 svg 文件中的所有路径,并应用此转换。老实说,多年来我一直在处理 SVG 文件,但我从未真正了解过它的内部工作原理、路径格式、坐标等等,所以这很有启发性:)
如果您遇到同样的情况,希望这对您有所帮助!
扩展 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,
},
});