Как я могу автоматически масштабировать элемент SVG в React Native View?
Я пытаюсь поставить react-native-svg
элемент внутри View
так, чтобы он отображался с определенным фиксированным соотношением сторон, но затем масштабировался до максимально возможного в пределах границ содержащего представления.
Svg
элемент (от react-native-svg
кажется, принимает только абсолют width
а также height
атрибуты (я пробовал использовать проценты, но ничего не рендерится, и отладка подтверждает, что значения процентов 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"}}/>
И это то, что я вижу, когда это делает.
Я хочу, чтобы красные и синие кружки были увеличены, чтобы заполнить область 200x200 (оставаясь круглыми, если вмещающий вид является прямоугольным, а не квадратным), без предварительного знания размера, требуемого потребителем / пользователем компонента.
Как уже упоминалось, я попытался использовать проценты, как это (остальное то же самое):
<Svg.Svg width="100%" height="100%">
Но тогда часть SVG вообще не рисует. Как это:
В журналах консоли нет сообщений об ошибках или других признаков того, почему это не работает.
Методы измерения элементов пользовательского интерфейса после размещения в RN кажутся асинхронными, что кажется плохим соответствием тому, что я пытаюсь сделать. Могу ли я использовать магию масштабирования или трансформации?
Желаемый результат будет выглядеть следующим образом (получен путем жесткого кодирования):
И когда вмещающий вид не является идеальным квадратом, я бы хотел, чтобы он работал так:
8 ответов
Вот компонент, который ведет себя как ваши изображения:
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
,
Трюк в
preserveAspectRatio="xMinYMin slice"
ты должен сделать это
<Svg
height="100%"
preserveAspectRatio="xMinYMin slice"
width="100%"
viewBox="0 0 100 100"
>
Вы должны играть с шириной и высотой вместе с viewBox. Обычно в viewBox вам нужно разместить исходные размеры желаемой формы. А определяя ширину / высоту в зависимости от ваших потребностей, ваша фигура будет правильно масштабироваться.
Пожалуйста, взгляните на этот урок, где эти понятия были объяснены довольно ясно.
Для моего SVG я использовал те, которые предоставлены на https://material.io/resources/icons
Что исправило для меня, так это убедиться, что вы не возитесь с viewBox
или заданные значения в Paths
(как и я), но измените только height
а также width
для заполнения, а затем использовать контейнеры, как и другие ответы:
<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>
Вам понадобятся эти переменные
const originalWidth = 744;
const originalHeight = 539.286;
const aspectRatio = originalWidth / originalHeight;
Оберните свой svg в виде со следующими свойствами:
<View style={{ width: '100%', aspectRatio }}></View>
или
<View style={{ width: Dimensions.get('window').width, aspectRatio }}>
</View>
Используйте svg внутри со следующими свойствами:
<Svg
width='100%'
height='100%'
viewBox={`0 0 ${originalWidth} ${originalHeight}`}
>
И ты должен быть в порядке!
Я положил все это дело в пример Snack, может быть, это поможет.
ЗАКУСКА: https://snack.expo.dev/@changnoi69/fbf937
Когда вы меняете marginLeft и marginRight этого представления, которое обернуто вокруг SVG-компонента, размер SVG изменяется в соответствии с ним.
<View style={{marginLeft:"20%", marginRight:"20%", backgroundColor: "pink"}}>
<NoInternetConnectionSVG />
</View>
Оригинальный пост Stackoverflow находится здесь: https://stackoverflow.com/a/73511233/12647753
я использую react-native-svg-transformer без использования react-native-svg, который я нашел очень тяжелым с точки зрения размера, поэтому я могу изменить размер и изменить цвет обводки, а также цвет заливки, но только вместо передачи реквизита заливки , просто передайте цвет, как показано ниже, он отлично работает
import React from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import Logo from "../../assets/profile.svg";
function FirstScreen(props) {
return (
<View style={styles.container}>
<TouchableOpacity
onPress={() => { props.navigation.navigate('SecondScreen'); }}
>
<Text>Welcome</Text>
<View style={{ aspectRatio: 1,justifyContent:"center",alignItems:"center", backgroundColor: 'blue',width:200,height:200 }}>
<Logo color="white" stroke="black" height={50} width={50} />
</View>
</TouchableOpacity>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
export default FirstScreen;
SVG-код
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><title>profile</title><g fill="currentColor" class="nc-icon-wrapper"><path d="M38,37H26A19.021,19.021,0,0,0,7,56a1,1,0,0,0,.594.914C7.97,57.081,16.961,61,32,61s24.03-3.919,24.406-4.086A1,1,0,0,0,57,56,19.021,19.021,0,0,0,38,37Z"></path><path data-color="color-2" d="M32,32c8.013,0,14-8.412,14-15.933a14,14,0,1,0-28,0C18,23.588,23.987,32,32,32Z"></path></g></svg>
зависимости
"dependencies": {
"@expo/webpack-config": "~0.16.2",
"@react-navigation/native": "^6.0.10",
"@react-navigation/native-stack": "^6.6.2",
"expo": "~45.0.0",
"expo-font": "^10.1.0",
"expo-status-bar": "~1.3.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-native": "0.68.2",
"react-native-svg-transformer": "^1.0.0",
},
файл metro.config.js добавить в корень
const { getDefaultConfig } = require('expo/metro-config');
module.exports = (() => {
const config = getDefaultConfig(__dirname);
const { transformer, resolver } = config;
config.transformer = {
...transformer,
babelTransformerPath: require.resolve('react-native-svg-transformer'),
};
config.resolver = {
...resolver,
assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'),
sourceExts: [...resolver.sourceExts, 'svg'],
};
return config;
})();
В моем случае мне пришлось масштабировать значок SVG в зависимости от размера устройства, и он использовал и<Path>
для рисования значка. После нескольких часов проб и ошибок я нашел решение - присвоить значение динамического масштаба (в зависимости от размера устройства) внутреннему компоненту компонента Svg. Здесь внутренняя составляющая<G>
<Svg width={RfH(24)} height={RfH(24)} style={{backgroundColor: 'salmon'}}>
<G
scale={RfH(1)} // Scaling added to the inner component
fill="none"
fillRule="evenodd">
<G
stroke={props?.isFocused ? '#302F4C' : '#8B8B88'}
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}>
<Path
d="M9.393 2.792 3.63 7.022c-.9.7-1.63 2.19-1.63 3.32v7.41c0 2.32 1.89 4.22 4.21 4.22h11.58c2.32 0 4.21-1.9 4.21-4.21v-7.28c0-1.21-.81-2.76-1.8-3.45l-5.807-4.36c-1.4-.98-3.65-.93-5 .12Z"
fill={props?.isFocused ? '#7BBDFF' : 'none'}
fillRule="nonzero"
/>
<Path fill="#FFF" d="M12 17.993v-2.924" />
</G>
</G>
- Значок дома iPad с масштабированием
- Главная иконка iPad без масштабирования
- Значок дома iPhone с масштабированием
- Домашняя иконка iPhone без масштабирования
Rfh
просто преобразует входное значение в эквивалент текущего устройства.
import {Dimensions} from 'react-native';
const STANDARD_SCREEN_DIMENSIONS = {height: 812, width: 375};
const RfH = (value) => {
const dim = Dimensions.get('window');
return dim.height * (value / STANDARD_SCREEN_DIMENSIONS.height);
};